The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
connection.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: a64332c312ea253fe014ca336fab6a3c6f5dd2fb $
19  * @file lib/ldap/connection.c
20  * @brief Asynchronous connection management functions for LDAP.
21  *
22  * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  */
24 RCSID("$Id: a64332c312ea253fe014ca336fab6a3c6f5dd2fb $")
25 
27 
28 #include <freeradius-devel/ldap/base.h>
29 #include <freeradius-devel/util/debug.h>
30 
31 /*
32  * Lookup of libldap result message types to meaningful strings
33  */
34 static char const *ldap_msg_types[UINT8_MAX] = {
35  [LDAP_RES_BIND] = "bind response",
36  [LDAP_RES_SEARCH_ENTRY] = "search entry",
37  [LDAP_RES_SEARCH_REFERENCE] = "search reference",
38  [LDAP_RES_SEARCH_RESULT] = "search result",
39  [LDAP_RES_MODIFY] = "modify response",
40  [LDAP_RES_ADD] = "add response",
41  [LDAP_RES_DELETE] = "delete response",
42  [LDAP_RES_MODDN] = "modify dn response",
43  [LDAP_RES_COMPARE] = "compare response",
44  [LDAP_RES_EXTENDED] = "extended response",
45  [LDAP_RES_INTERMEDIATE] = "intermediate response"
46 };
47 
48 
49 /** Allocate and configure a new connection
50  *
51  * Configures both our ldap handle, and libldap's handle.
52  *
53  * This can be used by async code and async code as no attempt is made to connect
54  * to the LDAP server. An attempt will only be made if ldap_start_tls* or ldap_bind*
55  * functions are called.
56  *
57  * If called on an #fr_ldap_connection_t which has already been initialised, will
58  * clear any memory allocated to the connection, unbind the ldap handle, and reinitialise
59  * everything.
60  *
61  * @param[in] c to configure.
62  * @param[in] config to apply.
63  * @return
64  * - 0 on success.
65  * - -1 on error.
66  */
68 {
69  LDAP *handle = NULL;
70  int ldap_errno, ldap_version, keepalive, probes, is_server;
71 
72  fr_assert(config->server);
73 
74  ldap_errno = ldap_initialize(&handle, config->server);
75  if (ldap_errno != LDAP_SUCCESS) {
76  ERROR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
77  error:
78  return -1;
79  }
80 
81  DEBUG3("New connection %p libldap handle %p", c, handle);
82 
83  c->config = config;
84  c->handle = handle;
85 
86  /*
87  * We now have a connection structure, but no actual connection.
88  *
89  * Set a bunch of LDAP options, using common code.
90  */
91 #define do_ldap_option(_option, _name, _value) \
92  if (ldap_set_option(c->handle, _option, _value) != LDAP_OPT_SUCCESS) do { \
93  ldap_get_option(c->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
94  ERROR("Failed setting connection option %s: %s", _name, \
95  (ldap_errno != LDAP_SUCCESS) ? ldap_err2string(ldap_errno) : "Unknown error"); \
96  goto error;\
97  } while (0)
98 
99 DIAG_OFF(unused-macros)
100 #define maybe_ldap_option(_option, _name, _value) \
101  if (_value) do_ldap_option(_option, _name, _value)
102 DIAG_ON(unused-macros)
103 
104  /*
105  * Leave "dereference" unset to use the OpenLDAP default.
106  */
107  if (config->dereference_str) do_ldap_option(LDAP_OPT_DEREF, "dereference", &(config->dereference));
108 
109  /*
110  * We handle our own referral chasing as there is no way to
111  * get the fd for a referred query.
112  */
113  do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_OFF);
114 
115  /*
116  * A value of zero results in an handle configuration failure.
117  *
118  * When most people specify zero they mean infinite.
119  *
120  * libldap requires tv_sec to be -1 to mean that.
121  */
122  do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
123  (fr_time_delta_ispos(config->net_timeout) ?
124  &fr_time_delta_to_timeval(config->net_timeout) :
125  &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
126 
127  do_ldap_option(LDAP_OPT_TIMELIMIT, "srv_timelimit", &fr_time_delta_to_timeval(config->srv_timelimit));
128 
129  ldap_version = LDAP_VERSION3;
130  do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version);
131 
132  keepalive = fr_time_delta_to_sec(config->keepalive_idle);
133  do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive_idle", &keepalive);
134 
135  probes = config->keepalive_probes;
136  do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive_probes", &probes);
137 
138  keepalive = fr_time_delta_to_sec(config->keepalive_interval);
139  do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive_interval", &keepalive);
140 
141  /*
142  * Set all of the TLS options
143  */
144  if (config->tls_mode) do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(config->tls_mode));
145 
146  maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "ca_file", config->tls_ca_file);
147  maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "ca_path", config->tls_ca_path);
148 
149  /*
150  * Set certificate options
151  */
152  maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certificate_file", config->tls_certificate_file);
153  maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "private_key_file", config->tls_private_key_file);
154 
155  if (config->tls_require_cert_str) {
156  do_ldap_option(LDAP_OPT_X_TLS_REQUIRE_CERT, "require_cert", &config->tls_require_cert);
157  }
158 
159  if (config->tls_min_version_str) {
160  do_ldap_option(LDAP_OPT_X_TLS_PROTOCOL_MIN, "tls_min_version", &config->tls_min_version);
161  }
162 
163  /*
164  * Counter intuitively the TLS context appears to need to be initialised
165  * after all the TLS options are set on the handle.
166  */
167 
168  /* Always use the new TLS configuration context */
169  is_server = 0;
170  do_ldap_option(LDAP_OPT_X_TLS_NEWCTX, "new TLS context", &is_server);
171 
172  if (config->sasl_secprops) do_ldap_option(LDAP_OPT_X_SASL_SECPROPS, "sasl_secprops", config->sasl_secprops);
173 
174  if (config->start_tls) {
175  if (config->port == 636) {
176  WARN("Told to Start TLS on LDAPS port this will probably fail, please correct the "
177  "configuration");
178  }
179  }
180 
181  return 0;
182 }
183 
184 /** Free the handle, closing the connection to ldap
185  *
186  * @param[in] el UNUSED.
187  * @param[in] h to close.
188  * @param[in] uctx Connection config and handle.
189  */
190 static void _ldap_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
191 {
192  fr_ldap_connection_t *c = talloc_get_type_abort(h, fr_ldap_connection_t);
193 
194  /*
195  * Explicitly remove the file descriptor event
196  *
197  * Even if the fr_ldap_connection_t has outstanding
198  * queries, we still don't want its fd in the event loop.
199  */
200  if (c->fd >= 0) {
202  c->fd = -1;
203  }
204 
205  talloc_free(h);
206 }
207 
208 /** Close and delete a connection
209  *
210  * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
211  * connection handle.
212  *
213  * @param[in] c to destroy.
214  * @return always indicates success.
215  */
217 {
218  /*
219  * If there are any pending queries, don't free
220  */
221  if (((c->queries) && (fr_rb_num_elements(c->queries) > 0)) || (fr_dlist_num_elements(&c->refs) > 0)) return -1;
222 
223  talloc_free_children(c); /* Force inverted free order */
224 
225  if (c->handle) {
226  LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
227  LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
228 
229  fr_ldap_control_merge(our_serverctrls, our_clientctrls,
230  NUM_ELEMENTS(our_serverctrls),
231  NUM_ELEMENTS(our_clientctrls),
232  c, NULL, NULL);
233 
234  DEBUG3("Closing connection %p libldap handle %p", c->handle, c);
235  ldap_unbind_ext(c->handle, our_serverctrls, our_clientctrls); /* Same code as ldap_unbind_ext_s */
236  }
237 
239 
240  return 0;
241 }
242 
243 /** Allocate our ldap connection handle layer
244  *
245  * This is using handles outside of the connection state machine.
246  *
247  * @param[in] ctx to allocate connection handle in.
248  * @return
249  * - A new unbound/unconfigured connection handle on success.
250  * Call f#r_ldap_connection_configure next.
251  * - NULL on OOM.
252  */
254 {
256 
257  /*
258  * Allocate memory for the handle.
259  */
260  c = talloc_zero(ctx, fr_ldap_connection_t);
261  if (!c) return NULL;
262 
263  talloc_set_destructor(c, _ldap_connection_free);
264 
265  /*
266  * Ensure the fd is invalid to start with, preventing
267  * attempts to remove fd events if the server is shut down
268  * before the LDAP connection is established
269  */
270  c->fd = -1;
271 
272  return c;
273 }
274 
275 /** Watcher for LDAP connections being closed
276  *
277  * If there are any outstanding queries on the connection then
278  * re-parent the connection to the NULL ctx so that it remains
279  * until all the queries have been dealt with.
280  */
282  UNUSED fr_connection_state_t state, void *uctx)
283 {
284  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(uctx, fr_ldap_connection_t);
285 
286  if ((fr_rb_num_elements(ldap_conn->queries) == 0) && (fr_dlist_num_elements(&ldap_conn->refs) == 0)) return;
287 
288  talloc_reparent(conn, NULL, ldap_conn);
289  ldap_conn->conn = NULL;
290 }
291 
292 /** (Re-)Initialises the libldap side of the connection handle
293  *
294  * The first ldap state transition is either:
295  *
296  * init -> start tls
297  * or
298  * init -> bind
299  *
300  * Either way libldap will try an open the connection so when fr_ldap_state_next
301  * returns we should have the file descriptor to pass back.
302  *
303  * The complete order of operations is:
304  *
305  * - Initialise the libldap handle with fr_ldap_connection_configure (calls ldap_init)
306  * - Initiate the connection with fr_ldap_state_next, which either binds or calls start_tls.
307  * - Either operation calls ldap_send_server_request.
308  * - Which calls ldap_new_connection.
309  * - Which calls ldap_int_open_connection.
310  * - Which calls ldap_connect_to_(host|path) and adds socket buffers, and possibly
311  * calls ldap_int_tls_start (for ldaps://).
312  * - When ldap_new_connection returns, because LDAP_OPT_CONNECT_ASYNC
313  * is set to LDAP_OPT_ON, lc->lconn_status is set to LDAP_CONNST_CONNECTING.
314  * - ldap_send_server_request checks for lconn_stats == LDAP_CONNST_CONNECTING,
315  * and calls ldap_int_poll, which checks the fd for error conditions
316  * and immediately returns due to the network timeout value.
317  * - If the socket is not yet connected:
318  * - As network timeout on the LDAP handle is 0, ld->ld_errno is set to
319  * LDAP_X_CONNECTING. ldap_send_server_request returns -1.
320  * - bind or start_tls errors with LDAP_X_CONNECTING without sending the request.
321  * - We install a write I/O handler, and wait to be called again, then we retry the
322  * operation.
323  * - else
324  * - the bind or start_tls operation succeeds, our ldap state machine advances,
325  * the connection callback is called and our socket state machine transitions to
326  * connected.
327  * - Continue running the state machine
328  *
329  * @param[out] h Underlying file descriptor from libldap handle.
330  * @param[in] conn Being initialised.
331  * @param[in] uctx Our LDAP connection handle (a #fr_ldap_connection_t).
332  * @return
333  * - FR_CONNECTION_STATE_CONNECTING on success.
334  * - FR_CONNECTION_STATE_FAILED on failure.
335  */
337 {
338  fr_ldap_config_t const *config = uctx;
340  fr_ldap_state_t state;
341 
342  c = fr_ldap_connection_alloc(conn);
343  c->conn = conn;
344  /*
345  * Initialise tree for outstanding queries handled by this connection
346  */
348  fr_dlist_init(&c->refs, fr_ldap_query_t, entry);
349 
350  /*
351  * Configure/allocate the libldap handle
352  */
353  if (fr_ldap_connection_configure(c, config) < 0) {
354  error:
355  talloc_free(c);
357  }
358 
359  /* Don't block */
360  if (ldap_set_option(c->handle, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON) != LDAP_OPT_SUCCESS) goto error;
361  fr_ldap_connection_timeout_set(c, fr_time_delta_wrap(0)); /* Forces LDAP_X_CONNECTING */
362 
363  state = fr_ldap_state_next(c);
364  if (state == FR_LDAP_STATE_ERROR) goto error;
365 
367 
368  *h = c; /* Set the handle */
369 
371 }
372 
373 /** Alloc a self re-establishing connection to an LDAP server
374  *
375  * @param[in] ctx to allocate any memory in, and to bind the lifetime of the connection to.
376  * @param[in] el to insert I/O and timer callbacks into.
377  * @param[in] config to use to bind the connection to an LDAP server.
378  * @param[in] log_prefix to prepend to connection state messages.
379  */
381  fr_ldap_config_t const *config, char const *log_prefix)
382 {
383  fr_connection_t *conn;
384 
385  conn = fr_connection_alloc(ctx, el,
387  .init = _ldap_connection_init,
388  .close = _ldap_connection_close
389  },
391  .connection_timeout = config->net_timeout,
392  .reconnection_delay = config->reconnection_delay
393  },
394  log_prefix, config);
395  if (!conn) {
396  PERROR("Failed allocating state handler for new LDAP connection");
397  return NULL;
398  }
399 
400  return conn;
401 }
402 
404 {
405  int ldap_errno;
406 
407  /*
408  * A value of zero results in an handle configuration failure.
409  *
410  * When most people specify zero they mean infinite.
411  *
412  * libldap requires tv_sec to be -1 to mean that.
413  */
414  do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
417  &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
418 
419  return 0;
420 
421 error:
422  return -1;
423 }
424 
426 {
427  int ldap_errno;
428 
429  /*
430  * A value of zero results in an handle configuration failure.
431  *
432  * When most people specify zero they mean infinite.
433  *
434  * libldap requires tv_sec to be -1 to mean that.
435  */
436  do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
439  &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
440 
441  return 0;
442 
443 error:
444  return -1;
445 }
446 
447 /** Callback for closing idle LDAP trunk
448  *
449  */
451 {
452  fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
453 
454  if (ttrunk->trunk->req_alloc == 0) {
455  DEBUG2("Removing idle LDAP trunk to \"%s\"", ttrunk->uri);
456  talloc_free(ttrunk->trunk);
457  talloc_free(ttrunk);
458  } else {
459  /*
460  * There are still pending queries - insert a new event
461  */
462  (void) fr_event_timer_in(ttrunk, el, &ttrunk->ev, ttrunk->t->config->idle_timeout,
463  _ldap_trunk_idle_timeout, ttrunk);
464  }
465 }
466 
467 /** Callback when an LDAP trunk request is cancelled
468  *
469  * Ensure the request is removed from the list of outstanding requests
470  */
472  UNUSED void *uctx) {
473  fr_ldap_query_t *query = talloc_get_type_abort(preq, fr_ldap_query_t);
474 
475  if (query->ldap_conn) {
476  fr_rb_remove(query->ldap_conn->queries, query);
477  query->ldap_conn = NULL;
478  }
479 }
480 
481 /** Callback to cancel LDAP queries
482  *
483  * Inform the remote LDAP server that we no longer want responses to specific queries.
484  *
485  * @param[in] el For timer management.
486  * @param[in] tconn The trunk connection handle
487  * @param[in] conn The specific connection queries will be cancelled on
488  * @param[in] uctx Context provided to fr_trunk_alloc
489  */
491  fr_connection_t *conn, UNUSED void *uctx)
492 {
493  fr_trunk_request_t *treq;
494  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
495  fr_ldap_query_t *query;
496 
497  while ((fr_trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
498  query = talloc_get_type_abort(treq->preq, fr_ldap_query_t);
499  ldap_abandon_ext(ldap_conn->handle, query->msgid, NULL, NULL);
500 
502  }
503 }
504 
505 /** Callback to tidy up when a trunk request fails
506  *
507  */
508 static void ldap_request_fail(request_t *request, void *preq, UNUSED void *rctx,
509  UNUSED fr_trunk_request_state_t state, UNUSED void *uctx)
510 {
511  fr_ldap_query_t *query = talloc_get_type_abort(preq, fr_ldap_query_t);
512 
513  /*
514  * Failed trunk requests get freed - so remove association in query.
515  */
516  query->treq = NULL;
517  query->ret = LDAP_RESULT_ERROR;
518 
519  /*
520  * Ensure request is runnable.
521  */
522  if (request) unlang_interpret_mark_runnable(request);
523 }
524 
525 /** I/O read function
526  *
527  * Underlying FD is now readable - call the trunk to read any pending requests.
528  *
529  * @param[in] el The event list signalling.
530  * @param[in] fd that's now readable.
531  * @param[in] flags describing the read event.
532  * @param[in] uctx The trunk connection handle.
533  */
534 static void ldap_conn_readable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
535 {
536  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
537 
539 }
540 
541 
542 /** I/O write function
543  *
544  * Underlying FD is now writable - call the trunk to write any pending requests.
545  *
546  * @param[in] el The event list signalling.
547  * @param[in] fd that's now writable.
548  * @param[in] flags describing the write event.
549  * @param[in] uctx The trunk connection handle
550  */
551 static void ldap_conn_writable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
552 {
553  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
554 
556 }
557 
558 
559 /** I/O error function
560  *
561  * The event loop signalled that a fatal error occurec on this connection.
562  *
563  * @param[in] el The event list signalling.
564  * @param[in] fd that errored.
565  * @param[in] flags EL flags.
566  * @param[in] fd_errno The nature of the error.
567  * @param[in] uctx The trunk connection handle
568  */
569 static void ldap_conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
570 {
571  fr_trunk_connection_t *tconn = talloc_get_type_abort(uctx, fr_trunk_connection_t);
572 
573  ERROR("%s - Connection failed: %s", tconn->conn->name, fr_syserror(fd_errno));
574 
576 }
577 
578 /** Setup callbacks requested by LDAP trunk connections
579  *
580  * @param[in] tconn Trunk handle.
581  * @param[in] conn Individual connection callbacks are to be installed for.
582  * @param[in] el The event list to install events in.
583  * @param[in] notify_on The types of event the trunk wants to be notified on.
584  * @param[in] uctx Context provided to fr_trunk_alloc.
585  */
588  fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
589 {
590  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
591  fr_event_fd_cb_t read_fn = NULL;
592  fr_event_fd_cb_t write_fn = NULL;
593 
594  switch (notify_on) {
597  return;
598 
600  read_fn = ldap_conn_readable;
601  break;
602 
604  write_fn = ldap_conn_writable;
605  break;
606 
608  read_fn = ldap_conn_readable;
609  write_fn = ldap_conn_writable;
610  break;
611  }
612 
613  if (fr_event_fd_insert(ldap_conn, NULL, el, ldap_conn->fd,
614  read_fn,
615  write_fn,
617  tconn) < 0) {
618  PERROR("Failed inserting FD event");
620  }
621 }
622 
623 /** Allocate an LDAP trunk connection
624  *
625  * @param[in] tconn Trunk handle.
626  * @param[in] el Event list which will be used for I/O and timer events.
627  * @param[in] conn_conf Configuration of the connection.
628  * @param[in] log_prefix What to prefix log messages with.
629  * @param[in] uctx User context passed to fr_trunk_alloc.
630  */
632  UNUSED fr_connection_conf_t const *conn_conf,
633  char const *log_prefix, void *uctx)
634 {
635  fr_ldap_thread_trunk_t *thread_trunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
636 
637  return fr_ldap_connection_state_alloc(tconn, el, &thread_trunk->config, log_prefix);
638 }
639 
640 #define POPULATE_LDAP_CONTROLS(_dest, _src) do { \
641  int i; \
642  for (i = 0; (i < LDAP_MAX_CONTROLS) && (_src[i].control); i++) { \
643  _dest[i] = _src[i].control; \
644  } \
645  _dest[i] = NULL; \
646 } while (0)
647 
648 /** Take LDAP pending queries from the queue and send them.
649  *
650  * @param[in] el Event list for timers.
651  * @param[in] tconn Trunk handle.
652  * @param[in] conn on which to send the queries
653  * @param[in] uctx User context passed to fr_trunk_alloc
654  */
656  fr_connection_t *conn, UNUSED void *uctx)
657 {
658  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
659  fr_trunk_request_t *treq;
660 
661  LDAPURLDesc *referral_url = NULL;
662 
663  fr_ldap_query_t *query = NULL;
664  fr_ldap_rcode_t status;
665 
666  while (fr_trunk_connection_pop_request(&treq, tconn) == 0) {
667  LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS + 1];
668  LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS + 1];
669 
670  if (!treq) break;
671 
672  query = talloc_get_type_abort(treq->preq, fr_ldap_query_t);
673 
674  switch (query->type) {
675  case LDAP_REQUEST_SEARCH:
676  /*
677  * This query is a LDAP search
678  */
679  if (query->referral) referral_url = query->referral->referral_url;
680 
681  POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
682  POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
683 
684  /*
685  * If we are chasing a referral, referral_url will be populated and may
686  * have a base dn or scope to override the original query
687  */
688  status = fr_ldap_search_async(&query->msgid, query->treq->request, ldap_conn,
689  (referral_url && referral_url->lud_dn) ?
690  referral_url->lud_dn : query->dn,
691  (referral_url && referral_url->lud_scope) ?
692  referral_url->lud_scope : query->search.scope,
693  query->search.filter, query->search.attrs,
694  our_serverctrls, our_clientctrls);
695  break;
696 
697  case LDAP_REQUEST_MODIFY:
698  /*
699  * This query is an LDAP modification
700  */
701  POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
702  POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
703 
704  status = fr_ldap_modify_async(&query->msgid, query->treq->request,
705  ldap_conn, query->dn, query->mods,
706  our_serverctrls, our_clientctrls);
707  break;
708 
710  /*
711  * This query is an LDAP extended operation.
712  */
713  status = fr_ldap_extended_async(&query->msgid, query->treq->request, ldap_conn,
714  query->extended.reqoid, query->extended.reqdata);
715  break;
716 
717  default:
718  status = LDAP_PROC_ERROR;
719  ERROR("Invalid LDAP query for trunk connection");
720  error:
724  continue;
725 
726  }
727 
728  if (status != LDAP_PROC_SUCCESS) goto error;
729 
730  /*
731  * If the query has previously been associated with a different
732  * connection, remove that reference. Typically when following references.
733  */
734  if (query->ldap_conn) fr_dlist_remove(&query->ldap_conn->refs, query);
735 
736  /*
737  * Record which connection was used for this query
738  * - results processing often needs access to an LDAP handle
739  */
740  query->ldap_conn = ldap_conn;
741 
742  /*
743  * Add the query to the tree of pending queries for this trunk
744  */
745  fr_rb_insert(query->ldap_conn->queries, query);
746 
748  }
749 
750 }
751 
752 /** Read LDAP responses
753  *
754  * Responses from the LDAP server will cause the fd to become readable and trigger this
755  * callback. Most LDAP search responses have multiple messages in their response - we
756  * only gather those which are complete before either following a referral or passing
757  * the head of the resulting chain of messages back.
758  *
759  * @param[in] el To insert timers into.
760  * @param[in] tconn Trunk connection associated with these results.
761  * @param[in] conn Connection handle for these results.
762  * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
763  */
765 {
766  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
767  fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
768 
769  int ret = 0, msgtype;
770  struct timeval poll = { 0, 10 };
771  LDAPMessage *result = NULL;
772  fr_ldap_rcode_t rcode;
773  fr_ldap_query_t find = { .msgid = -1 }, *query = NULL;
774  request_t *request;
775  bool really_no_result = false;
776  fr_trunk_request_t *treq;
777 
778  /*
779  * Reset the idle timeout event
780  */
781  (void) fr_event_timer_in(ttrunk, el, &ttrunk->ev,
782  ttrunk->t->config->idle_timeout, _ldap_trunk_idle_timeout, ttrunk);
783 
784  do {
785  /*
786  * Look for any results for which we have the complete result message
787  * ldap_result will return a pointer to a chain of messages.
788  *
789  * The first time ldap_result is called when there's pending network
790  * data, it may read the data, but not return any results.
791  *
792  * In order to fix the spurious debugging messages and overhead,
793  * if this is the first iteration through the loop and ldap_result
794  * returns no result (0), we call it again.
795  */
796  ret = ldap_result(ldap_conn->handle, LDAP_RES_ANY, LDAP_MSG_ALL, &poll, &result);
797  switch (ret) {
798  case 0:
799  if (really_no_result) return;
800  really_no_result = true;
801  continue;
802 
803  case -1:
804  rcode = fr_ldap_error_check(NULL, ldap_conn, NULL, NULL);
805  if (rcode == LDAP_PROC_BAD_CONN) {
806  ERROR("Bad LDAP connection");
808  }
809  return;
810 
811  default:
812  /*
813  * We only retry ldap_result the first time through the loop.
814  */
815  really_no_result = true;
816  break;
817  }
818 
819  find.msgid = ldap_msgid(result);
820  query = fr_rb_find(ldap_conn->queries, &find);
821 
822  if (!query) {
823  WARN("Ignoring msgid %i - doesn't match any outstanding queries (it may have been cancelled)",
824  find.msgid);
825  ldap_msgfree(result);
826  continue;
827  }
828 
829  /*
830  * Remove the query from the tree of outstanding queries
831  */
832  fr_rb_remove(ldap_conn->queries, query);
833 
834  /*
835  * Add the query to the list of queries referencing this connection.
836  * Prevents the connection from being freed until the query has finished using it.
837  */
838  fr_dlist_insert_tail(&ldap_conn->refs, query);
839 
840  /*
841  * This really shouldn't happen - as we only retrieve complete sets of results -
842  * but as the query data structure will last until its results are fully handled
843  * better to have this safety check here.
844  */
845  if (query->ret != LDAP_RESULT_PENDING) {
846  WARN("Received results for msgid %i which has already been handled - ignoring", find.msgid);
847  ldap_msgfree(result);
848  continue;
849  }
850 
851  msgtype = ldap_msgtype(result);
852 
853  /*
854  * Request to reference in debug output
855  */
856  request = query->treq->request;
857 
858  ROPTIONAL(RDEBUG2, DEBUG2, "Got %s response for message %d",
859  ldap_msg_types[msgtype], query->msgid);
860  rcode = fr_ldap_error_check(NULL, ldap_conn, result, query->dn);
861 
862  switch (rcode) {
863  case LDAP_PROC_SUCCESS:
864  switch (query->type) {
865  case LDAP_REQUEST_SEARCH:
866  query->ret = (ldap_count_entries(ldap_conn->handle, result) == 0) ?
868  break;
869 
870  default:
871  query->ret = LDAP_RESULT_SUCCESS;
872  break;
873  }
874  break;
875 
876  case LDAP_PROC_REFERRAL:
877  if (!ttrunk->t->config->chase_referrals) {
879  "LDAP referral received but 'chase_referrals' is set to 'no'");
880  query->ret = LDAP_RESULT_EXCESS_REFERRALS;
881  break;
882  }
883 
884  if (query->referral_depth >= ttrunk->t->config->referral_depth) {
885  ROPTIONAL(REDEBUG, ERROR, "Maximum LDAP referral depth (%d) exceeded",
886  ttrunk->t->config->referral_depth);
887  query->ret = LDAP_RESULT_EXCESS_REFERRALS;
888  break;
889  }
890 
891  /*
892  * If we've come here as the result of an existing referral
893  * clear the previous list of URLs before getting the next list.
894  */
895  if (query->referral_urls) ldap_memvfree((void **)query->referral_urls);
896 
897  ldap_get_option(ldap_conn->handle, LDAP_OPT_REFERRAL_URLS, &query->referral_urls);
898  if (!(query->referral_urls) || (!(query->referral_urls[0]))) {
899  ROPTIONAL(REDEBUG, ERROR, "LDAP referral missing referral URL");
900  query->ret = LDAP_RESULT_MISSING_REFERRAL;
901  break;
902  }
903 
904  query->referral_depth ++;
905 
906  if (fr_ldap_referral_follow(ttrunk->t, request, query) == 0) {
907  next_follow:
908  ldap_msgfree(result);
909  continue;
910  }
911 
912  ROPTIONAL(REDEBUG, ERROR, "Unable to follow any LDAP referral URLs");
913  query->ret = LDAP_RESULT_REFERRAL_FAIL;
914  break;
915 
916  case LDAP_PROC_BAD_DN:
917  ROPTIONAL(RDEBUG2, DEBUG2, "DN %s does not exist", query->dn);
918  query->ret = LDAP_RESULT_BAD_DN;
919  break;
920 
921  default:
922  ROPTIONAL(RPERROR, PERROR, "LDAP server returned an error");
923 
924  if (query->referral_depth > 0) {
925  /*
926  * We're processing a referral - see if there are any more to try
927  */
928  fr_dlist_talloc_free_item(&query->referrals, query->referral);
929  query->referral = NULL;
930 
931  if ((fr_dlist_num_elements(&query->referrals) > 0) &&
932  (fr_ldap_referral_next(ttrunk->t, request, query) == 0)) goto next_follow;
933  }
934 
935  query->ret = LDAP_RESULT_REFERRAL_FAIL;
936  break;
937  }
938 
939  /*
940  * Remove the timeout event
941  */
942  if (query->ev) fr_event_timer_delete(&query->ev);
943 
944  query->result = result;
945 
946  /*
947  * If we have a specific parser to handle the result, call it
948  */
949  if (query->parser && (rcode == LDAP_PROC_SUCCESS)) query->parser(ldap_conn->handle, query,
950  result, query->treq->rctx);
951 
952  /*
953  * Set the request as runnable
954  */
955  if (request) unlang_interpret_mark_runnable(request);
956 
957  /*
958  * If referral following failed, there is no active trunk request.
959  */
960  if (!query->treq) continue;
961 
962  /*
963  * If the query is parented off the treq then it will be freed when
964  * the request is completed. If it is parented by something else then it will not.
965  */
966  treq = query->treq;
967  query->treq = NULL;
969  } while (1);
970 }
971 
973 {
974  if (ttrunk->t && fr_rb_node_inline_in_tree(&ttrunk->node)) fr_rb_remove(ttrunk->t->trunks, ttrunk);
975 
976  return 0;
977 }
978 
979 /** Find a thread specific LDAP connection for a specific URI / bind DN
980  *
981  * If no existing connection exists for that combination then create a new one
982  *
983  * @param[in] thread to which the connection belongs
984  * @param[in] uri of the host to find / create a connection to
985  * @param[in] bind_dn to make the connection as
986  * @param[in] bind_password for making connection
987  * @param[in] request currently being processed (only for debug messages)
988  * @param[in] config LDAP config of the module requesting the connection.
989  * @return
990  * - an existing or new connection matching the URI and bind DN
991  * - NULL on failure
992  */
994  char const *bind_dn, char const *bind_password,
995  request_t *request, fr_ldap_config_t const *config)
996 {
997  fr_ldap_thread_trunk_t *found, find = {.uri = uri, .bind_dn = bind_dn};
998 
999  ROPTIONAL(RDEBUG2, DEBUG2, "Looking for LDAP connection to \"%s\" bound as \"%s\"", uri,
1000  bind_dn ? bind_dn : "(anonymous)");
1001  found = fr_rb_find(thread->trunks, &find);
1002 
1003  if (found) return found;
1004 
1005  /*
1006  * No existing connection matching the requirement - create a new one
1007  */
1008  ROPTIONAL(RDEBUG2, DEBUG2, "No existing connection found - creating new one");
1009  found = talloc_zero(thread, fr_ldap_thread_trunk_t);
1010  talloc_set_destructor(found, _thread_ldap_trunk_free);
1011 
1012  /*
1013  * Build config for this connection - start with module settings and
1014  * override server and bind details
1015  */
1016  memcpy(&found->config, config, sizeof(fr_ldap_config_t));
1017  found->config.server = talloc_strdup(found, uri);
1018  found->config.admin_identity = talloc_strdup(found, bind_dn);
1019  found->config.admin_password = talloc_strdup(found, bind_password);
1020 
1021  found->uri = found->config.server;
1022  found->bind_dn = found->config.admin_identity;
1023 
1024  found->trunk = fr_trunk_alloc(found, thread->el,
1026  .connection_alloc = ldap_trunk_connection_alloc,
1027  .connection_notify = ldap_trunk_connection_notify,
1028  .request_mux = ldap_trunk_request_mux,
1029  .request_demux = ldap_trunk_request_demux,
1030  .request_cancel = ldap_request_cancel,
1031  .request_cancel_mux = ldap_request_cancel_mux,
1032  .request_fail = ldap_request_fail,
1033  },
1034  thread->trunk_conf,
1035  "rlm_ldap", found, false);
1036 
1037  if (!found->trunk) {
1038  error:
1039  ROPTIONAL(REDEBUG, ERROR, "Unable to create LDAP connection");
1040  talloc_free(found);
1041  return NULL;
1042  }
1043 
1044  found->t = thread;
1045 
1046  /*
1047  * Insert event to close trunk if it becomes idle
1048  */
1049  if (!fr_cond_assert_msg(fr_event_timer_in(found, thread->el, &found->ev, thread->config->idle_timeout,
1050  _ldap_trunk_idle_timeout, found) == 0, "cannot insert trunk idle event")) goto error;
1051 
1052  /*
1053  * Attempt to discover what type directory we are talking to
1054  */
1055  if (fr_ldap_trunk_directory_alloc_async(found, found) < 0) goto error;
1056 
1057  fr_rb_insert(thread->trunks, found);
1058 
1059  return found;
1060 }
1061 
1062 /** Lookup the state of a thread specific LDAP connection trunk for a specific URI / bind DN
1063  *
1064  * @param[in] thread to which the connection belongs
1065  * @param[in] uri of the host to find / create a connection to
1066  * @param[in] bind_dn to make the connection as
1067  * @return
1068  * - State of a trunk matching the URI and bind DN
1069  * - FR_TRUNK_STATE_MAX if no matching trunk
1070  */
1071 fr_trunk_state_t fr_thread_ldap_trunk_state(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn)
1072 {
1073  fr_ldap_thread_trunk_t *found, find = {.uri = uri, .bind_dn = bind_dn};
1074 
1075  found = fr_rb_find(thread->trunks, &find);
1076 
1077  return (found) ? found->trunk->state : FR_TRUNK_STATE_MAX;
1078 }
1079 
1080 /** Take pending LDAP bind auths from the queue and send them.
1081  *
1082  * @param[in] el Event list for timers.
1083  * @param[in] tconn Trunk handle.
1084  * @param[in] conn on which to send the queries
1085  * @param[in] uctx User context passed to fr_trunk_alloc
1086  */
1088  fr_connection_t *conn, void *uctx)
1089 {
1090  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1091  fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
1092  fr_ldap_thread_t *thread = ttrunk->t;
1093  fr_trunk_request_t *treq;
1094 
1095  fr_ldap_bind_auth_ctx_t *bind = NULL;
1096  int ret = 0;
1097  struct berval cred;
1098  request_t *request;
1099 
1100  if (fr_trunk_connection_pop_request(&treq, tconn) != 0) return;
1101 
1102  /* Pacify clang scan */
1103  if (!treq) return;
1104 
1105  bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
1106  request = bind->request;
1107 
1108  switch (bind->type) {
1109  case LDAP_BIND_SIMPLE:
1110  {
1111  fr_ldap_bind_ctx_t *bind_ctx = bind->bind_ctx;
1112 
1113  RDEBUG2("Starting bind auth operation as %s", bind_ctx->bind_dn);
1114 
1115  if (bind_ctx->password) {
1116  memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
1117  cred.bv_len = talloc_array_length(bind_ctx->password) - 1;
1118  } else {
1119  cred.bv_val = NULL;
1120  cred.bv_len = 0;
1121  }
1122 
1123  ret = ldap_sasl_bind(ldap_conn->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE,
1124  &cred, NULL, NULL, &bind->msgid);
1125 
1126  switch (ret) {
1127  case LDAP_SUCCESS:
1128  fr_rb_insert(thread->binds, bind);
1129  RDEBUG3("Bind auth sent as LDAP msgid %d", bind->msgid);
1130  break;
1131 
1132  default:
1133  bind->ret = LDAP_PROC_ERROR;
1134  unlang_interpret_mark_runnable(treq->request);
1135  RERROR("Failed to send bind auth");
1136  break;
1137  }
1138  }
1139  break;
1140 
1141 #ifdef WITH_SASL
1142  case LDAP_BIND_SASL:
1143  {
1144  fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
1145 
1146  RDEBUG2("%s SASL bind auth operation as %s", sasl_ctx->rmech ? "Continuing" : "Starting",
1147  sasl_ctx->identity);
1148 
1149  ret = fr_ldap_sasl_bind_auth_send(sasl_ctx, &bind->msgid, ldap_conn);
1150 
1151  switch (ret) {
1152  case LDAP_SASL_BIND_IN_PROGRESS:
1153  /*
1154  * Add the bind to the list of pending binds.
1155  */
1156  fr_rb_insert(thread->binds, bind);
1157  RDEBUG3("SASL bind auth sent as LDAP msgid %d", bind->msgid);
1158  break;
1159 
1160  case LDAP_SUCCESS:
1161  bind->ret = LDAP_PROC_SUCCESS;
1162  unlang_interpret_mark_runnable(treq->request);
1163  break;
1164 
1165  default:
1166  bind->ret = LDAP_PROC_ERROR;
1167  unlang_interpret_mark_runnable(treq->request);
1168  RERROR("Failed to send SASL bind auth");
1169  break;
1170  }
1171  }
1172 #endif
1173  break;
1174  }
1175  /*
1176  * The request is marked as sent, to remove from the pending list.
1177  * This is regardless of whether the sending was successful or not as
1178  * the different states are handled by the resume function which then
1179  * marks the request as complete triggering the tidy up.
1180  */
1182 }
1183 
1184 /** Read LDAP bind auth responses
1185  *
1186  * @param[in] el To insert timers into.
1187  * @param[in] tconn Trunk connection associated with these results.
1188  * @param[in] conn Connection handle for these results.
1189  * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
1190  */
1192  fr_connection_t *conn, void *uctx)
1193 {
1194  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1195  fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
1196  fr_ldap_thread_t *thread = ttrunk->t;
1197  fr_ldap_bind_auth_ctx_t find = { .msgid = -1 }, *bind = NULL;
1198 
1199  int ret = 0;
1200  LDAPMessage *result = NULL;
1201  request_t *request;
1202  bool really_no_result = false;
1203 
1204  do {
1205  /*
1206  * The first time ldap_result is called when there's pending network
1207  * data, it may read the data, but actually return a timeout.
1208  *
1209  * In order to fix the spurious debugging messages and overhead,
1210  * if this is the first iteration through the loop and fr_ldap_result
1211  * returns a timeout, we call it again.
1212  */
1213  ret = fr_ldap_result(&result, NULL, ldap_conn, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(10));
1214  if (ret == LDAP_PROC_TIMEOUT) {
1215  if (really_no_result) return;
1216  really_no_result = true;
1217  continue;
1218  }
1219 
1220  if (!result) return;
1221 
1222  really_no_result = true;
1223  find.msgid = ldap_msgid(result);
1224  bind = fr_rb_find(thread->binds, &find);
1225 
1226  if (!bind) {
1227  WARN("Ignoring bind result msgid %i - doesn't match any outstanding binds", find.msgid);
1228  ldap_msgfree(result);
1229  result = NULL;
1230  continue;
1231  }
1232  } while (!bind);
1233 
1234  /*
1235  * There will only ever be one bind in flight at a time on a given
1236  * connection - so having got a result, no need to loop.
1237  */
1238 
1239  fr_rb_remove(thread->binds, bind);
1240  request = bind->request;
1241  bind->ret = ret;
1242 
1243  switch (ret) {
1244  /*
1245  * Accept or reject will be SUCCESS, NOT_PERMITTED or REJECT
1246  */
1248  case LDAP_PROC_REJECT:
1249  case LDAP_PROC_BAD_DN:
1250  case LDAP_PROC_NO_RESULT:
1251  break;
1252 
1253  case LDAP_PROC_SUCCESS:
1254  if (bind->type == LDAP_BIND_SIMPLE) break;
1255 
1256  /*
1257  * With SASL binds, we will be here after ldap_sasl_interactive_bind
1258  * returned LDAP_SASL_BIND_IN_PROGRESS. That always requires a further
1259  * call of ldap_sasl_interactive_bind to get the final result.
1260  */
1261  bind->ret = LDAP_PROC_CONTINUE;
1262  FALL_THROUGH;
1263 
1264  case LDAP_PROC_CONTINUE:
1265  {
1266  fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
1267  struct berval *srv_cred;
1268 
1269  /*
1270  * Free any previous result and track the new one.
1271  */
1272  if (sasl_ctx->result) ldap_msgfree(sasl_ctx->result);
1273  sasl_ctx->result = result;
1274  result = NULL;
1275 
1276  ret = ldap_parse_sasl_bind_result(ldap_conn->handle, sasl_ctx->result, &srv_cred, 0);
1277  if (ret != LDAP_SUCCESS) {
1278  RERROR("SASL decode failed (bind failed): %s", ldap_err2string(ret));
1279  break;
1280  }
1281 
1282  if (srv_cred) {
1283  RDEBUG3("SASL response : %pV",
1284  fr_box_strvalue_len(srv_cred->bv_val, srv_cred->bv_len));
1285  ber_bvfree(srv_cred);
1286  }
1287 
1288  if (sasl_ctx->rmech) RDEBUG3("Continuing SASL mech %s...", sasl_ctx->rmech);
1289  }
1290  break;
1291 
1292  default:
1293  break;
1294  }
1295 
1296  ldap_msgfree(result);
1298 }
1299 
1300 /** Callback to cancel LDAP bind auth
1301  *
1302  * Inform the remote LDAP server that we no longer want responses to specific bind.
1303  *
1304  * @param[in] el For timer management.
1305  * @param[in] tconn The trunk connection handle
1306  * @param[in] conn The specific connection binds will be cancelled on
1307  * @param[in] uctx Context provided to fr_trunk_alloc
1308  */
1310  fr_connection_t *conn, UNUSED void *uctx)
1311 {
1312  fr_trunk_request_t *treq;
1313  fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1315 
1316  while ((fr_trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
1317  bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
1318 #ifdef WITH_SASL
1319  if (bind->type == LDAP_BIND_SASL) {
1320  /*
1321  * With SASL binds, abandoning the bind part way through
1322  * seems to leave the connection in an unpredictable state
1323  * so safer to restart.
1324  */
1326  } else {
1327 #endif
1328  ldap_abandon_ext(ldap_conn->handle, bind->msgid, NULL, NULL);
1329 #ifdef WITH_SASL
1330  }
1331 #endif
1333  }
1334 }
1335 
1336 /** Callback to tidy up when a bind auth trunk request fails
1337  *
1338  */
1339 static void ldap_trunk_bind_auth_fail(request_t *request, void *preq, UNUSED void *rctx,
1340  UNUSED fr_trunk_request_state_t state, UNUSED void *uctx)
1341 {
1342  fr_ldap_bind_auth_ctx_t *bind = talloc_get_type_abort(preq, fr_ldap_bind_auth_ctx_t);
1343 
1344  /*
1345  * Failed trunk requests get freed - so remove association in bind structure,
1346  * and change talloc parentage so resume function still has something to work with.
1347  */
1348  bind->treq = NULL;
1349  bind->ret = LDAP_PROC_ERROR;
1350  talloc_steal(NULL, bind);
1351 
1352  /*
1353  * Ensure request is runnable.
1354  */
1355  if (request) unlang_interpret_mark_runnable(request);
1356 }
1357 
1358 /** Find the thread specific trunk to use for LDAP bind auths
1359  *
1360  * If there is no current trunk then a new one is created.
1361  *
1362  * @param[in] thread to which the connection belongs
1363  * @return
1364  * - an existing or new trunk.
1365  * - NULL on failure
1366  */
1368 {
1369  fr_ldap_thread_trunk_t *ttrunk;
1370 
1371  if (thread->bind_trunk) return (thread->bind_trunk);
1372 
1373  MEM(ttrunk = talloc_zero(thread, fr_ldap_thread_trunk_t));
1374  memcpy(&ttrunk->config, thread->config, sizeof(fr_ldap_config_t));
1375 
1376  ttrunk->uri = ttrunk->config.server;
1377  ttrunk->bind_dn = ttrunk->config.admin_identity;
1378 
1379  ttrunk->trunk = fr_trunk_alloc(ttrunk, thread->el,
1381  .connection_alloc = ldap_trunk_connection_alloc,
1382  .connection_notify = ldap_trunk_connection_notify,
1383  .request_mux = ldap_trunk_bind_auth_mux,
1384  .request_demux = ldap_trunk_bind_auth_demux,
1385  .request_cancel_mux = ldap_bind_auth_cancel_mux,
1386  .request_fail = ldap_trunk_bind_auth_fail,
1387  },
1388  thread->bind_trunk_conf,
1389  "rlm_ldap bind auth", ttrunk, false);
1390 
1391  if (!ttrunk->trunk) {
1392  ERROR("Unable to create LDAP connection");
1393  talloc_free(ttrunk);
1394  return NULL;
1395  }
1396 
1397  ttrunk->t = thread;
1398  thread->bind_trunk = ttrunk;
1399 
1400  return ttrunk;
1401 }
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define DIAG_ON(_x)
Definition: build.h:419
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define DIAG_OFF(_x)
Definition: build.h:418
fr_connection_state_t
Definition: connection.h:45
@ FR_CONNECTION_STATE_CLOSED
Connection has been closed.
Definition: connection.h:55
@ FR_CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ FR_CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ FR_CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
Holds a complete set of functions for a connection.
Definition: connection.h:186
#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
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
static void * fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition: dlist.h:876
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition: dlist.h:939
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
#define fr_event_fd_insert(...)
Definition: event.h:232
void(* fr_event_fd_cb_t)(fr_event_list_t *el, int fd, int flags, void *uctx)
Called when an IO event occurs on a file descriptor.
Definition: event.h:137
@ 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_ldap_control_t serverctrls[LDAP_MAX_CONTROLS]
Server controls specific to this query.
Definition: base.h:444
LDAPURLDesc * referral_url
URL for the referral.
Definition: base.h:477
fr_ldap_thread_t * t
Thread this connection is associated with.
Definition: base.h:404
fr_trunk_request_t * treq
Trunk request this query is associated with.
Definition: base.h:451
fr_ldap_rcode_t ret
Return code of bind operation.
Definition: base.h:619
char const * admin_password
Password used in administrative bind.
Definition: base.h:229
fr_ldap_config_t * config
Module instance config.
Definition: base.h:381
void fr_ldap_control_clear(fr_ldap_connection_t *conn)
Clear and free any controls associated with a connection.
Definition: control.c:134
bool chase_referrals
If the LDAP server returns a referral to another server or point in the tree, follow it,...
Definition: base.h:238
int msgid
libldap msgid for this bind.
Definition: base.h:612
char * server
Initial server to bind to.
Definition: base.h:222
LDAP * handle
libldap handle.
Definition: base.h:331
int msgid
The unique identifier for this query.
Definition: base.h:448
char const * dn
Base DN for searches, DN for modifications.
Definition: base.h:427
char const * bind_dn
DN connection is bound as.
Definition: base.h:400
void fr_ldap_control_merge(LDAPControl *serverctrls_out[], LDAPControl *clientctrls_out[], size_t serverctrls_len, size_t clientctrls_len, fr_ldap_connection_t *conn, LDAPControl *serverctrls_in[], LDAPControl *clientctrls_in[])
Merge connection and call specific client and server controls.
Definition: control.c:48
fr_rb_node_t node
Entry in the tree of connections.
Definition: base.h:398
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition: base.h:227
fr_ldap_result_code_t ret
Result code.
Definition: base.h:465
fr_rb_tree_t * trunks
Tree of LDAP trunks used by this thread.
Definition: base.h:380
int fr_ldap_referral_follow(fr_ldap_thread_t *thread, request_t *request, fr_ldap_query_t *query)
Follow an LDAP referral.
Definition: referral.c:112
fr_trunk_conf_t * trunk_conf
Module trunk config.
Definition: base.h:382
fr_rb_tree_t * queries
Outstanding queries on this connection.
Definition: base.h:349
fr_connection_t * conn
Connection state handle.
Definition: base.h:343
fr_ldap_state_t
LDAP connection handle states.
Definition: base.h:166
@ FR_LDAP_STATE_ERROR
Connection is in an error state.
Definition: base.h:171
char const * identity
of the user.
Definition: base.h:504
fr_event_timer_t const * ev
Event to close the thread when it has been idle.
Definition: base.h:405
fr_dlist_head_t refs
Replied to queries still referencing this connection.
Definition: base.h:350
int fd
File descriptor for this connection.
Definition: base.h:347
int fr_ldap_trunk_directory_alloc_async(TALLOC_CTX *ctx, fr_ldap_thread_trunk_t *ttrunk)
Async extract useful information from the rootDSE of the LDAP server.
Definition: directory.c:253
char const * uri
Server URI for this connection.
Definition: base.h:399
LDAPMessage * result
Previous result.
Definition: base.h:512
fr_time_delta_t net_timeout
How long we wait in blocking network calls.
Definition: base.h:301
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition: base.h:342
request_t * request
this bind relates to.
Definition: base.h:613
fr_ldap_control_t clientctrls[LDAP_MAX_CONTROLS]
Client controls specific to this query.
Definition: base.h:445
@ LDAP_BIND_SIMPLE
Definition: base.h:517
static int8_t fr_ldap_query_cmp(void const *one, void const *two)
Compare two ldap query structures on msgid.
Definition: base.h:706
fr_ldap_config_t config
Config used for this connection.
Definition: base.h:401
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition: state.c:49
@ LDAP_REQUEST_MODIFY
A modification to an LDAP entity.
Definition: base.h:179
@ LDAP_REQUEST_SEARCH
A lookup in an LDAP directory.
Definition: base.h:178
@ LDAP_REQUEST_EXTENDED
An extended LDAP operation.
Definition: base.h:180
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition: base.h:452
fr_trunk_conf_t * bind_trunk_conf
Trunk config for bind auth trunk.
Definition: base.h:383
@ LDAP_RESULT_EXCESS_REFERRALS
The referral chain took too many hops.
Definition: base.h:195
@ LDAP_RESULT_REFERRAL_FAIL
Initial results indicated a referral was needed but the referral could not be followed.
Definition: base.h:193
@ LDAP_RESULT_ERROR
A general error occurred.
Definition: base.h:189
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition: base.h:188
@ LDAP_RESULT_PENDING
Result not yet returned.
Definition: base.h:187
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition: base.h:192
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition: base.h:191
@ LDAP_RESULT_MISSING_REFERRAL
A referral was indicated but no URL was provided.
Definition: base.h:196
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition: base.h:94
uint16_t referral_depth
How many referrals to chase.
Definition: base.h:245
fr_event_list_t * el
Thread event list for callbacks / timeouts.
Definition: base.h:384
char const * rmech
Mech we're continuing with.
Definition: base.h:513
fr_trunk_t * trunk
Connection trunk.
Definition: base.h:403
fr_ldap_thread_trunk_t * bind_trunk
LDAP trunk used for bind auths.
Definition: base.h:385
char const * bind_dn
of the user, may be NULL to bind anonymously.
Definition: base.h:490
fr_ldap_referral_t * referral
Referral actually being followed.
Definition: base.h:459
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition: base.h:386
fr_trunk_request_t * treq
Trunk request this bind is associated with.
Definition: base.h:611
fr_ldap_bind_type_t type
type of bind.
Definition: base.h:614
int fr_ldap_referral_next(fr_ldap_thread_t *thread, request_t *request, fr_ldap_query_t *query)
Follow an alternative LDAP referral.
Definition: referral.c:309
char const * password
of the user, may be NULL if no password is specified.
Definition: base.h:491
fr_time_delta_t idle_timeout
How long to wait before closing unused connections.
Definition: base.h:311
fr_ldap_request_type_t type
What type of query this is.
Definition: base.h:442
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition: base.h:577
@ LDAP_PROC_CONTINUE
Operation is in progress.
Definition: base.h:579
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition: base.h:580
@ LDAP_PROC_REFERRAL
LDAP server returned referral URLs.
Definition: base.h:578
@ LDAP_PROC_TIMEOUT
Operation timed out.
Definition: base.h:597
@ LDAP_PROC_ERROR
Unrecoverable library/server error.
Definition: base.h:582
@ LDAP_PROC_BAD_CONN
Transitory error, caller should retry the operation with a new connection.
Definition: base.h:584
@ LDAP_PROC_NOT_PERMITTED
Operation was not permitted, either current user was locked out in the case of binds,...
Definition: base.h:587
@ LDAP_PROC_REJECT
Bind failed, user was rejected.
Definition: base.h:591
@ LDAP_PROC_BAD_DN
Specified an invalid object in a bind or search DN.
Definition: base.h:593
@ LDAP_PROC_NO_RESULT
Got no results.
Definition: base.h:595
Holds arguments for async bind auth requests.
Definition: base.h:608
Holds arguments for the async bind operation.
Definition: base.h:488
Connection configuration.
Definition: base.h:219
Tracks the state of a libldap connection handle.
Definition: base.h:330
LDAP query structure.
Definition: base.h:420
Holds arguments for the async SASL bind operation.
Definition: base.h:501
Thread specific structure to manage LDAP trunk connections.
Definition: base.h:379
Thread LDAP trunk structure.
Definition: base.h:397
static void ldap_bind_auth_cancel_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
Callback to cancel LDAP bind auth.
Definition: connection.c:1309
static void ldap_request_cancel(UNUSED fr_connection_t *conn, void *preq, UNUSED fr_trunk_cancel_reason_t reason, UNUSED void *uctx)
Callback when an LDAP trunk request is cancelled.
Definition: connection.c:471
static fr_connection_t * ldap_trunk_connection_alloc(fr_trunk_connection_t *tconn, fr_event_list_t *el, UNUSED fr_connection_conf_t const *conn_conf, char const *log_prefix, void *uctx)
Allocate an LDAP trunk connection.
Definition: connection.c:631
static void _ldap_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
Free the handle, closing the connection to ldap.
Definition: connection.c:190
static void ldap_trunk_connection_notify(fr_trunk_connection_t *tconn, fr_connection_t *conn, fr_event_list_t *el, fr_trunk_connection_event_t notify_on, UNUSED void *uctx)
Setup callbacks requested by LDAP trunk connections.
Definition: connection.c:586
static void ldap_conn_writable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
I/O write function.
Definition: connection.c:551
fr_ldap_thread_trunk_t * fr_thread_ldap_trunk_get(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn, char const *bind_password, request_t *request, fr_ldap_config_t const *config)
Find a thread specific LDAP connection for a specific URI / bind DN.
Definition: connection.c:993
static void ldap_trunk_bind_auth_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED fr_trunk_request_state_t state, UNUSED void *uctx)
Callback to tidy up when a bind auth trunk request fails.
Definition: connection.c:1339
fr_ldap_connection_t * fr_ldap_connection_alloc(TALLOC_CTX *ctx)
Allocate our ldap connection handle layer.
Definition: connection.c:253
static void ldap_trunk_request_demux(fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, void *uctx)
Read LDAP responses.
Definition: connection.c:764
#define do_ldap_option(_option, _name, _value)
static int _ldap_connection_free(fr_ldap_connection_t *c)
Close and delete a connection.
Definition: connection.c:216
static void _ldap_trunk_idle_timeout(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Callback for closing idle LDAP trunk.
Definition: connection.c:450
static void ldap_conn_readable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
I/O read function.
Definition: connection.c:534
static fr_connection_state_t _ldap_connection_init(void **h, fr_connection_t *conn, void *uctx)
(Re-)Initialises the libldap side of the connection handle
Definition: connection.c:336
static void ldap_conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
I/O error function.
Definition: connection.c:569
fr_trunk_state_t fr_thread_ldap_trunk_state(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn)
Lookup the state of a thread specific LDAP connection trunk for a specific URI / bind DN.
Definition: connection.c:1071
static USES_APPLE_DEPRECATED_API char const * ldap_msg_types[UINT8_MAX]
Definition: connection.c:34
static void ldap_trunk_request_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
Take LDAP pending queries from the queue and send them.
Definition: connection.c:655
static void ldap_request_cancel_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, UNUSED void *uctx)
Callback to cancel LDAP queries.
Definition: connection.c:490
fr_ldap_thread_trunk_t * fr_thread_ldap_bind_trunk_get(fr_ldap_thread_t *thread)
Find the thread specific trunk to use for LDAP bind auths.
Definition: connection.c:1367
static void ldap_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED fr_trunk_request_state_t state, UNUSED void *uctx)
Callback to tidy up when a trunk request fails.
Definition: connection.c:508
#define maybe_ldap_option(_option, _name, _value)
static int _thread_ldap_trunk_free(fr_ldap_thread_trunk_t *ttrunk)
Definition: connection.c:972
static void ldap_trunk_bind_auth_demux(UNUSED fr_event_list_t *el, UNUSED fr_trunk_connection_t *tconn, fr_connection_t *conn, void *uctx)
Read LDAP bind auth responses.
Definition: connection.c:1191
fr_connection_t * fr_ldap_connection_state_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_ldap_config_t const *config, char const *log_prefix)
Alloc a self re-establishing connection to an LDAP server.
Definition: connection.c:380
#define POPULATE_LDAP_CONTROLS(_dest, _src)
Definition: connection.c:640
static void _ldap_connection_close_watch(fr_connection_t *conn, UNUSED fr_connection_state_t prev, UNUSED fr_connection_state_t state, void *uctx)
Watcher for LDAP connections being closed.
Definition: connection.c:281
static void ldap_trunk_bind_auth_mux(UNUSED fr_event_list_t *el, fr_trunk_connection_t *tconn, fr_connection_t *conn, void *uctx)
Take pending LDAP bind auths from the queue and send them.
Definition: connection.c:1087
int fr_ldap_connection_timeout_set(fr_ldap_connection_t const *c, fr_time_delta_t timeout)
Definition: connection.c:403
int fr_ldap_connection_configure(fr_ldap_connection_t *c, fr_ldap_config_t const *config)
Allocate and configure a new connection.
Definition: connection.c:67
int fr_ldap_connection_timeout_reset(fr_ldap_connection_t const *c)
Definition: connection.c:425
fr_ldap_rcode_t fr_ldap_error_check(LDAPControl ***ctrls, fr_ldap_connection_t const *conn, LDAPMessage *msg, char const *dn)
Perform basic parsing of multiple types of messages, checking for error conditions.
Definition: base.c:232
fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Search for something in the LDAP directory.
Definition: base.c:528
fr_ldap_rcode_t fr_ldap_result(LDAPMessage **result, LDAPControl ***ctrls, fr_ldap_connection_t const *conn, int msgid, int all, char const *dn, fr_time_delta_t timeout)
Parse response from LDAP server dealing with any errors.
Definition: base.c:449
fr_ldap_rcode_t fr_ldap_modify_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, LDAPMod *mods[], LDAPControl **serverctrls, LDAPControl **clientctrls)
Modify something in the LDAP directory.
Definition: base.c:786
fr_ldap_rcode_t fr_ldap_extended_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *reqoid, struct berval *reqdata)
Initiate an LDAP extended operation.
Definition: base.c:870
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
#define RPERROR(fmt,...)
Definition: log.h:302
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
#define UINT8_MAX
Definition: merged_model.c:32
static const conf_parser_t config[]
Definition: base.c:188
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define WARN(fmt,...)
Definition: radclient.h:47
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition: rb.c:775
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition: rb.c:691
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition: rb.c:624
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rb.c:576
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition: rb.h:246
static bool fr_rb_node_inline_in_tree(fr_rb_node_t const *node)
Check to see if an item is in a tree by examining its inline fr_rb_node_t.
Definition: rb.h:314
int fr_ldap_sasl_bind_auth_send(fr_ldap_sasl_ctx_t *sasl_ctx, int *msgid, fr_ldap_connection_t *ldap_conn)
Send a SASL LDAP auth bind.
Definition: sasl.c:367
fr_connection_watch_entry_t * fr_connection_add_watch_pre(fr_connection_t *conn, fr_connection_state_t state, fr_connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed before a state function has been called.
Definition: connection.c:509
fr_connection_t * fr_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_connection_funcs_t const *funcs, fr_connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
Definition: connection.c:1507
void fr_connection_signal_reconnect(fr_connection_t *conn, fr_connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1166
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define fr_time_delta_wrap(_time)
Definition: time.h:152
#define fr_time_delta_ispos(_a)
Definition: time.h:288
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
Definition: time.h:654
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:645
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
"server local" time.
Definition: time.h:69
int fr_trunk_connection_pop_cancellation(fr_trunk_request_t **treq_out, fr_trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition: trunk.c:3708
void fr_trunk_request_signal_sent(fr_trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:1973
void fr_trunk_request_signal_complete(fr_trunk_request_t *treq)
Signal that a trunk request is complete.
Definition: trunk.c:1995
fr_trunk_t * fr_trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_trunk_io_funcs_t const *funcs, fr_trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start)
Allocate a new collection of connections.
Definition: trunk.c:4767
int fr_trunk_connection_pop_request(fr_trunk_request_t **treq_out, fr_trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3756
void fr_trunk_connection_signal_readable(fr_trunk_connection_t *tconn)
Signal that a trunk connection is readable.
Definition: trunk.c:3794
void fr_trunk_request_signal_fail(fr_trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2027
void fr_trunk_connection_signal_reconnect(fr_trunk_connection_t *tconn, fr_connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition: trunk.c:3872
void fr_trunk_connection_signal_writable(fr_trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition: trunk.c:3776
void fr_trunk_request_signal_cancel_complete(fr_trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition: trunk.c:2179
Associates request queues with a connection.
Definition: trunk.c:127
Wraps a normal request.
Definition: trunk.c:97
fr_trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition: trunk.h:55
fr_trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition: trunk.h:161
fr_trunk_state_t
Definition: trunk.h:62
@ FR_TRUNK_STATE_MAX
Definition: trunk.h:66
fr_trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition: trunk.h:72
@ FR_TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition: trunk.h:73
@ FR_TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition: trunk.h:77
@ FR_TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition: trunk.h:75
@ FR_TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition: trunk.h:79
I/O functions to pass to fr_trunk_alloc.
Definition: trunk.h:711
static fr_event_list_t * el
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279