The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
io.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 64e2a30adc41d96cd08d271bf1d8fbc19ba29eda $
19  * @file curl/io.c
20  * @brief Implement asynchronous callbacks for curl
21  *
22  * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  */
24 #include <freeradius-devel/curl/base.h>
25 #include <freeradius-devel/server/module.h>
26 #include <freeradius-devel/unlang/interpret.h>
27 #include <freeradius-devel/util/debug.h>
28 #include <freeradius-devel/util/syserror.h>
29 
30 /*
31  * CURL headers do:
32  *
33  * #define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)
34  */
36 DIAG_OFF(disabled-macro-expansion)
38 #define SET_MOPTION(_mandle, _opt, _val)\
39 do {\
40  if ((ret = curl_multi_setopt(mandle, _opt, _val)) != CURLM_OK) {\
41  option = STRINGIFY(_opt);\
42  goto error;\
43  }\
44 } while (0)
45 
46 /** De-queue curl requests and wake up the requests that initiated them
47  *
48  * @param[in] mhandle containing the event loop and request counter.
49  * @param[in] mandle to dequeue curl easy handles/responses from.
50  */
51 static inline void _fr_curl_io_demux(fr_curl_handle_t *mhandle, CURLM *mandle)
52 {
53  struct CURLMsg *m;
54  int msg_queued = 0;
55 
56  while ((m = curl_multi_info_read(mandle, &msg_queued))) {
57  switch (m->msg) {
58  case CURLMSG_DONE:
59  {
60  fr_curl_io_request_t *randle;
61  request_t *request = NULL;
62  CURL *candle = m->easy_handle;
63  CURLcode ret;
64 
65  fr_assert(candle);
66 
67  mhandle->transfers--;
68 
69  ret = curl_easy_getinfo(candle, CURLINFO_PRIVATE, &randle);
70  if (!fr_cond_assert_msg(ret == CURLE_OK,
71  "Failed retrieving request data from CURL easy handle (candle)")) {
72  curl_multi_remove_handle(mandle, candle);
73  return;
74  }
75  request = randle->request;
76 
77  REQUEST_VERIFY(request);
78 
79  /*
80  * If the request failed, say why...
81  */
82  if (m->data.result != CURLE_OK) {
83  REDEBUG("curl request failed: %s (%i)",
84  curl_easy_strerror(m->data.result), m->data.result);
85  }
86  randle->result = m->data.result;
87 
88  /*
89  * Looks like this needs to be done last,
90  * else m->data.result ends up being junk.
91  */
92  curl_multi_remove_handle(mandle, candle);
93 
95  }
96  break;
97 
98  default:
99 #ifndef NDEBUG
100  DEBUG4("Got unknown msg (%i) when dequeueing curl responses", msg_queued);
101 #endif
102  break;
103  }
104  }
105 }
106 
107 /** libcurl's timer expired
108  *
109  * @param[in] el the timer was inserted into.
110  * @param[in] now The current time according to the event loop.
111  * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
112  */
114 {
115  fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
116  CURLM *mandle = mhandle->mandle;
117  CURLMcode ret;
118  int running = 0;
119 
120  DEBUG4("multi-handle %p - Timer fired", mandle);
121 
122  ret = curl_multi_socket_action(mandle, CURL_SOCKET_TIMEOUT, 0, &running);
123  if (ret != CURLM_OK) {
124  ERROR("multi-handle %p - Servicing failed - %s (%i)", mandle, curl_multi_strerror(ret), ret);
125  return;
126  }
127 
128  DEBUG3("multi-handle %p - Serviced by timer. %i request(s) in progress, %" PRIu64 " requests(s) to dequeue",
129  mandle, running, mhandle->transfers - (uint64_t)running);
130 
131  _fr_curl_io_demux(mhandle, mandle);
132 }
133 
134 /** Service an IO event on a file descriptor
135  *
136  * @param[in] mhandle containing the event loop and request counter.
137  * @param[in] fd the IO event occurred for.
138  * @param[in] event type.
139  */
140 static inline void _fr_curl_io_service(fr_curl_handle_t *mhandle, int fd, int event)
141 {
142  CURLMcode ret;
143  CURLM *mandle = mhandle->mandle;
144  int running = 0;
145 
146  ret = curl_multi_socket_action(mandle, fd, event, &running);
147  if (ret != CURLM_OK) {
148  ERROR("multi-handle %p - Servicing failed - %s (%i)", mandle, curl_multi_strerror(ret), ret);
149  return;
150  }
151 
152  if (DEBUG_ENABLED3) {
153  char const *event_str;
154 
155  switch (event) {
156  case CURL_CSELECT_ERR:
157  event_str = "error";
158  break;
159 
160  case CURL_CSELECT_OUT:
161  event_str = "socket-writable";
162  break;
163 
164  case CURL_CSELECT_IN:
165  event_str = "socket-readable";
166  break;
167 
168  case 0:
169  event_str = "closed"; /* Not really closed, more do your own eval! */
170  break;
171 
172  default:
173  event_str = "<INVALID>";
174  break;
175  }
176 
177  DEBUG3("multi-handle %p - Serviced on fd %i event (%s). "
178  "%i request(s) in progress, %" PRIu64 " requests(s) to dequeue",
179  mandle, fd, event_str, running, mhandle->transfers - (uint64_t)running);
180  }
181 
182 
183  _fr_curl_io_demux(mhandle, mandle);
184 }
185 
186 /** File descriptor experienced an error
187  *
188  * @param[in] el fd was registered with.
189  * @param[in] fd that errored.
190  * @param[in] flags from kevent.
191  * @param[in] fd_errno from kevent.
192  * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
193  */
194 static void _fr_curl_io_service_errored(UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
195 {
196  fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
197 
198  DEBUG4("multi-handle %p - fd %i errored: %s", mhandle->mandle, fd, fr_syserror(fd_errno));
199 
200  /*
201  * The remote server closed the socket
202  *
203  * Instead of signalling curl with CURL_CSELECT_ERR
204  * which always results in spurious errors being
205  * printed, pass in '0', which causes libcurl to do
206  * its own evaluation on the socket state, and hopefully
207  * run the right code for socket closure.
208  */
209  if (flags & EV_EOF) {
210  _fr_curl_io_service(mhandle, fd, 0);
211  return;
212  }
213 
214  _fr_curl_io_service(mhandle, fd, CURL_CSELECT_ERR);
215 }
216 
217 /** File descriptor became writable
218  *
219  * @param[in] el fd was registered with.
220  * @param[in] fd that became writable.
221  * @param[in] flags from kevent.
222  * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
223  */
224 static void _fr_curl_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
225 {
226  fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
227 
228  DEBUG4("multi-handle %p - fd %i now writable", mhandle->mandle, fd);
229 
230  _fr_curl_io_service(mhandle, fd, CURL_CSELECT_OUT);
231 }
232 
233 /** File descriptor became readable
234  *
235  * @param[in] el fd was registered with.
236  * @param[in] fd that became readable.
237  * @param[in] flags from kevent.
238  * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
239  */
240 static void _fr_curl_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
241 {
242  fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
243 
244  DEBUG4("multi-handle %p - fd %i now readable", mhandle->mandle, fd);
245 
246  _fr_curl_io_service(mhandle, fd, CURL_CSELECT_IN);
247 }
248 
249 /** Callback called by libcurl to set/unset timers
250  *
251  * Each rlm_fr_curl_thread_t has a timer event which is controller by libcurl.
252  * This allows libcurl to honour timeouts set on requests to remote hosts,
253  * and means we don't need to set timeouts for individual I/O events.
254  *
255  * @param[in] mandle handle requesting the timer be set/unset.
256  * @param[in] timeout_ms If > 0, how long to wait before calling curl_multi_socket_action.
257  * If == 0, we call curl_multi_socket_action as soon as possible.
258  * If < 0, we delete the timer.
259  * @param[in] ctx The rlm_fr_curl_thread_t specific to this thread.
260  * @return
261  * - 0 on success.
262  * - -1 on error.
263  */
264 static int _fr_curl_io_timer_modify(CURLM *mandle, long timeout_ms, void *ctx)
265 {
266  fr_curl_handle_t *mhandle = talloc_get_type_abort(ctx, fr_curl_handle_t);
267 
268  if (timeout_ms < 0) {
269  if (fr_event_timer_delete(&mhandle->ev) < 0) {
270  PERROR("Failed deleting multi-handle timer");
271  return -1;
272  }
273  DEBUG3("multi-handle %p - Timer removed", mandle);
274  return 0;
275  }
276 
277  DEBUG3("multi-handle %p will need servicing in %li ms", mandle, timeout_ms);
278 
279  /*
280  * https://curl.haxx.se/libcurl/c/CURLMOPT_TIMERFUNCTION.html
281  *
282  * WARNING: even if it feels tempting, avoid calling libcurl directly from within the
283  * callback itself when the timeout_ms value is zero, since it risks triggering an
284  * unpleasant recursive behavior that immediately calls another call to the callback
285  * with a zero timeout...
286  *
287  * Setting a timeout of zero when calling fr_event_timer_in should result in the event
288  * repeating at most twice during one iteration of the event loop.
289  *
290  * In a previous version of this code we called curl_multi_socket_action immediately
291  * if timeout_ms was 0. It was observed that this lead to this callback being called
292  * ~665 times per request which is why we no longer do that.
293  */
294  if (fr_event_timer_in(mhandle, mhandle->el, &mhandle->ev,
295  fr_time_delta_from_msec(timeout_ms), _fr_curl_io_timer_expired, mhandle) < 0) return -1;
296  return 0;
297 }
298 
299 /** Called by libcurl to register a socket that it's intefr_curled in receiving IO events for
300  *
301  *
302  * @param[in] easy handle this fd relates to.
303  * @param[in] fd File descriptor curl wants to be notified about.
304  * @param[in] what Which events libcurl wants to be notified of, may be one of:
305  * - CURL_POLL_IN Wait for incoming data. For the socket
306  * to become readable.
307  * - CURL_POLL_OUT Wait for outgoing data. For the socket
308  * to become writable.
309  * - CURL_POLL_INOUT Wait for incoming and outgoing data.
310  * For the socket to become readable or writable.
311  * - CURL_POLL_REMOVE The specified socket/file descriptor is no
312  * longer used by libcurl.
313  * @param[in] ctx The fr_curl_handle_t specific to this thread.
314  * @param[in] fd_ctx Private data associated with the socket.
315  */
316 static int _fr_curl_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx)
317 {
318  fr_curl_handle_t *mhandle = talloc_get_type_abort(ctx, fr_curl_handle_t);
319 
320  switch (what) {
321  case CURL_POLL_IN:
322  if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
324  NULL,
326  mhandle) < 0) {
327  PERROR("multi-handle %p registration failed for read+error events on FD %i",
328  mhandle->mandle, fd);
329  return -1;
330  }
331  DEBUG4("multi-handle %p registered for read+error events on FD %i", mhandle->mandle, fd);
332  break;
333 
334  case CURL_POLL_OUT:
335  if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
336  NULL,
339  mhandle) < 0) {
340  PERROR("multi-handle %p registration failed for write+error events on FD %i",
341  mhandle->mandle, fd);
342  return -1;
343  }
344  DEBUG4("multi-handle %p registered for write+error events on FD %i", mhandle->mandle, fd);
345  break;
346 
347  case CURL_POLL_INOUT:
348  if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
352  mhandle) < 0) {
353  PERROR("multi-handle %p registration failed for read+write+error events on FD %i",
354  mhandle->mandle, fd);
355  return -1;
356  }
357  DEBUG4("multi-handle %p registered for read+write+error events on FD %i", mhandle->mandle, fd);
358  break;
359 
360  case CURL_POLL_REMOVE:
361  if (fr_event_fd_delete(mhandle->el, fd, FR_EVENT_FILTER_IO) < 0) {
362  PERROR("multi-handle %p de-registration failed for FD %i", mhandle->mandle, fd);
363  return -1;
364  }
365  DEBUG4("multi-handle %p unregistered events for FD %i", mhandle->mandle, fd);
366  break;
367 
368  default:
369  fr_assert(0);
370  return -1;
371  }
372 
373  return CURLM_OK;
374 }
375 
376 /** Callback to receive debugging data from libcurl
377  *
378  * @note Should only be set on a handle if RDEBUG_ENABLED3 is true.
379  *
380  * @param[in] candle Curl handle the debugging data pertains to.
381  * @param[in] type The type of debugging data we received.
382  * @param[in] data Buffer containing debug data (not \0 terminated). Despite the
383  * type being char *, this can be binary data depending on the
384  * curl_infotype.
385  * @param[in] len The length of the data in the buffer.
386  * @param[in] uctx The current request.
387  * @return
388  * - 0 (we always indicate success)
389  */
390 static int curl_debug_log(UNUSED CURL *candle, curl_infotype type, char *data, size_t len, void *uctx)
391 {
392  request_t *request = talloc_get_type_abort(uctx, request_t);
393  char const *p = data, *q, *end = p + len;
394  char const *verb;
395 
396  switch (type) {
397  case CURLINFO_TEXT:
398  /*
399  * Curl debug output has trailing newlines, and could conceivably
400  * span multiple lines. Take care of both cases.
401  */
402  while (p < end) {
403  q = memchr(p, '\n', end - p);
404  if (!q) q = end;
405 
406  RDEBUG3("libcurl - %pV", fr_box_strvalue_len(p, q ? q - p : p - end));
407  p = q + 1;
408  }
409 
410  break;
411 
412  case CURLINFO_HEADER_IN:
413  verb = "received";
414  print_header:
415  while (p < end) {
416  q = memchr(p, '\n', end - p);
417  q = q ? q + 1 : end;
418 
419  if (RDEBUG_ENABLED4) {
420  RHEXDUMP4((uint8_t const *)p, q - p,
421  "%s header: %pV",
422  verb, fr_box_strvalue_len(p, q - p));
423  } else {
424  RDEBUG3("%s header: %pV",
425  verb, fr_box_strvalue_len(p, q - p));
426  }
427  p = q;
428  }
429  break;
430 
431  case CURLINFO_HEADER_OUT:
432  verb = "sending";
433  goto print_header;
434 
435  case CURLINFO_DATA_IN:
436  RHEXDUMP4((uint8_t const *)data, len, "received data[length %zu]", len);
437  break;
438 
439  case CURLINFO_DATA_OUT:
440  RHEXDUMP4((uint8_t const *)data, len, "sending data[length %zu]", len);
441  break;
442 
443  case CURLINFO_SSL_DATA_OUT:
444  RHEXDUMP4((uint8_t const *)data, len, "sending ssl-data[length %zu]", len);
445  break;
446 
447  case CURLINFO_SSL_DATA_IN:
448  RHEXDUMP4((uint8_t const *)data, len, "received ssl-data[length %zu]", len);
449  break;
450 
451  default:
452  RHEXDUMP3((uint8_t const *)data, len, "libcurl - debug data (unknown type %i)", (int)type);
453  break;
454  }
455 
456  return 0;
457 }
458 
459 /** Sends a request using libcurl
460  *
461  * Send the actual curl request to the server. The response will be handled by
462  * the numerous callbacks configured for the easy handle.
463  *
464  * @param[in] mhandle Thread-specific mhandle wrapper.
465  * @param[in] request Current request.
466  * @param[in] randle representing the request.
467  * @return
468  * - 0 on success.
469  * - -1 on failure.
470  */
472 {
473  CURLcode ret;
474  CURLMcode mret;
475 
476  REQUEST_VERIFY(request);
477 
478  randle->request = request;
479 
480  /*
481  * Set debugging functions so we can track the
482  * IO request's progress.
483  */
484  if (RDEBUG_ENABLED3) {
485  FR_CURL_REQUEST_SET_OPTION(CURLOPT_DEBUGFUNCTION, curl_debug_log);
486  FR_CURL_REQUEST_SET_OPTION(CURLOPT_DEBUGDATA, request);
487  FR_CURL_REQUEST_SET_OPTION(CURLOPT_VERBOSE, 1L);
488  }
489 
490  /*
491  * Stick the current request in the curl handle's
492  * private data. This makes it simple to resume
493  * the request in the demux function later...
494  */
495  ret = curl_easy_setopt(randle->candle, CURLOPT_PRIVATE, randle);
496  if (ret != CURLE_OK) {
497  REDEBUG("Request failed: %i - %s", ret, curl_easy_strerror(ret));
498  return -1;
499  }
500 
501  /*
502  * Increment here, else the debug output looks
503  * messed up is curl_multi_add_handle triggers
504  * event loop modifications calls immediately.
505  */
506  mhandle->transfers++;
507  mret = curl_multi_add_handle(mhandle->mandle, randle->candle);
508  if (mret != CURLM_OK) {
509  mhandle->transfers--;
510  REDEBUG("Request failed: %i - %s", mret, curl_multi_strerror(mret));
511  return -1;
512  }
513 
514  return 0;
515 
516 error:
517  return -1;
518 }
519 
521 {
522  curl_easy_cleanup(randle->candle);
523 
524  return 0;
525 }
526 
527 /** Allocate a new curl easy request and wrapper struct
528  *
529  */
531 {
532  fr_curl_io_request_t *randle;
533 
534  randle = talloc_zero(ctx, fr_curl_io_request_t);
535  if (unlikely(!randle)) return NULL;
536 
537  randle->candle = curl_easy_init();
538  if (!randle->candle) {
539  talloc_free(randle);
540  return NULL;
541  }
542 
543  talloc_set_destructor(randle, _fr_curl_io_request_free);
544 
545  return randle;
546 }
547 
548 /** Free the multi-handle
549  *
550  */
551 static int _mhandle_free(fr_curl_handle_t *mhandle)
552 {
553  curl_multi_cleanup(mhandle->mandle);
554 
555  return 0;
556 }
557 
558 /** Performs the libcurl initialisation of the thread
559  *
560  * @param[in] ctx to alloc handle in.
561  * @param[in] el to initial.
562  * @param[in] multiplex Run multiple requests over the same connection simultaneously.
563  * HTTP/2 only.
564  * @return
565  * - 0 on success.
566  * - -1 on error.
567  */
570 #ifndef CURLPIPE_MULTIPLEX
571  UNUSED
572 #endif
573  bool multiplex)
574 {
575  CURLMcode ret;
576  CURLM *mandle;
577  fr_curl_handle_t *mhandle;
578  char const *option;
579 
580  mandle = curl_multi_init();
581  if (!mandle) {
582  ERROR("Curl multi-handle instantiation failed");
583  return NULL;
584  }
585 
586  /*
587  * Structure to store extra data.
588  *
589  * Passed to all curl I/O and timer callbacks.
590  *
591  * If uctx data is needed in the future, can be added here.
592  */
593  MEM(mhandle = talloc_zero(ctx, fr_curl_handle_t));
594  mhandle->el = el;
595  mhandle->mandle = mandle;
596  talloc_set_destructor(mhandle, _mhandle_free);
597 
598  SET_MOPTION(mandle, CURLMOPT_TIMERFUNCTION, _fr_curl_io_timer_modify);
599  SET_MOPTION(mandle, CURLMOPT_TIMERDATA, mhandle);
600 
601  SET_MOPTION(mandle, CURLMOPT_SOCKETFUNCTION, _fr_curl_io_event_modify);
602  SET_MOPTION(mandle, CURLMOPT_SOCKETDATA, mhandle);
603 
604 #ifdef CURLPIPE_MULTIPLEX
605  SET_MOPTION(mandle, CURLMOPT_PIPELINING, multiplex ? CURLPIPE_MULTIPLEX : CURLPIPE_NOTHING);
606 #endif
607 
608  return mhandle;
609 
610 error:
611  ERROR("Failed setting curl option %s: %s (%i)", option, curl_multi_strerror(ret), ret);
612 
613  return NULL;
614 }
#define DIAG_UNKNOWN_PRAGMAS
Definition: build.h:417
#define DIAG_ON(_x)
Definition: build.h:419
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
#define DIAG_OFF(_x)
Definition: build.h:418
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition: base.h:67
fr_event_list_t * el
Event list servicing I/O events.
Definition: base.h:92
CURLcode result
Result of executing the request.
Definition: base.h:103
uint64_t transfers
How many transfers are current in progress.
Definition: base.h:94
CURLM * mandle
The multi handle.
Definition: base.h:95
request_t * request
Current request.
Definition: base.h:104
fr_event_timer_t const * ev
Multi-Handle timer.
Definition: base.h:93
CURL * candle
Request specific handle.
Definition: base.h:102
Uctx data for timer and I/O functions.
Definition: base.h:91
Structure representing an individual request being passed to curl for processing.
Definition: base.h:101
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:154
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define fr_event_fd_insert(...)
Definition: event.h:232
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition: event.h:62
#define fr_event_timer_in(...)
Definition: event.h:255
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1340
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED bool multiplex)
Performs the libcurl initialisation of the thread.
Definition: io.c:568
static int _fr_curl_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx)
Called by libcurl to register a socket that it's intefr_curled in receiving IO events for.
Definition: io.c:316
static int _fr_curl_io_request_free(fr_curl_io_request_t *randle)
Definition: io.c:520
static void _fr_curl_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
File descriptor became writable.
Definition: io.c:224
#define SET_MOPTION(_mandle, _opt, _val)
Definition: io.c:38
static void _fr_curl_io_timer_expired(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
libcurl's timer expired
Definition: io.c:113
static void _fr_curl_io_demux(fr_curl_handle_t *mhandle, CURLM *mandle)
De-queue curl requests and wake up the requests that initiated them.
Definition: io.c:51
static void _fr_curl_io_service_errored(UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
File descriptor experienced an error.
Definition: io.c:194
static int curl_debug_log(UNUSED CURL *candle, curl_infotype type, char *data, size_t len, void *uctx)
Callback to receive debugging data from libcurl.
Definition: io.c:390
static void _fr_curl_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
File descriptor became readable.
Definition: io.c:240
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *randle)
Sends a request using libcurl.
Definition: io.c:471
static int _fr_curl_io_timer_modify(CURLM *mandle, long timeout_ms, void *ctx)
Callback called by libcurl to set/unset timers.
Definition: io.c:264
fr_curl_io_request_t * fr_curl_io_request_alloc(TALLOC_CTX *ctx)
Allocate a new curl easy request and wrapper struct.
Definition: io.c:530
static void _fr_curl_io_service(fr_curl_handle_t *mhandle, int fd, int event)
Service an IO event on a file descriptor.
Definition: io.c:140
static int _mhandle_free(fr_curl_handle_t *mhandle)
Free the multi-handle.
Definition: io.c:551
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RHEXDUMP4(_data, _len, _fmt,...)
Definition: log.h:706
#define DEBUG4(_fmt,...)
Definition: log.h:267
#define RHEXDUMP3(_data, _len, _fmt,...)
Definition: log.h:705
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition: log.h:259
#define RDEBUG_ENABLED4
True if request debug level 1-4 messages are enabled.
Definition: log.h:336
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition: event.c:1604
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition: event.c:1253
Stores all information relating to an event list.
Definition: event.c:411
unsigned char uint8_t
Definition: merged_model.c:30
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define REQUEST_VERIFY(_x)
Definition: request.h:275
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition: time.h:573
"server local" time.
Definition: time.h:69
static fr_event_list_t * el
static fr_slen_t data
Definition: value.h:1259
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279