The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
proto_ldap_sync_ldap.c
Go to the documentation of this file.
1/*
2 * This program 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
5 * (at 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: 2859d501e51f2f647ed840cd23f8a35ff0a09ebc $
19 * @file proto_ldap_sync_ldap.c
20 * @brief LDAP sync handler.
21 *
22 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23 */
25
26#define LOG_PREFIX "proto_ldap_sync_ldap"
27
28#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
29#include <freeradius-devel/internal/internal.h>
30#include <freeradius-devel/server/protocol.h>
31#include <freeradius-devel/server/request.h>
32#include <freeradius-devel/io/listen.h>
33#include <freeradius-devel/unlang/call.h>
34#include <freeradius-devel/util/dbuff.h>
35#include <freeradius-devel/ldap/base.h>
36#include <freeradius-devel/ldap/conf.h>
37
39#include "rfc4533.h"
40#include "persistent_search.h"
41#include "active_directory.h"
42
44
47
49 /*
50 * LDAP server definition
51 */
53
54 /*
55 * Common LDAP conf parsers
56 */
58
59 /*
60 * Network tunable parameters
61 */
62 { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, proto_ldap_sync_ldap_t, recv_buff) },
63 { FR_CONF_OFFSET("max_outstanding", proto_ldap_sync_ldap_t, max_outstanding), .dflt = "65536" },
64
66};
67
70
73 { .out = &dict_ldap_sync, .proto = "ldap" },
74 { .out = &dict_freeradius, .proto = "freeradius" },
76};
77
86
89 { .out = &attr_ldap_sync_packet_id, .name = "Sync-Packet-ID", .type = FR_TYPE_UINT32, .dict = &dict_ldap_sync },
90 { .out = &attr_ldap_sync_cookie, .name = "LDAP-Sync.Cookie", .type = FR_TYPE_OCTETS, .dict = &dict_ldap_sync },
91 { .out = &attr_ldap_sync_entry_dn, .name = "LDAP-Sync.Entry-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
92 { .out = &attr_ldap_sync_entry_uuid, .name = "LDAP-Sync.Entry-UUID", .type = FR_TYPE_OCTETS, .dict = &dict_ldap_sync },
93 { .out = &attr_ldap_sync_orig_dn, .name = "LDAP-Sync.Original-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
94 { .out = &attr_ldap_sync_root_dn, .name = "LDAP-Sync.Directory-Root-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
95 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_ldap_sync },
96 { .out = &attr_ldap_sync_base_dn, .name = "LDAP-Sync-Base-DN", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
98};
99
105
106/** Operations performed on entries
107 */
109 { L("add"), SYNC_OP_ADD },
110 { L("delete"), SYNC_OP_DELETE },
111 { L("modify"), SYNC_OP_MODIFY },
112 { L("present"), SYNC_OP_PRESENT },
113};
115
116/** Context used when looking up Directory types
117 */
124
125/** Context for "load Cookie" retry timed event
126 */
132
133/** Compare two sync state structures on msgid
134 *
135 * @param[in] one first sync to compare.
136 * @param[in] two second sync to compare.
137 * @return CMP(one, two)
138 */
139int8_t sync_state_cmp(void const *one, void const *two)
140{
141 sync_state_t const *a = one, *b = two;
142
143 return CMP(a->msgid, b->msgid);
144}
145
146/** Tell the remote server to stop the sync
147 *
148 * Terminates the search informing the remote server that we no longer want to receive results
149 * for this sync. A RFC 4511 abandon request is used to inform the server.
150 *
151 * This allows individual syncs to be stopped without destroying the underlying connection.
152 *
153 * Removes the sync's msgid from the tree of msgids associated with the connection.
154 *
155 * @param[in] sync to abandon.
156 * @return 0
157 */
159{
160 fr_ldap_connection_t *conn = talloc_get_type_abort(sync->conn, fr_ldap_connection_t);
161 fr_rb_tree_t *tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
162
163 DEBUG3("Abandoning sync base dn \"%s\", filter \"%s\"", sync->config->base_dn, sync->config->filter);
164
165 trigger(unlang_interpret_get_thread_default(), sync->config->cs, NULL, "modules.ldap_sync.stop", true, &sync->trigger_args);
166
167 if (!sync->conn->handle) return 0; /* Handled already closed? */
168
169 /*
170 * Tell the remote server to stop sending results
171 */
172 if (sync->msgid >= 0) ldap_abandon_ext(sync->conn->handle, sync->msgid, NULL, NULL);
173 fr_rb_delete(tree, &(sync_state_t){.msgid = sync->msgid});
174
175 return 0;
176}
177
178/** Allocate a sync state
179 *
180 * @param[in] ctx to allocate the sync state in.
181 * @param[in] conn which the sync will run on.
182 * @param[in] inst module instance for the sync.
183 * @param[in] sync_no number of the sync in the array of configs.
184 * @param[in] config for the sync.
185 * @return new sync state.
186 */
188 size_t sync_no, sync_config_t const *config)
189{
190 sync_state_t *sync;
191 fr_pair_t *vp;
192
193 MEM(sync = talloc_zero(ctx, sync_state_t));
194 sync->conn = conn;
195 sync->inst = inst;
196 sync->config = config;
197 sync->sync_no = sync_no;
198 sync->phase = SYNC_PHASE_INIT;
199
201
202 /*
203 * Create arguments to pass to triggers
204 */
207 talloc_strlen(config->base_dn), false);
208
209 /*
210 * If the connection is freed, all the sync state is also freed
211 */
212 talloc_set_destructor(sync, sync_state_free);
213
214 return sync;
215}
216
217/** Add a new cookie packet ctx to the pending list
218 *
219 * Does not actually send the packet.
220 *
221 * @param[in] sync the cookie was received for.
222 * @param[in] refresh the sync after storing this cookie.
223 * @return
224 * - 0 on success.
225 * - -1 on failure
226 */
227int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
228{
229 sync_packet_ctx_t *sync_packet_ctx = NULL;
230 uint8_t *cookie = sync->cookie;
231
232 MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
233 sync_packet_ctx->sync = sync;
234
235 sync_packet_ctx->type = SYNC_PACKET_TYPE_COOKIE;
236 if (cookie) sync_packet_ctx->cookie = talloc_memdup(sync_packet_ctx, cookie, talloc_array_length(cookie));
237 sync_packet_ctx->refresh = refresh;
238
239 if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) {
240 talloc_free(sync_packet_ctx);
241 return -1;
242 }
243 sync->pending_cookies++;
244
245 return 0;
246}
247
248/** Event to handle storing of cookies on a timed basis
249 *
250 * Looks at the head of the list of pending sync packets for a cookie.
251 * A cookie at the head says that all the previous changes have been
252 * completed, so the cookie can be sent.
253 */
255{
256 sync_state_t *sync = talloc_get_type_abort(uctx, sync_state_t);
257 sync_packet_ctx_t *sync_packet_ctx;
258
259 if (sync->pending_cookies == 0) goto finish;
260
261 /*
262 * Check the head entry in the list - is it a pending cookie
263 */
264 sync_packet_ctx = fr_dlist_head(&sync->pending);
265 if ((sync_packet_ctx->type != SYNC_PACKET_TYPE_COOKIE) ||
266 (sync_packet_ctx->status != SYNC_PACKET_PENDING)) goto finish;
267
268 ldap_sync_cookie_send(sync_packet_ctx);
269
270finish:
271 (void) fr_timer_in(sync, tl, &sync->cookie_ev, sync->inst->cookie_interval,
272 false, ldap_sync_cookie_event, sync);
273}
274
275/** Enqueue a new cookie store packet
276 *
277 * Create a new internal packet containing the cookie we received from the LDAP server.
278 * This allows the administrator to store the cookie and provide it on a future call to
279 * load Cookie.
280 *
281 * @param[in] sync_packet_ctx packet context containing the cookie to store.
282 * @return
283 * - 0 on success.
284 * - -1 on failure.
285*/
287{
288 sync_state_t *sync = sync_packet_ctx->sync;
289 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(sync->config->user_ctx, proto_ldap_sync_ldap_thread_t);
290 fr_dbuff_t *dbuff;
291 fr_pair_list_t pairs;
292 fr_pair_t *vp;
293 TALLOC_CTX *local = NULL;
294 uint8_t *cookie = sync_packet_ctx->cookie;
295
296 if (sync_packet_ctx->status != SYNC_PACKET_PENDING) return 0;
297 sync_packet_ctx->status = SYNC_PACKET_PREPARING;
298
299 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
300
301 local = talloc_new(NULL);
302 fr_pair_list_init(&pairs);
303 if (fr_pair_list_copy(local, &pairs, &sync->config->sync_pairs) < 0) {
304 error:
305 talloc_free(local);
306 return -1;
307 }
308
310 if (!vp) goto error;
311
313 if (!vp) goto error;
314
315 /*
316 * Add the cookie to the packet, if set.
317 * If the server has indicated a refresh is required it can do so
318 * with no cookie set - so we store a blank cookie to clear anything
319 * which was previously stored.
320 */
321 if (cookie) {
323 cookie, talloc_array_length(cookie), true);
324 if (!vp) goto error;
325 }
326
327 if (fr_internal_encode_list(dbuff, &pairs, &encode_ctx) < 0) goto error;
328 talloc_free(local);
329
330 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
331 fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx) < 0) {
332 sync_packet_ctx->status = SYNC_PACKET_PENDING;
333 return -1;
334 }
335
336 sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
337
338 return 0;
339}
340
341/** Send a change packet to the workers
342 *
343 * Called each time a change packet is received and also from a
344 * timer event retrying packets which previously failed to send.
345 *
346 * @param sync_packet_ctx Packet to send
347 * @return
348 * - 0 on success.
349 * - -1 on failure.
350 */
352{
353 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(sync_packet_ctx->sync->config->user_ctx,
355 fr_dbuff_t *dbuff;
356
357 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
358
359 if (fr_internal_encode_list(dbuff, &sync_packet_ctx->pairs, &encode_ctx) < 0) return -1;
360 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
361 fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx) < 0) return -1;
362
363 sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
364 fr_pair_list_free(&sync_packet_ctx->pairs);
365
366 return 0;
367}
368
369/** Event to handle sending of any change packets which failed to send.
370 *
371 * Looks at the head of the list of pending sync packets for unsent
372 * change packets and sends any up to the first cookie.
373 */
374static void ldap_sync_retry_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
375{
376 sync_state_t *sync = talloc_get_type_abort(uctx, sync_state_t);
377 sync_packet_ctx_t *sync_packet_ctx = NULL;
378
379 while ((sync_packet_ctx = fr_dlist_next(&sync->pending, sync_packet_ctx))) {
380 if (sync_packet_ctx->type != SYNC_PACKET_TYPE_CHANGE) break;
381 if (sync_packet_ctx->status != SYNC_PACKET_PENDING) continue;
382
383 /*
384 * Retry sending packet. Don't try any more if it fails.
385 */
386 if (ldap_sync_entry_send_network(sync_packet_ctx) < 0) break;
387 }
388
389 /*
390 * We didn't run through the whole list, so there may be other pending
391 * packets - reschedule a retry event.
392 */
393 if (sync_packet_ctx) {
394 (void) fr_timer_in(sync, tl, &sync->retry_ev, sync->inst->retry_interval,
395 false, ldap_sync_retry_event, sync);
396 }
397}
398
405
406/** Enqueue a new entry change packet.
407 *
408 * @param[in] sync notification has arrived for.
409 * @param[in] uuid of the entry (RFC 4533 only).
410 * @param[in] orig_dn original DN of the entry - provided by those directories
411 * implementing persistent search, when an entry is renamed.
412 * @param[in] msg containing the entry.
413 * @param[in] op The type of modification we need to perform to our
414 * representation of the entry.
415 * @return
416 * - 0 on success.
417 * - -1 on failure.
418 */
419int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH], struct berval *orig_dn,
420 LDAPMessage *msg, sync_op_t op)
421{
423 fr_pair_list_t *pairs;
424 fr_pair_t *vp;
425 sync_packet_ctx_t *sync_packet_ctx = NULL;
426
427 MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
428 sync_packet_ctx->sync = sync;
429
430 fr_pair_list_init(&sync_packet_ctx->pairs);
431 pairs = &sync_packet_ctx->pairs;
432
433 if (fr_pair_list_copy(sync_packet_ctx, pairs, &sync->config->sync_pairs) < 0) {
434 error:
435 if (msg) ldap_msgfree(msg);
436 talloc_free(sync_packet_ctx);
437 return -1;
438 }
439
440 pcode = sync_packet_code_table[op];
441
442 fr_pair_list_append_by_da(sync_packet_ctx, vp, pairs, attr_packet_type, (uint32_t)pcode, false);
443 if (!vp) goto error;
444
445 fr_pair_list_append_by_da(sync_packet_ctx, vp, pairs, attr_ldap_sync_packet_id, (uint32_t)sync->sync_no, false);
446 if (!vp) goto error;
447
448 /*
449 * Add the UUID if provided
450 */
451 if (uuid) {
453 uuid, SYNC_UUID_LENGTH, true);
454 if (!vp) goto error;
455 }
456
457 /*
458 * Add the original DN if provided
459 */
460 if (orig_dn && (orig_dn->bv_len > 0)) {
462 orig_dn->bv_val, orig_dn->bv_len, true);
463 if (!vp) goto error;
464 }
465
466 /*
467 * Add the entry DN if there is an LDAP message to read
468 */
469 if (msg) {
470 char *entry_dn = ldap_get_dn(sync->conn->handle, msg);
471 map_t const *map = NULL;
472 struct berval **values;
473 int count, i;
474
476 entry_dn, strlen(entry_dn), true);
477 if (!vp) goto error;
478
479 ldap_memfree(entry_dn);
480
481 /*
482 * Map LDAP returned attributes to pairs as per update map
483 */
484 while ((map = map_list_next(&sync->config->entry_map, map))) {
485 values = ldap_get_values_len(fr_ldap_handle_thread_local(), msg, map->rhs->name);
486 if (!values) goto next;
487
488 count = ldap_count_values_len(values);
489
490 for (i = 0; i < count; i++) {
491 if (values[i]->bv_len == 0) continue;
492
493 if (pair_append_by_tmpl_parent(sync_packet_ctx, &vp, pairs, map->lhs, true) < 0) break;
494 if (fr_value_box_from_str(vp, &vp->data, vp->vp_type, NULL, values[i]->bv_val,
495 values[i]->bv_len, NULL) < 0) {
496 fr_pair_remove(pairs, vp);
498 }
499
500 /* Only += operator adds multiple values */
501 if (map->op != T_OP_ADD_EQ) break;
502 }
503 next:
504 ldap_value_free_len(values);
505 }
506 }
507
508 if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) goto error;
509
510 ldap_msgfree(msg);
511
512 /*
513 * Send the packet and if it fails to send add a retry event
514 */
515 if ((ldap_sync_entry_send_network(sync_packet_ctx) < 0) &&
516 (fr_timer_in(sync, sync->conn->conn->el->tl, &sync->retry_ev,
517 sync->inst->retry_interval, false, ldap_sync_retry_event, sync) < 0)) {
518 PERROR("Inserting LDAP sync retry timer failed");
519 }
520
521 return 0;
522}
523
525 UNUSED connection_state_t state, void *uctx);
526
528 UNUSED connection_state_t state, void *uctx);
529
530/** Attempt to (re)initialise a connection
531 *
532 * Performs complete re-initialization of a connection. Called during socket_open
533 * to create the initial connection and again any time we need to reopen the connection.
534 *
535 * @param[in] tl the event list managing listen event.
536 * @param[in] now current time.
537 * @param[in] user_ctx Listener.
538 */
539static void proto_ldap_connection_init(fr_timer_list_t *tl, UNUSED fr_time_t now, void *user_ctx)
540{
541 fr_listen_t *listen = talloc_get_type_abort(user_ctx, fr_listen_t);
542 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(listen->thread_instance, proto_ldap_sync_ldap_thread_t);
544
545 if (thread->conn) talloc_free(thread->conn);
546
547 /*
548 * Allocate an outbound LDAP connection
549 */
550 thread->conn = connection_alloc(thread, thread->el,
552 .init = fr_ldap_connection_init,
553 .close = fr_ldap_connection_close
554 },
556 .connection_timeout = inst->handle_config.net_timeout,
557 .reconnection_delay = inst->handle_config.reconnection_delay
558 },
559 "ldap_sync", &inst->handle_config);
560
561 if (!thread->conn) {
562 PERROR("Failed (re)initialising connection, will retry in %pV seconds",
563 fr_box_time_delta(inst->handle_config.reconnection_delay));
564
565 if (fr_timer_in(thread, tl, &thread->conn_retry_ev,
566 inst->handle_config.reconnection_delay,
567 false, proto_ldap_connection_init, listen) < 0) {
568 FATAL("Failed inserting event: %s", fr_strerror());
569 }
570 }
571
572 /*
573 * Add watch functions on the LDAP connection
574 */
576 _proto_ldap_socket_init, true, thread);
577
580
581 /*
582 * Signal the connection to start
583 */
585
586 return;
587}
588
589/** Child listener mod_close
590 *
591 * Ensures the LDAP connection is signalled to close gracefully when
592 * the listener is closed.
593 */
595{
597
599 return 0;
600}
601
602/** LDAP sync mod_read for child listener
603 *
604 * Called when there is data to read on the LDAP connection
605 *
606 * Actual packets are created by the various callbacks since a single LDAP
607 * message can result in multiple packets to process e.g.:
608 *
609 * - Sync Info Message with syncInfoValue of syncIdSet can reference
610 * multiple directory entries.
611 * - Various sync related messages can include a new cookie in
612 * addition to their other data.
613 */
615 UNUSED size_t buffer_len, UNUSED size_t *leftover)
616{
618 fr_ldap_connection_t *conn = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
619 struct timeval poll = { 1, 0 };
620 LDAPMessage *msg = NULL;
621 int ret = 0;
622 fr_ldap_rcode_t rcode;
623 sync_state_t *sync = NULL;
624 fr_rb_tree_t *tree;
625 int type, msgid;
626 LDAPControl **ctrls = NULL;
627 sync_msg_t callback = NULL;
628
629 fr_assert(conn);
630
631 /*
632 * If there are already too many outstanding requests just return.
633 * This will (potentially) cause the TCP buffer to fill and push the
634 * backpressure back to the LDAP server.
635 */
636 if (fr_network_listen_outstanding(thread->nr, li) >= thread->inst->max_outstanding) return 0;
637
638 tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
639
640 /*
641 * Pull the next outstanding message from this connection.
642 * We process one message at a time so that the message can be
643 * passed to the worker, and freed once the request has been
644 * handled.
645 */
646 ret = ldap_result(conn->handle, LDAP_RES_ANY, LDAP_MSG_ONE, &poll, &msg);
647
648 switch (ret) {
649 case 0: /*
650 * Timeout - this has been observed if changes are being
651 * processed slowly, the TCP receive buffer fills and
652 * the LDAP directory pauses sending data for a period.
653 * Then all pending changes are processed and the receive buffer
654 * is emptied.
655 * The situation resolves when the directory starts sending
656 * data again.
657 */
658 return 0;
659
660 case -1:
661 rcode = fr_ldap_error_check(NULL, conn, NULL, NULL);
662 if (rcode == LDAP_PROC_BAD_CONN) return -2;
663 return -1;
664
665 default:
666 break;
667 }
668
669 /*
670 * De-multiplex based on msgid
671 */
672 if (!msg) return 0;
673
674 msgid = ldap_msgid(msg);
675 type = ldap_msgtype(msg);
676
677 ret = 0;
678 if (msgid == 0) {
679 WARN("Ignoring unsolicited %s message",
681 free_msg:
682 if (ctrls) ldap_controls_free(ctrls);
683 ldap_msgfree(msg);
684 return ret;
685 }
686
687 sync = fr_rb_find(tree, &(sync_state_t){.msgid = msgid});
688 if (!sync) {
689 WARN("Ignoring msgid %i, doesn't match any outstanding syncs", msgid);
690 goto free_msg;
691 }
692
693 /*
694 * Check for errors contained within the message.
695 * This has to be per message, as multiple syncs
696 * are multiplexed together on one connection.
697 */
698 switch (fr_ldap_error_check(&ctrls, conn, msg, sync->config->base_dn)) {
700 break;
701
702 /*
703 * The e-syncRefresRequired result code is the server informing us that
704 * the query needs to be restarted for a new refresh phase to run.
705 * It is sent as the result code for a SearchResultsDone message.
706 */
708 if (type != LDAP_RES_SEARCH_RESULT) {
709 PERROR("e-syncRefreshRequired result code received on wrong message type");
710 ret = -1;
711 goto free_msg;
712 }
713
714 DEBUG2("LDAP Server returned e-syncRefreshRequired");
715 if (sync->config->refresh) {
716 return sync->config->refresh(sync, msg, ctrls);
717 }
718 goto free_msg;
719
720 /*
721 * Don't think this should happen... but libldap
722 * is wonky sometimes...
723 */
725 PERROR("Connection unusable");
726 ret = -2;
727 goto free_msg;
728
729 default:
730 PERROR("Sync error");
731 ret = -1;
732 goto free_msg;
733 }
734
735 DEBUG3("Got %s message for sync (msgid %i)",
737
738 switch (type) {
739 case LDAP_RES_SEARCH_REFERENCE:
740 case LDAP_RES_SEARCH_ENTRY:
741 callback = sync->config->entry;
742 break;
743
744 case LDAP_RES_INTERMEDIATE:
745 callback = sync->config->intermediate;
746 break;
747
748 default:
749 WARN("Ignoring unexpected message type (%i)", type);
750 ret = 0;
751 goto free_msg;
752 }
753
754 if (callback) {
755 ret = callback(sync, msg, ctrls);
756 if (ret < 0) PERROR("Sync callback error");
757 } else {
758 /*
759 * Callbacks are responsible for freeing the msg
760 * so if there is no callback, free it.
761 */
762 ldap_msgfree(msg);
763 }
764
765 ldap_controls_free(ctrls);
766
767 return ret;
768}
769
770/** Send a fake packet to run the "load Cookie" section
771 *
772 * @param ctx Context to allocate temporary pairs in.
773 * @param inst LDAP sync configuration.
774 * @param sync_no Id of the sync whose.
775 * @param thread Thread specific LDAP sync data.
776 * @return
777 * - 0 on success
778 * - -1 on failure
779 */
780static int proto_ldap_cookie_load_send(TALLOC_CTX *ctx, proto_ldap_sync_ldap_t const *inst, size_t sync_no,
782 size_t len;
783 sync_config_t *config = inst->parent->sync_config[sync_no];
784 fr_pair_list_t pairs;
785 fr_pair_t *vp;
786 fr_dbuff_t *dbuff;
787 fr_ldap_connection_t *ldap_conn = thread->conn->h;
788
789 fr_pair_list_init(&pairs);
790 if (unlikely(fr_pair_list_copy(ctx, &pairs, &config->sync_pairs) < 0)) return -1;
791
792 /*
793 * Ensure we have access to the thread instance
794 * in for the demux callbacks
795 */
796 inst->parent->sync_config[sync_no]->user_ctx = thread;
797
798 /*
799 * Assess the namingContext which applies to this sync
800 */
801 if (ldap_conn->directory->naming_contexts) {
802 char const * const *contexts = ldap_conn->directory->naming_contexts;
803 size_t j, num = talloc_str_array_len(contexts);
804 size_t base_dn_len = talloc_strlen(config->base_dn);
805
806 for (j = 0; j < num; j++) {
807 len = talloc_strlen(contexts[j]);
808 if (base_dn_len < len) continue;
809
810 if (strncasecmp(&config->base_dn[base_dn_len - len], contexts[j], len) == 0) {
811 config->root_dn = contexts[j];
812 break;
813 }
814 }
815 }
816
817 /*
818 * Set up callbacks based on directory type.
819 */
820 switch (ldap_conn->directory->sync_type) {
824 config->intermediate = rfc4533_sync_intermediate;
826 break;
827
831 break;
832
836 break;
837
838 default:
839 fr_assert(0);
840 }
841
844 if (!vp) return -1;
845 fr_pair_list_append_by_da(ctx, vp, &pairs, attr_ldap_sync_packet_id, (uint32_t)sync_no, false);
846 if (!vp) return -1;
847
848 if (config->root_dn) {
850 config->root_dn, strlen(config->root_dn), false);
851 if (!vp) return -1;
852 }
853
854 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
855
856 if (fr_internal_encode_list(dbuff, &pairs, &encode_ctx) < 0) return -1;
857
858 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li,
859 fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff),
860 fr_time(), NULL) < 0) return -1;
861 fr_pair_list_free(&pairs);
862 return 0;
863}
864
865/** Timer event to retry running "load Cookie" on failures
866 *
867 */
869{
870 proto_ldap_cookie_load_retry_ctx *retry_ctx = talloc_get_type_abort(uctx, proto_ldap_cookie_load_retry_ctx);
871
872 DEBUG2("Retrying \"load Cookie\" for sync no %ld", retry_ctx->sync_no);
873 if (proto_ldap_cookie_load_send(retry_ctx, retry_ctx->inst, retry_ctx->sync_no,
874 retry_ctx->thread) < 0) {
875 ERROR("Failed retrying \"load Cookie\". Will try again in %pV seconds",
877 (void) fr_timer_in(retry_ctx->thread->conn->h, tl,
878 &retry_ctx->inst->parent->sync_config[retry_ctx->sync_no]->ev,
880 false, proto_ldap_cookie_load_retry, retry_ctx);
881 return;
882 }
883 talloc_free(retry_ctx);
884}
885
886/** LDAP sync mod_write for child listener
887 *
888 * Handle any returned data after the worker has processed the packet and,
889 * for packets where tracking structures were used, ensure they are freed.
890 */
891static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
892 uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
893{
896 fr_dbuff_t dbuff;
898 uint32_t packet_id;
899 fr_pair_list_t tmp;
900 fr_pair_t *vp = NULL;
901 ssize_t ret;
902 TALLOC_CTX *local;
903 sync_packet_ctx_t *sync_packet_ctx = NULL;
904
905 local = talloc_new(NULL);
906 fr_dbuff_init(&dbuff, buffer, buffer_len);
907
908 if (packet_ctx) sync_packet_ctx = talloc_get_type_abort(packet_ctx, sync_packet_ctx_t);
909
910 /*
911 * Extract returned attributes into a temporary list
912 */
913 fr_pair_list_init(&tmp);
914
915 ret = fr_internal_decode_list_dbuff(local, &tmp, fr_dict_root(dict_ldap_sync), &dbuff, NULL);
916 if (ret < 0) goto finish;
917
918 /*
919 * There should always be a packet ID and code
920 */
922 fr_assert(vp);
923 packet_id = vp->vp_uint32;
924
926 fr_assert(vp);
927 pcode = vp->vp_uint32;
928
929 switch (pcode) {
931 {
932 uint8_t *cookie = NULL;
933
934 /*
935 * If the received packet ID is greater than the number of syncs
936 * we have then something very bad has happened
937 */
938 fr_assert (packet_id < talloc_array_length(inst->parent->sync_config));
939
940 /*
941 * Look for the returned cookie.
942 */
944 if (vp) cookie = talloc_memdup(inst, vp->vp_octets, vp->vp_length);
945
946 if (inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent, cookie) < 0) {
947 ret = -1;
948 goto finish;
949 }
950 }
951 break;
952
954 break;
955
957 {
958 sync_config_t const *sync_config;
959
960 if (!sync_packet_ctx || !sync_packet_ctx->refresh) break;
961
962 /*
963 * Abandon the old sync and start a new one with the relevant cookie.
964 */
965 sync_config = sync_packet_ctx->sync->config;
966 DEBUG3("Restarting sync with base %s", sync_config->base_dn);
967 talloc_free(sync_packet_ctx->sync);
968 if (inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent,
969 sync_packet_ctx->cookie) < 0) {
970 ret = -1;
971 goto finish;
972 }
973 }
974 break;
975
977 {
979
980 ERROR("Load Cookie failed for sync %d, retrying in %pV seconds", packet_id,
981 fr_box_time_delta(inst->handle_config.reconnection_delay));
982
983 MEM(retry_ctx = talloc(thread, proto_ldap_cookie_load_retry_ctx));
985 .thread = thread,
986 .inst = inst,
987 .sync_no = packet_id,
988 };
989
990 (void) fr_timer_in(thread->conn->h, thread->el->tl, &inst->parent->sync_config[packet_id]->ev,
991 inst->handle_config.reconnection_delay,
992 false, proto_ldap_cookie_load_retry, retry_ctx);
993 }
994 break;
995
996 default:
997 ERROR("Invalid packet type returned %d", pcode);
998 break;
999 }
1000
1001 if (sync_packet_ctx) {
1002 sync_state_t *sync = sync_packet_ctx->sync;
1004 proto_ldap_sync_t *ldap_sync = inst->parent;
1005
1006 sync_packet_ctx->status = SYNC_PACKET_COMPLETE;
1007
1008 /*
1009 * A cookie has been stored, reset the counter of changes
1010 */
1011 if (sync_packet_ctx->type == SYNC_PACKET_TYPE_COOKIE) sync->changes_since_cookie = 0;
1012
1013 /*
1014 * Pop any processed updates from the head of the list
1015 */
1016 while ((pc = fr_dlist_head(&sync->pending))) {
1017 /*
1018 * If the head entry in the list is a pending cookie but we have
1019 * not processed enough entries and there are more pending
1020 * cookies, mark this one as processed.
1021 */
1022 if ((pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
1023 (sync->changes_since_cookie < ldap_sync->cookie_changes) &&
1024 (sync->pending_cookies > 1)) pc->status = SYNC_PACKET_COMPLETE;
1025
1026 if (pc->status != SYNC_PACKET_COMPLETE) break;
1027
1028 /*
1029 * Update counters depending on entry type
1030 */
1031 if (pc->type == SYNC_PACKET_TYPE_COOKIE) {
1032 sync->pending_cookies--;
1033 } else {
1034 sync->changes_since_cookie++;
1035 }
1036 pc = fr_dlist_pop_head(&sync->pending);
1037 talloc_free(pc);
1038 }
1039
1040 /*
1041 * If the head of the list is a cookie which has not yet
1042 * been processed and sufficient changes have been recorded
1043 * send the cookie.
1044 */
1045 if (pc && (pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
1047 }
1048
1049finish:
1050 fr_pair_list_free(&tmp);
1051 talloc_free(local);
1052
1053 return ret;
1054}
1055
1056/** Callback for socket errors when running initial root query
1057 */
1059 UNUSED int fd_errno, void *uctx)
1060{
1061 proto_ldap_dir_ctx *dir_ctx = talloc_get_type_abort(uctx, proto_ldap_dir_ctx);
1062 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(dir_ctx->conn->h, fr_ldap_connection_t);
1063
1064 talloc_free(dir_ctx);
1065 fr_ldap_state_error(ldap_conn);
1066}
1067
1068/** Callback to process results of initial root query, identifying directory type
1069 */
1070static void _proto_ldap_socket_open_read(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1071{
1072 proto_ldap_dir_ctx *dir_ctx = talloc_get_type_abort(uctx, proto_ldap_dir_ctx);
1073 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(dir_ctx->conn->h, fr_ldap_connection_t);
1076 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(dir_ctx->main_listen->thread_instance,
1078 fr_ldap_rcode_t status;
1079 LDAPMessage *result;
1080
1081 size_t i;
1082 TALLOC_CTX *local = NULL;
1083
1084 /*
1085 * Fetch the result. Setting the timeout to 0 here means use
1086 * res_timeout from the configuration.
1087 */
1088 status = fr_ldap_result(&result, NULL, ldap_conn, dir_ctx->msgid, LDAP_MSG_ALL, NULL, fr_time_delta_from_msec(0));
1089 if (status != LDAP_PROC_SUCCESS) {
1090 PERROR("Failed querying for directory type");
1091 if (result) ldap_msgfree(result);
1092 error:
1093 talloc_free(dir_ctx);
1094 if (local) talloc_free(local);
1096 return;
1097 }
1098
1099 fr_ldap_directory_result_parse(ldap_conn->directory, ldap_conn->handle, result, ldap_conn->config->name);
1100 ldap_msgfree(result);
1101
1102 /*
1103 * If the server does not support any of the relevant controls, we just
1104 * tidy up - no point in signalling to reconnect.
1105 */
1106 if (ldap_conn->directory->sync_type == FR_LDAP_SYNC_NONE) {
1107 ERROR("LDAP sync configured for directory which does not support any suitable control");
1108 talloc_free(dir_ctx);
1109 connection_signal_halt(ldap_conn->conn);
1110 return;
1111 }
1112
1113 /*
1114 * We've done all the preparation work on the LDAP connection, now
1115 * use normal network event listeners.
1116 */
1118 if (unlikely(fr_network_listen_add(thread->nr, thread->li) < 0)) {
1119 PERROR("Failed adding listener");
1120 goto error; /* retry? */
1121 }
1122
1123 DEBUG2("Starting sync(s)");
1124
1125 local = talloc_new(NULL);
1126
1127 /*
1128 * Sync operations start by sending a fake packet to run
1129 * the load Cookie section in order to retrieve the cookie
1130 */
1131 for (i = 0; i < talloc_array_length(inst->parent->sync_config); i++) {
1132 if (proto_ldap_cookie_load_send(local, inst, i, thread) < 0) goto error;
1133 }
1134
1135 talloc_free(dir_ctx);
1136 talloc_free(local);
1137}
1138
1139/** Allocate a child listener
1140 *
1141 * Called as a watch function when the LDAP connection enters the INIT state
1142 */
1144 UNUSED connection_state_t state, void *uctx)
1145{
1146 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(uctx, proto_ldap_sync_ldap_thread_t);
1147 fr_listen_t *li;
1148
1149 MEM(li = talloc_zero(conn, fr_listen_t));
1150
1151 thread->li = li;
1152 li->thread_instance = thread;
1153
1154 li->cs = thread->parent->cs;
1156 li->name = li->app_io->common.name;
1158
1159 /*
1160 * Use the app from the parent listener to access
1161 * the encoder / decoder functions
1162 */
1163 li->app = thread->parent->app;
1164 li->app_instance = thread->parent->app_instance;
1165 li->server_cs = thread->inst->parent->server_cs;
1166}
1167
1168/** Callback for closure of LDAP connection
1169 *
1170 * Schedules re-start of the connection if appropriate
1171 */
1173 UNUSED connection_state_t state, void *uctx)
1174{
1175 fr_listen_t *listen = talloc_get_type_abort(uctx, fr_listen_t);
1176 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(listen->thread_instance, proto_ldap_sync_ldap_thread_t);
1177 proto_ldap_sync_ldap_t const *inst = thread->inst;
1178
1179 if (fr_event_loop_exiting(thread->el)) return;
1180
1181 if (prev == CONNECTION_STATE_CONNECTED) {
1182 ERROR("LDAP connection closed. Scheduling restart in %pVs",
1183 fr_box_time_delta(inst->handle_config.reconnection_delay));
1184 if (fr_timer_in(thread, thread->el->tl, &thread->conn_retry_ev,
1185 inst->handle_config.reconnection_delay,
1186 false, proto_ldap_connection_init, listen) < 0) {
1187 FATAL("Failed inserting event: %s", fr_strerror());
1188 }
1189 }
1190}
1191
1192/** Query an LDAP server to establish its type
1193 *
1194 * Called as a watch function once the LDAP connection enters the CONNECTED state
1195 *
1196 * There are three different forms of LDAP sync/persistent search - so we need
1197 * to know what we're dealing with, and whether the relevant options have been enabled.
1198 */
1200 UNUSED connection_state_t state, void *uctx)
1201{
1202 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(uctx, proto_ldap_sync_ldap_thread_t);
1203 fr_listen_t *listen = talloc_get_type_abort(thread->parent, fr_listen_t);
1206 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1207
1208 proto_ldap_dir_ctx *dir_ctx;
1209
1210 if (ldap_conn->fd < 0) {
1211 connection_failed:
1212 if (fr_timer_in(thread, thread->el->tl, &thread->conn_retry_ev,
1213 inst->handle_config.reconnection_delay,
1214 false, proto_ldap_connection_init, listen) < 0) {
1215 FATAL("Failed inserting event: %s", fr_strerror());
1216 }
1217 return;
1218 }
1219
1220 thread->li->fd = ldap_conn->fd;
1221
1222 MEM(dir_ctx = talloc_zero(inst, proto_ldap_dir_ctx));
1223 if (!dir_ctx) goto connection_failed;
1224
1225 dir_ctx->main_listen = listen;
1226 dir_ctx->conn = conn;
1227 dir_ctx->child_listen = thread->li;
1228
1229#ifdef SO_RCVBUF
1230 if (inst->recv_buff_is_set) {
1231 int opt;
1232
1233 opt = inst->recv_buff;
1234 if (setsockopt(ldap_conn->fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
1235 WARN("Failed setting 'recv_buff': %s", fr_syserror(errno));
1236 }
1237 }
1238#endif
1239
1240 /*
1241 * Set the callback which will handle the results of this query
1242 */
1243 if (fr_event_fd_insert(conn, NULL, conn->el, ldap_conn->fd,
1245 NULL,
1247 dir_ctx) < 0) {
1248 goto connection_failed;
1249 }
1250
1251 /*
1252 * Allocate the directory structure and send the query
1253 */
1254 dir_ctx->msgid = fr_ldap_conn_directory_alloc_async(ldap_conn);
1255 if (dir_ctx->msgid < 0) {
1256 talloc_free(dir_ctx);
1257 goto connection_failed;
1258 }
1259
1260 /*
1261 * Add a watch to catch closed LDAP connections
1262 */
1264 _proto_ldap_socket_closed, true, listen);
1265}
1266
1267/** Callback triggered when parent listener app_io has its event list set
1268 *
1269 * Initiates the actual outbound LDAP connection
1270 *
1271 * @param[in] li The parent listener.
1272 * @param[in] el Event list for this listener.
1273 * @param[in] nr Network handler.
1274 */
1276{
1279
1280 /*
1281 * Set up thread data
1282 */
1283 thread->name = inst->handle_config.name;
1284 thread->parent = li;
1285 thread->el = el;
1286 thread->nr = nr;
1287 thread->inst = inst;
1288
1289 /*
1290 * Initialise the connection
1291 */
1293}
1294
1295static int mod_instantiate(module_inst_ctx_t const *mctx)
1296{
1297 proto_ldap_sync_ldap_t *inst = talloc_get_type_abort(mctx->mi->data, proto_ldap_sync_ldap_t);
1298 CONF_SECTION *conf = mctx->mi->conf;
1299 char const *server;
1300
1301 /*
1302 * Verify that the LDAP server configuration is valid, either
1303 * distinct server and port or an LDAP url.
1304 */
1305 fr_assert(inst->server);
1306
1307 inst->parent = talloc_get_type_abort(mctx->mi->parent->data, proto_ldap_sync_t);
1308 inst->cs = conf;
1309
1310 if (inst->recv_buff_is_set) {
1311 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32);
1312 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX);
1313 }
1314
1315 server = inst->server;
1316 inst->handle_config.server = talloc_strdup(inst, "");
1317
1318 if (ldap_is_ldap_url(server)) {
1319 if (fr_ldap_server_url_check(&inst->handle_config, server, conf) < 0) return -1;
1320 } else {
1321 if (fr_ldap_server_config_check(&inst->handle_config, server, conf) < 0) return -1;
1322 }
1323
1324 inst->handle_config.server[talloc_strlen(inst->handle_config.server)] = '\0';
1325
1326 inst->handle_config.name = talloc_typed_asprintf(inst, "proto_ldap_conn (%s)",
1328
1329 return 0;
1330}
1331
1333 .common = {
1334 .magic = MODULE_MAGIC_INIT,
1335 .name = "ldap_sync_child"
1336 },
1340
1341 .default_message_size = 4096,
1342 .track_duplicates = false,
1343};
1344
1346 .common = {
1347 .magic = MODULE_MAGIC_INIT,
1348 .name = "ldap_sync_ldap",
1350 .inst_size = sizeof(proto_ldap_sync_ldap_t),
1351 .thread_inst_size = sizeof(proto_ldap_sync_ldap_thread_t),
1352 .instantiate = mod_instantiate
1353 },
1354
1355 .default_message_size = 4096,
1356 .track_duplicates = false,
1357
1358 .event_list_set = mod_event_list_set,
1359};
int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie)
Allocate a sync state structure and issue the search.
int active_directory_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
Handle a LDAP_RES_SEARCH_ENTRY (SearchResultEntry) response.
static int const char char buffer[256]
Definition acutest.h:576
log_entry msg
Definition acutest.h:794
module_t common
Common fields to all loadable modules.
Definition app_io.h:34
size_t default_message_size
Usually maximum message size.
Definition app_io.h:39
Public structure describing an I/O path for a protocol.
Definition app_io.h:33
#define USES_APPLE_DEPRECATED_API
Definition build.h:499
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:529
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition cf_parse.h:294
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:429
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
char const * cf_section_name(CONF_SECTION const *cs)
Return name2 if set, else name1.
Definition cf_util.c:1371
#define cf_parent(_cf)
Definition cf_util.h:118
static int max_outstanding
connection_state_t
Definition connection.h:47
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:57
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:54
@ CONNECTION_STATE_INIT
Init state, sets up connection.
Definition connection.h:51
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:85
Holds a complete set of functions for a connection.
Definition connection.h:195
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:775
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:362
#define fr_dbuff_buff(_dbuff_or_marker)
Return the underlying buffer in a dbuff or one of marker.
Definition dbuff.h:890
#define FR_DBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Create a function local and thread local extensible dbuff.
Definition dbuff.h:564
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2639
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
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:360
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
talloc_free(hp)
bool allow_name_only
Allow name only pairs.
Definition internal.h:36
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2446
CONF_SECTION * cs
of this listener
Definition listen.h:41
char const * name
printable name for this socket - set by open
Definition listen.h:29
void const * app_instance
Definition listen.h:39
size_t default_message_size
copied from app_io, but may be changed
Definition listen.h:56
fr_app_t const * app
Definition listen.h:38
void const * app_io_instance
I/O path configuration context.
Definition listen.h:33
CONF_SECTION * server_cs
CONF_SECTION of the server.
Definition listen.h:42
void * thread_instance
thread / socket context
Definition listen.h:34
int fd
file descriptor for this socket - set by open
Definition listen.h:28
fr_app_io_t const * app_io
I/O path functions.
Definition listen.h:32
int fr_network_listen_send_packet(fr_network_t *nr, fr_listen_t *parent, fr_listen_t *li, const uint8_t *buffer, size_t buflen, fr_time_t recv_time, void *packet_ctx)
Send a packet to the worker.
Definition network.c:798
int fr_network_listen_add(fr_network_t *nr, fr_listen_t *li)
Add a fr_listen_t to a network.
Definition network.c:231
ssize_t fr_network_listen_outstanding(fr_network_t *nr, fr_listen_t *li)
Get the number of outstanding packets.
Definition network.c:840
int fr_ldap_conn_directory_alloc_async(fr_ldap_connection_t *ldap_conn)
Asynchronously extract useful information from the rootDSE of the LDAP server.
Definition directory.c:442
fr_ldap_sync_type_t sync_type
What kind of LDAP sync this directory supports.
Definition base.h:212
LDAP * handle
libldap handle.
Definition base.h:343
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:352
int fd
File descriptor for this connection.
Definition base.h:359
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition state.c:153
int fr_ldap_server_url_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION const *cs)
Check an LDAP server entry in URL format is valid.
Definition util.c:1036
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:354
int fr_ldap_server_config_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION *cs)
Check an LDAP server config in server:port format is valid.
Definition util.c:1133
char const * name
Name of the module that created this connection.
Definition base.h:232
fr_time_delta_t reconnection_delay
How long to wait before attempting to reconnect.
Definition base.h:321
int fr_ldap_directory_result_parse(fr_ldap_directory_t *directory, LDAP *handle, LDAPMessage *result, char const *name)
Definition directory.c:68
void * uctx
User data associated with the handle.
Definition base.h:364
@ FR_LDAP_SYNC_NONE
No support for LDAP sync.
Definition base.h:158
@ FR_LDAP_SYNC_ACTIVE_DIRECTORY
Directory supports AD style persistent search.
Definition base.h:160
@ FR_LDAP_SYNC_PERSISTENT_SEARCH
Directory supports persistent search.
Definition base.h:161
@ FR_LDAP_SYNC_RFC4533
Directory supports RFC 4533.
Definition base.h:159
connection_t * conn
Connection state handle.
Definition base.h:355
char const ** naming_contexts
NULL terminated array of databases served by this directory.
Definition base.h:218
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:585
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:588
@ LDAP_PROC_BAD_CONN
Transitory error, caller should retry the operation with a new connection.
Definition base.h:592
@ LDAP_PROC_REFRESH_REQUIRED
Don't continue with the current refresh phase, exit, and retry the operation with a NULL cookie.
Definition base.h:607
Tracks the state of a libldap connection handle.
Definition base.h:342
#define FR_LDAP_COMMON_CONF(_conf)
Definition conf.h:19
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
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1130
global_lib_autoinst_t fr_libldap_global_config
Definition base.c:134
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:450
#define PERROR(_fmt,...)
Definition log.h:228
#define FATAL(_fmt,...)
Definition log.h:224
#define DEBUG3(_fmt,...)
Definition log.h:266
fr_time_t fr_event_list_time(fr_event_list_t *el)
Get the current server time according to the event list.
Definition event.c:590
#define fr_time()
Definition event.c:60
bool fr_event_loop_exiting(fr_event_list_t *el)
Check to see whether the event loop is in the process of exiting.
Definition event.c:2376
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:1203
Stores all information relating to an event list.
Definition event.c:377
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2326
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition pair.c:784
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
int persistent_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle a SearchResultEntry response from Persistent Search LDAP servers.
int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie)
Allocate and initialise sync queries for persistent searches.
static const conf_parser_t config[]
Definition base.c:163
char const * filter
Filter to retrieve only user objects.
CONF_SECTION * server_cs
server CS for this listener.
fr_timer_t * ev
Event for retrying cookie load.
sync_config_t ** sync_config
DNs and filters to monitor.
int(* sync_msg_t)(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Received an LDAP message related to a sync.
sync_op_t
Operations to perform on entries.
@ SYNC_OP_ADD
Entry should be added to our copy.
@ SYNC_OP_MODIFY
Entry should be updated in our copy.
@ SYNC_OP_DELETE
Entry should be deleted from our copy.
@ SYNC_OP_PRESENT
Entry is present and unchanged on the server.
char const * base_dn
DN to search for users under.
sync_msg_t entry
Called when we receive a searchEntry message.
sync_msg_t refresh
Called when we receive a eSyncRefreshRequired code.
CONF_SECTION * cs
Config section where this sync was defined.
sync_msg_t intermediate
Called when we receive a syncIntermediate message.
map_list_t entry_map
How to convert attributes in entries to FreeRADIUS attributes.
fr_pair_list_t sync_pairs
Pairs representing the sync config sent to the worker with each request.
fr_time_delta_t cookie_interval
Interval between storing cookies.
uint32_t cookie_changes
Number of LDAP changes to process between each cookie store operation.
fr_time_delta_t retry_interval
Interval between retrying failed change packets.
void * user_ctx
User ctx to pass to the callbacks.
An instance of a proto_ldap_sync listen section.
Areas of the directory to receive notifications for.
int ldap_sync_cookie_send(sync_packet_ctx_t *sync_packet_ctx)
Enqueue a new cookie store packet.
fr_app_io_t proto_ldap_sync_ldap
static void _proto_ldap_socket_closed(UNUSED connection_t *conn, connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Callback for closure of LDAP connection.
static fr_dict_attr_t const * attr_packet_type
static fr_ldap_sync_packet_code_t const sync_packet_code_table[4]
static int proto_ldap_child_mod_close(fr_listen_t *li)
Child listener mod_close.
static int proto_ldap_cookie_load_send(TALLOC_CTX *ctx, proto_ldap_sync_ldap_t const *inst, size_t sync_no, proto_ldap_sync_ldap_thread_t *thread)
Send a fake packet to run the "load Cookie" section.
global_lib_autoinst_t const * proto_ldap_sync_ldap_lib[]
static fr_dict_attr_t const * attr_ldap_sync_packet_id
static void ldap_sync_retry_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Event to handle sending of any change packets which failed to send.
static fr_dict_t const * dict_ldap_sync
proto_ldap_sync_ldap_t const * inst
static fr_dict_attr_t const * attr_ldap_sync_entry_dn
int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
Add a new cookie packet ctx to the pending list.
static int sync_state_free(sync_state_t *sync)
Tell the remote server to stop the sync.
static fr_dict_t const * dict_freeradius
static fr_dict_attr_t const * attr_ldap_sync_base_dn
void ldap_sync_cookie_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Event to handle storing of cookies on a timed basis.
static fr_dict_attr_t const * attr_ldap_sync_orig_dn
static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, void *nr)
Callback triggered when parent listener app_io has its event list set.
fr_app_io_t proto_ldap_sync_child
static conf_parser_t const proto_ldap_sync_ldap_config[]
static void _proto_ldap_socket_open_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED int fd_errno, void *uctx)
Callback for socket errors when running initial root query.
int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH], struct berval *orig_dn, LDAPMessage *msg, sync_op_t op)
Enqueue a new entry change packet.
static ssize_t proto_ldap_child_mod_read(fr_listen_t *li, UNUSED void **packet_ctx, UNUSED fr_time_t *recv_time_p, UNUSED uint8_t *buffer, UNUSED size_t buffer_len, UNUSED size_t *leftover)
LDAP sync mod_read for child listener.
size_t sync_op_table_len
fr_table_num_sorted_t const sync_op_table[]
Operations performed on entries.
static fr_dict_attr_t const * attr_ldap_sync_root_dn
static fr_internal_encode_ctx_t encode_ctx
static int ldap_sync_entry_send_network(sync_packet_ctx_t *sync_packet_ctx)
Send a change packet to the workers.
int8_t sync_state_cmp(void const *one, void const *two)
Compare two sync state structures on msgid.
static void proto_ldap_connection_init(fr_timer_list_t *tl, UNUSED fr_time_t now, void *user_ctx)
Attempt to (re)initialise a connection.
static fr_dict_attr_t const * attr_ldap_sync_entry_uuid
static void _proto_ldap_socket_init(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Allocate a child listener.
sync_state_t * sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, proto_ldap_sync_t const *inst, size_t sync_no, sync_config_t const *config)
Allocate a sync state.
fr_dict_attr_autoload_t proto_ldap_sync_ldap_dict_attr[]
static void proto_ldap_cookie_load_retry(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Timer event to retry running "load Cookie" on failures.
static fr_dict_attr_t const * attr_ldap_sync_cookie
proto_ldap_sync_ldap_thread_t * thread
fr_dict_autoload_t proto_ldap_sync_ldap_dict[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
static void _proto_ldap_socket_open_read(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Callback to process results of initial root query, identifying directory type.
static void _proto_ldap_socket_open_connected(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Query an LDAP server to establish its type.
static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
LDAP sync mod_write for child listener.
Context used when looking up Directory types.
uint32_t pending_cookies
How many cookies are in the pending heap.
uint8_t * cookie
Opaque cookie, used to resume synchronisation.
size_t sync_no
Array position of config for this sync.
@ SYNC_PACKET_TYPE_CHANGE
Packet is an entry change.
@ SYNC_PACKET_TYPE_COOKIE
sync_phases_t phase
Phase this sync is in.
uint32_t max_outstanding
Maximum number of outstanding packets.
int msgid
The unique identifier for this sync session.
uint8_t * cookie
Cookie to store - can be NULL.
fr_timer_t * retry_ev
Timer event for retrying failed changes.
fr_pair_list_t pairs
Pairs to send with change packets.
fr_dlist_head_t pending
List of pending changes in progress.
sync_config_t const * config
Configuration for this sync.
static fr_table_num_sorted_t const sync_ldap_msg_table[]
Types of LDAP messages relevant to LDAP sync.
sync_state_t * sync
Sync packet relates to.
sync_packet_status_t status
Status of this packet.
@ SYNC_PACKET_PREPARING
Packet being prepared.
@ SYNC_PACKET_PENDING
Packet not yet sent.
@ SYNC_PACKET_PROCESSING
Packet sent to worker.
@ SYNC_PACKET_COMPLETE
Packet response received from worker.
fr_listen_t * li
Our listener.
fr_timer_t * cookie_ev
Timer event for sending cookies.
connection_t * conn
Our connection to the LDAP directory.
proto_ldap_sync_t const * inst
Module instance for this sync.
fr_ldap_config_t handle_config
Connection configuration instance.
fr_network_t * nr
Network handler.
fr_listen_t * parent
master IO handler.
#define SYNC_UUID_LENGTH
proto_ldap_sync_t * parent
The module that spawned us.
bool refresh
Does the sync require a refresh.
@ SYNC_PHASE_INIT
We haven't entered any of the refresh phases.
sync_packet_type_t type
Type of packet.
uint32_t changes_since_cookie
How many changes have been added since the last cookie was stored.
fr_ldap_connection_t * conn
Connection the sync is running on.
fr_pair_list_t trigger_args
Arguments to make available in triggers.
fr_event_list_t * el
Network side event list.
fr_timer_t * conn_retry_ev
When to retry re-establishing the conn.
proto_ldap_sync_ldap_t const * inst
instance data
Tracking structure for ldap sync packets.
State of an individual sync.
ssize_t fr_internal_decode_list_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *dbuff, void *decode_ctx)
Retrieve all pairs from the dbuff.
Definition decode.c:314
ssize_t fr_internal_encode_list(fr_dbuff_t *dbuff, fr_pair_list_t const *list, void *encode_ctx)
Encode a list of pairs using the internal encoder.
Definition encode.c:304
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
static rs_t * conf
Definition radsniff.c:52
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:577
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:741
The main red black tree structure.
Definition rb.h:71
int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle result code of e-syncRefreshRequired.
Definition rfc4533.c:717
int rfc4533_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle a SearchResultEntry or SearchResultReference response from an RFC 4533 server.
Definition rfc4533.c:260
int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, uint8_t const *cookie)
Allocate and initialise RFC 4533 sync queries.
Definition rfc4533.c:74
int rfc4533_sync_intermediate(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
Handle a LDAP_RES_INTERMEDIATE (SyncInfo) response.
Definition rfc4533.c:452
void connection_signal_shutdown(connection_t *conn)
Shuts down a connection gracefully.
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
connection_watch_entry_t * connection_add_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed after a state function has been called.
Definition connection.c:543
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
void * data
Module's instance data.
Definition module.h:293
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:359
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
int pair_append_by_tmpl_parent(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, tmpl_t const *vpt, bool skip_list))
Allocate and insert a leaf vp from a tmpl_t, building the parent vps if needed.
Definition tmpl_eval.c:862
return count
Definition module.c:155
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_ldap_sync_packet_code_t
Types of the internal packets for processing LDAP sync messages.
Definition sync.h:31
@ FR_LDAP_SYNC_CODE_PRESENT
LDAP server indicates a particular object is present and unchanged.
Definition sync.h:33
@ FR_LDAP_SYNC_CODE_COOKIE_STORE_RESPONSE
Response to storing the new cookie.
Definition sync.h:52
@ FR_LDAP_SYNC_CODE_ENTRY_RESPONSE
Response packet to present / add / modify / delete.
Definition sync.h:42
@ FR_LDAP_SYNC_CODE_COOKIE_LOAD_FAIL
Response when coolie load fails.
Definition sync.h:48
@ FR_LDAP_SYNC_CODE_ADD
Object has been added to the LDAP directory.
Definition sync.h:36
@ FR_LDAP_SYNC_CODE_COOKIE_STORE
The server has sent a new cookie.
Definition sync.h:50
@ FR_LDAP_SYNC_CODE_COOKIE_LOAD_RESPONSE
Response with the returned cookie.
Definition sync.h:46
@ FR_LDAP_SYNC_CODE_DELETE
Object has been deleted.
Definition sync.h:40
@ FR_LDAP_SYNC_CODE_COOKIE_LOAD
Before the sync starts, request any previously stored cookie.
Definition sync.h:44
@ FR_LDAP_SYNC_CODE_MODIFY
Object has been modified.
Definition sync.h:38
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:546
#define talloc_get_type_abort_const
Definition talloc.h:117
static size_t talloc_str_array_len(char const *const *strings)
Return the number of strings a NULL terminated string array was sized for.
Definition talloc.h:279
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#define fr_timer_in(...)
Definition timer.h:87
@ T_OP_ADD_EQ
Definition token.h:67
int trigger(unlang_interpret_t *intp, CONF_SECTION const *cs, CONF_PAIR **trigger_cp, char const *name, bool rate_limit, fr_pair_list_t *args)
Execute a trigger - call an executable to process an event.
Definition trigger.c:155
static fr_event_list_t * el
#define fr_pair_list_append_by_da_len(_ctx, _vp, _list, _attr, _val, _len, _tainted)
Append a pair to a list, assigning its value.
Definition pair.h:327
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define fr_pair_list_append_by_da(_ctx, _vp, _list, _attr, _val, _tainted)
Append a pair to a list, assigning its value.
Definition pair.h:304
#define fr_pair_list_append_by_da_parent_len(_ctx, _vp, _list, _attr, _val, _len, _tainted)
Definition pair.h:349
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6068
#define fr_box_time_delta(_val)
Definition value.h:366