The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
pair.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library 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 GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** AVP manipulation and search API
18 *
19 * @file src/lib/util/pair.c
20 *
21 * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 * @copyright 2000,2006,2015,2020 The FreeRADIUS server project
23 */
24RCSID("$Id: b1f9db252bc9b16d899870762def0fae059e89a6 $")
25
26#define _PAIR_PRIVATE 1
27#define _PAIR_INLINE 1
28
29#include <freeradius-devel/util/debug.h>
30#include <freeradius-devel/util/misc.h>
31#include <freeradius-devel/util/pair.h>
32#include <freeradius-devel/util/pair_legacy.h>
33#include <freeradius-devel/util/proto.h>
34#include <freeradius-devel/util/regex.h>
35
36FR_TLIST_FUNCS(fr_pair_order_list, fr_pair_t, order_entry)
37FR_TLIST_PARENT_FUNCS(fr_pair_order_list, fr_pair_t, fr_pair_list_t, order)
38
39#include <freeradius-devel/util/pair_inline.c>
40
41/** Initialise a pair list header
42 *
43 * @param[in,out] list to initialise
44 *
45 * @hidecallergraph
46 */
48{
49 /*
50 * Initialises the order list. This
51 * maintains the overall order of attributes
52 * in the list and allows us to iterate over
53 * all of them.
54 */
55 fr_pair_order_list_talloc_init(&list->order);
56
57#ifdef WITH_VERIFY_PTR
58 list->verified = true;
59#endif
60 list->is_child = false;
61}
62
63/** Free a fr_pair_t
64 *
65 * @note Do not call directly, use talloc_free instead.
66 *
67 * @param vp to free.
68 * @return 0
69 */
71{
72#ifdef TALLOC_DEBUG
73 talloc_report_depth_cb(NULL, 0, -1, fr_talloc_verify_cb, NULL);
74#endif
75
76#if 0
77 /*
78 * We would like to enforce that a VP must be removed from a list before it's freed. However, we
79 * free pair_lists via talloc_free(). And the talloc code just frees things in (essentially) a
80 * random order. So this guarantee can't be enforced.
81 */
82 fr_assert(fr_pair_order_list_parent(vp) == NULL);
83#endif
84
85 /*
86 * Pairs with children have the children
87 * freed explicitly.
88 */
89 if (likely(vp->da != NULL)) switch (vp->vp_type) {
91 fr_pair_list_free(&vp->vp_group);
92 break;
93
94 case FR_TYPE_STRING:
95 case FR_TYPE_OCTETS:
96 fr_assert(!vp->vp_edit);
97 if (fr_value_box_is_secret(&vp->data)) memset_explicit(vp->vp_ptr, 0, vp->vp_length);
98 break;
99
100 default:
101 fr_assert(!vp->vp_edit);
102 if (fr_value_box_is_secret(&vp->data)) memset_explicit(&vp->data, 0, sizeof(vp->data));
103 break;
104 }
105
106#ifndef NDEBUG
107 memset(vp, 0, sizeof(*vp));
108#endif
109
110 return 0;
111}
112
113/** Allocate a new pair list on the heap
114 *
115 * @param[in] ctx to allocate the pair list in.
116 * @return
117 * - A new #fr_pair_list_t.
118 * - NULL if an error occurred.
119 */
121{
122 fr_pair_list_t *pl;
123
124 pl = talloc(ctx, fr_pair_list_t);
125 if (unlikely(!pl)) return NULL;
126
128
129 return pl;
130}
131
132/** Initialise fields in an fr_pair_t without assigning a da
133 *
134 * @note Internal use by the allocation functions only.
135 */
136static inline CC_HINT(always_inline) void pair_init_null(fr_pair_t *vp)
137{
138 fr_pair_order_list_entry_init(vp);
139
140 /*
141 * Legacy cruft
142 */
143 vp->op = T_OP_EQ;
144}
145
146/** Initialise fields in an fr_pair_t without assigning a da
147 *
148 * Used only for temporary value-pairs which are not placed in any list.
149 */
151{
152 memset(vp, 0, sizeof(*vp));
153
155}
156
157/** Dynamically allocate a new attribute with no #fr_dict_attr_t assigned
158 *
159 * This is not the function you're looking for (unless you're binding
160 * unknown attributes to pairs, and need to pre-allocate the memory).
161 * You probably want #fr_pair_afrom_da instead.
162 *
163 * @note You must assign a #fr_dict_attr_t before freeing this #fr_pair_t.
164 *
165 * @param[in] ctx to allocate the pair list in.
166 * @return
167 * - A new #fr_pair_t.
168 * - NULL if an error occurred.
169 */
171{
172 fr_pair_t *vp;
173
174 vp = talloc_zero(ctx, fr_pair_t);
175 if (!vp) {
176 fr_strerror_printf("Out of memory");
177 return NULL;
178 }
179 talloc_set_destructor(vp, _fr_pair_free);
180
183
184 return vp;
185}
186
187/** Continue initialising an fr_pair_t assigning a da
188 *
189 * @note Internal use by the pair allocation functions only.
190 */
191static inline CC_HINT(always_inline) void pair_init_from_da(fr_pair_t *vp, fr_dict_attr_t const *da)
192{
193 /*
194 * Use the 'da' to initialize more fields.
195 */
196 vp->da = da;
197
198 if (likely(fr_type_is_leaf(da->type))) {
199 fr_value_box_init(&vp->data, da->type, da, false);
200 } else {
201#ifndef NDEBUG
202 /*
203 * Make it very obvious if we failed
204 * to initialise something.
205 * Given the definition of fr_value_box_t, this entails
206 * writing const-qualified fields. The compiler allows it,
207 * but Coverity points it out as a defect, so it is annotated.
208 */
209 /* coverity[store_writes_const_field] */
210 memset(&vp->data, 0xff, sizeof(vp->data));
211#endif
212
214
215 /*
216 * Make sure that the pad field is initialized.
217 */
218 if (sizeof(vp->pad)) memset(vp->pad, 0, sizeof(vp->pad));
219
220 /*
221 * Hack around const issues...
222 * Here again, the workaround suffices for the compiler but
223 * not for Coverity, so again we annotate.
224 */
225 /* coverity[store_writes_const_field] */
226 memcpy(UNCONST(fr_type_t *, &vp->vp_type), &da->type, sizeof(vp->vp_type));
227 fr_pair_list_init(&vp->vp_group);
228 vp->vp_group.is_child = true;
229 fr_pair_order_list_talloc_init_children(vp, &vp->vp_group.order);
230 }
231}
232
233/** A special allocation function which disables child autofree
234 *
235 * This is intended to allocate root attributes for requests.
236 * These roots are special in that they do not necessarily own
237 * the child attributes and _MUST NOT_ free them when they
238 * themselves are freed. The children are allocated in special
239 * ctxs which may be moved between session state entries and
240 * requests, or may belong to a parent request.
241 *
242 * @param[in] ctx to allocate the pair root in.
243 * @param[in] da The root attribute.
244 * @return
245 * - A new root pair on success.
246 * - NULL on failure.
247 * @hidecallergraph
248 */
250{
251 fr_pair_t *vp;
252
253#ifndef NDEBUG
254 if (da->type != FR_TYPE_GROUP) {
255 fr_strerror_const("Root must be a group type");
256 return NULL;
257 }
258#endif
259
260 vp = talloc_zero(ctx, fr_pair_t);
261 if (unlikely(!vp)) {
262 fr_strerror_const("Out of memory");
263 return NULL;
264 }
265
266 if (unlikely(da->flags.is_unknown)) {
267 fr_strerror_const("Root attribute cannot be unknown");
269 return NULL;
270 }
271
274
275 return vp;
276}
277
278/** Dynamically allocate a new attribute and assign a #fr_dict_attr_t
279 *
280 * @note Will duplicate any unknown attributes passed as the da.
281 *
282 * @param[in] ctx for allocated memory, usually a pointer to a #fr_packet_t
283 * @param[in] da Specifies the dictionary attribute to build the #fr_pair_t from.
284 * If unknown, will be duplicated, with the memory being bound to
285 * the pair.
286 * @return
287 * - A new #fr_pair_t.
288 * - NULL if an error occurred.
289 * @hidecallergraph
290 */
291fr_pair_t *fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
292{
293 fr_pair_t *vp;
294
295 vp = fr_pair_alloc_null(ctx);
296 if (!vp) {
297 oom:
298 fr_strerror_const("Out of memory");
299 return NULL;
300 }
301
302 /*
303 * If we get passed an unknown da, we need to ensure that
304 * it's parented by "vp".
305 */
306 if (da->flags.is_unknown) {
307 fr_dict_attr_t const *unknown;
308
309 unknown = fr_dict_attr_unknown_copy(vp, da);
310 if (!unknown) {
312 goto oom;
313 }
314 da = unknown;
315 }
316
319
320 return vp;
321}
322
323/** Re-initialise an attribute with a different da
324 *
325 * If the new da has a different type to the old da, we'll attempt to cast
326 * the current value in place.
327 */
329{
330 fr_dict_attr_t const *to_free;
331
332 /*
333 * vp may be created from fr_pair_alloc_null(), in which case it has no da.
334 */
335 if (vp->da && !vp->da->flags.is_raw) {
336 if (vp->da == da) return 0;
337
338 if (!fr_type_is_leaf(vp->vp_type)) return -1;
339
340 if ((da->type != vp->vp_type) && (fr_value_box_cast_in_place(vp, &vp->data, da->type, da) < 0)) return -1;
341 } else {
342 fr_assert(fr_type_is_leaf(vp->vp_type) || (fr_type_is_structural(vp->vp_type) && (fr_pair_list_num_elements(&vp->vp_group) == 0)));
343
344 fr_value_box_init(&vp->data, da->type, da, false);
345 }
346
347 to_free = vp->da;
348 vp->da = da;
349
350 /*
351 * Only frees unknown fr_dict_attr_t's
352 */
354
355 /*
356 * Ensure we update the attribute index in the parent.
357 */
358 if (list) {
359 fr_pair_remove(list, vp);
360
361 fr_pair_append(list, vp);
362 }
363
364 return 0;
365}
366
367/** Create a new valuepair
368 *
369 * If attr and vendor match a dictionary entry then a VP with that #fr_dict_attr_t
370 * will be returned.
371 *
372 * If attr or vendor are unknown will call dict_attruknown to create a dynamic
373 * #fr_dict_attr_t of #FR_TYPE_OCTETS.
374 *
375 * Which type of #fr_dict_attr_t the #fr_pair_t was created with can be determined by
376 * checking @verbatim vp->da->flags.is_unknown @endverbatim.
377 *
378 * @param[in] ctx for allocated memory, usually a pointer to a #fr_packet_t.
379 * @param[in] parent of the attribute being allocated (usually a dictionary or vendor).
380 * @param[in] attr number.
381 * @return
382 * - A new #fr_pair_t.
383 * - NULL on error.
384 */
385fr_pair_t *fr_pair_afrom_child_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
386{
387 fr_dict_attr_t const *da;
388 fr_pair_t *vp;
389
390 vp = fr_pair_alloc_null(ctx);
391 if (unlikely(!vp)) return NULL;
392
394 if (!da) {
395 fr_dict_attr_t *unknown;
396
398 if (!unknown) {
400 return NULL;
401 }
402 da = unknown;
403 }
404
407
408 return vp;
409}
410
411/** Create a pair (and all intermediate parents), and append it to the list
412 *
413 * Unlike fr_pair_afrom_da_nested(), this function starts off at an intermediate ctx and list.
414 *
415 * @param[in] ctx for allocated memory, usually a pointer to a #fr_packet_t.
416 * @param[out] list where the created pair is supposed to go.
417 * @param[in] da the da for the pair to create
418 * @param[in] start the starting depth. If start != 0, we must have ctx==vp at that depth, and list==&vp->vp_group
419 * @return
420 * - A new #fr_pair_t.
421 * - NULL on error.
422 */
423fr_pair_t *fr_pair_afrom_da_depth_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da, unsigned int start)
424{
425 fr_pair_t *vp;
426 unsigned int i;
427 TALLOC_CTX *cur_ctx;
428 fr_dict_attr_t const *find; /* DA currently being looked for */
429 fr_pair_list_t *cur_list; /* Current list being searched */
430 fr_da_stack_t da_stack;
431
432 /*
433 * Short-circuit the common case.
434 */
435 if (da->depth == (start + 1)) {
436 if (fr_pair_append_by_da(ctx, &vp, list, da) < 0) return NULL;
438 return vp;
439 }
440
441 fr_proto_da_stack_build(&da_stack, da);
442 cur_list = list;
443 cur_ctx = ctx;
444
445 for (i = start; i <= da->depth; i++) {
446 find = da_stack.da[i];
447
448 vp = fr_pair_find_by_da(cur_list, NULL, find);
449 if (!vp || (vp->da == da)) {
450 if (fr_pair_append_by_da(cur_ctx, &vp, cur_list, find) < 0) return NULL;
452 }
453
454 if (find == da) return vp;
455
457
458 cur_ctx = vp;
459 cur_list = &vp->vp_group;
460 }
461
462 fr_assert(0);
463
464 return NULL;
465}
466
467/** Create a pair (and all intermediate parents), and append it to the list
468 *
469 * If the relevant leaf pair already exists, then a new one is created.
470 *
471 * This function is similar to fr_pair_update_by_da_parent(), except that function requires
472 * a parent pair, and this one takes a separate talloc ctx and pair list.
473 *
474 * @param[in] ctx for allocated memory, usually a pointer to a #fr_packet_t.
475 * @param[out] list where the created pair is supposed to go.
476 * @param[in] da the da for the pair to create
477 * @return
478 * - A new #fr_pair_t.
479 * - NULL on error.
480 */
482{
483 if (da->depth <= 1) {
484 fr_pair_t *vp;
485
486 if (fr_pair_append_by_da(ctx, &vp, list, da) < 0) return NULL;
488 return vp;
489 }
490
491 return fr_pair_afrom_da_depth_nested(ctx, list, da, 0);
492}
493
494/** Copy a single valuepair
495 *
496 * Allocate a new valuepair and copy the da from the old vp.
497 *
498 * @param[in] ctx for talloc
499 * @param[in] vp to copy.
500 * @return
501 * - A copy of the input VP.
502 * - NULL on error.
503 */
504fr_pair_t *fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
505{
506 fr_pair_t *n;
507
509
510 n = fr_pair_afrom_da(ctx, vp->da);
511 if (!n) return NULL;
512
513 n->op = vp->op;
514
515 /*
516 * Groups are special.
517 */
518 if (fr_type_is_structural(n->vp_type)) {
519 if (fr_pair_list_copy(n, &n->vp_group, &vp->vp_group) < 0) {
520 error:
521 talloc_free(n);
522 return NULL;
523 }
524
525 } else {
526 if (unlikely(fr_value_box_copy(n, &n->data, &vp->data) < 0)) goto error;
527 }
529
530 return n;
531}
532
533/** Steal one VP
534 *
535 * @param[in] ctx to move fr_pair_t into
536 * @param[in] vp fr_pair_t to move into the new context.
537 */
538int fr_pair_steal(TALLOC_CTX *ctx, fr_pair_t *vp)
539{
540 fr_pair_t *nvp;
541
542 nvp = talloc_steal(ctx, vp);
543 if (unlikely(!nvp)) {
544 fr_strerror_printf("Failed moving pair %pV to new ctx", vp);
545 return -1;
546 }
547
548 return 0;
549}
550
551#define IN_A_LIST_MSG "Pair %pV is already in a list, and cannot be moved"
552#define NOT_IN_THIS_LIST_MSG "Pair %pV is not in the given list"
553
554/** Change a vp's talloc ctx and insert it into a new list
555 *
556 * @param[in] list_ctx to move vp into.
557 * @param[out] list to add vp to.
558 * @param[in] vp to move.
559 * @return
560 * - 0 on success.
561 * - -1 on failure (already in list).
562 */
563int fr_pair_steal_append(TALLOC_CTX *list_ctx, fr_pair_list_t *list, fr_pair_t *vp)
564{
565 if (fr_pair_order_list_in_a_list(vp)) {
567 return -1;
568 }
569
570 if (unlikely(fr_pair_steal(list_ctx, vp) < 0)) return -1;
571
572 if (unlikely(fr_pair_append(list, vp) < 0)) return -1;
573
574 return 0;
575}
576
577/** Change a vp's talloc ctx and insert it into a new list
578 *
579 * @param[in] list_ctx to move vp into.
580 * @param[out] list to add vp to.
581 * @param[in] vp to move.
582 * @return
583 * - 0 on success.
584 * - -1 on failure (already in list).
585 */
586int fr_pair_steal_prepend(TALLOC_CTX *list_ctx, fr_pair_list_t *list, fr_pair_t *vp)
587{
588 if (fr_pair_order_list_in_a_list(vp)) {
590 return -1;
591 }
592
593 if (unlikely(fr_pair_steal(list_ctx, vp) < 0)) return -1;
594
595 if (unlikely(fr_pair_prepend(list, vp) < 0)) return -1;
596
597 return 0;
598}
599
600/** Mark malformed attribute as raw
601 *
602 * @param[in] vp to mark as raw.
603 * @param[in] data to parse.
604 * @param[in] data_len of data to parse.
605 *
606 * @return
607 * - 0 on success
608 * - -1 on failure.
609 */
610int fr_pair_raw_afrom_pair(fr_pair_t *vp, uint8_t const *data, size_t data_len)
611{
612 fr_dict_attr_t *unknown;
613
615
616 if (!fr_cond_assert(vp->da->flags.is_unknown == false)) return -1;
617
618 if (!fr_cond_assert(vp->da->parent != NULL)) return -1;
619
621 if (!unknown) return -1;
622
623 fr_value_box_clear(&vp->data);
624
625 vp->da = unknown;
626 fr_assert(vp->da->type == FR_TYPE_OCTETS);
627
628 return fr_value_box_memdup(vp, &vp->data, NULL, data, data_len, true);
629}
630
631/** Iterate over pairs with a specified da
632 *
633 * @param[in] cursor to iterate over
634 * @param[in] current The fr_pair_t cursor->current. Will be advanced and checked to
635 * see if it matches the specified fr_dict_attr_t.
636 * @param[in] uctx The fr_dict_attr_t to search for.
637 * @return
638 * - Next matching fr_pair_t.
639 * - NULL if not more matching fr_pair_ts could be found.
640 */
641static void *fr_pair_iter_next_by_da(fr_dcursor_t *cursor, void *current, void *uctx)
642{
643 fr_pair_t *c = current;
644 fr_dict_attr_t *da = uctx;
645
646 while ((c = fr_dcursor_list_next(cursor, c))) {
647 PAIR_VERIFY(c);
648 if (c->da == da) break;
649 }
650
651 return c;
652}
653
654/** Iterate over pairs which are decedents of the specified da
655 *
656 * @param[in] cursor to iterate over.
657 * @param[in] current The fr_pair_t cursor->current. Will be advanced and checked to
658 * see if it matches the specified fr_dict_attr_t.
659 * @param[in] uctx The fr_dict_attr_t to search for.
660 * @return
661 * - Next matching fr_pair_t.
662 * - NULL if not more matching fr_pair_ts could be found.
663 */
664static void *fr_pair_iter_next_by_ancestor(fr_dcursor_t *cursor, void *current, void *uctx)
665{
666 fr_pair_t *c = current;
667 fr_dict_attr_t *da = uctx;
668
669 while ((c = fr_dcursor_list_next(cursor, c))) {
670 PAIR_VERIFY(c);
671 if (fr_dict_attr_common_parent(da, c->da, true)) break;
672 }
673
674 return c;
675}
676
677/** Return the number of instances of a given da in the specified list
678 *
679 * @param[in] list to search in.
680 * @param[in] da to look for in the list.
681 * @return
682 * - 0 if no instances exist.
683 * - >0 the number of instance of a given attribute.
684 */
685unsigned int fr_pair_count_by_da(fr_pair_list_t const *list, fr_dict_attr_t const *da)
686{
687 fr_pair_t *vp = NULL;
688 unsigned int count = 0;
689
690 if (fr_pair_list_empty(list)) return 0;
691
692 while ((vp = fr_pair_list_next(list, vp))) if (da == vp->da) count++;
693
694 return count;
695}
696
697/** Find the first pair with a matching da
698 *
699 * @param[in] list to search in.
700 * @param[in] prev the previous attribute in the list.
701 * @param[in] da the next da to find.
702 * @return
703 * - first matching fr_pair_t.
704 * - NULL if no fr_pair_ts match.
705 *
706 * @hidecallergraph
707 */
709{
710 fr_pair_t *vp = UNCONST(fr_pair_t *, prev);
711
712 if (fr_pair_list_empty(list)) return NULL;
713
714 PAIR_LIST_VERIFY(list);
715
716 while ((vp = fr_pair_list_next(list, vp))) if (da == vp->da) return vp;
717
718 return NULL;
719}
720
721/** Find the last pair with a matching da
722 *
723 * @param[in] list to search in.
724 * @param[in] da the da to find
725 * @return
726 * - first matching #fr_pair_t.
727 * - NULL if no #fr_pair_t's match.
728 *
729 * @hidecallergraph
730 */
732{
733 fr_pair_t *vp = NULL;
734
735 if (fr_pair_list_empty(list)) return NULL;
736
737 PAIR_LIST_VERIFY(list);
738
739 while ((vp = fr_pair_list_prev(list, vp))) if (da == vp->da) return vp;
740
741 return NULL;
742}
743
744/** Find a pair with a matching da at a given index
745 *
746 * @param[in] list to search in.
747 * @param[in] da to look for in the list.
748 * @param[in] idx Instance of the attribute to return.
749 * @return
750 * - first matching fr_pair_t.
751 * - NULL if no fr_pair_ts match.
752 *
753 * @hidecallergraph
754 */
755fr_pair_t *fr_pair_find_by_da_idx(fr_pair_list_t const *list, fr_dict_attr_t const *da, unsigned int idx)
756{
757 fr_pair_t *vp = NULL;
758
759 if (fr_pair_list_empty(list)) return NULL;
760
761 PAIR_LIST_VERIFY(list);
762
763 while ((vp = fr_pair_list_next(list, vp))) {
764 if (da != vp->da) continue;
765
766 if (idx == 0) return vp;
767
768 idx--;
769 }
770 return NULL;
771}
772
773/** Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree
774 *
775 * The list should be the one containing the top level attributes.
776 *
777 * @param[in] list to search in.
778 * @param[in] da the next da to find.
779 * @return
780 * - first matching fr_pair_t.
781 * - NULL if no fr_pair_ts match.
782 */
784{
785 fr_pair_t *vp;
786 fr_dict_attr_t const **find; /* DA currently being looked for */
787 fr_pair_list_t const *cur_list; /* Current list being searched */
788 fr_da_stack_t da_stack;
789
790 if (fr_pair_list_empty(list)) return NULL;
791
792 /*
793 * In the common case, we're looking for attributes in
794 * the root (at level 1), so we just skip to a special
795 * function for that
796 */
797 if (da->depth <= 1) return fr_pair_find_by_da(list, NULL, da);
798
799 fr_proto_da_stack_build(&da_stack, da);
800
801 /*
802 * Start at the top of the list, and at the top of the nesting.
803 */
804 cur_list = list;
805 find = &da_stack.da[0];
806 vp = NULL;
807
808 /*
809 * Loop over the list at each level until we find a matching da.
810 */
811 while (true) {
812 fr_pair_t *next;
813
814 fr_assert((*find)->depth <= da->depth);
815
816 /*
817 * Find a vp which matches a given da. If found,
818 * recurse into the child list to find the child
819 * attribute.
820 *
821 */
822 next = fr_pair_find_by_da(cur_list, vp, *find);
823 if (next) {
824 /*
825 * We've found a match for the requested
826 * da - return it.
827 */
828 if ((*find) == da) return next;
829
830 /*
831 * Prepare to search the next level.
832 */
833 cur_list = &next->vp_group;
834 find++;
835 vp = NULL;
836 continue;
837 }
838
839 /*
840 * We hit the end of the top-level list. Therefore we found nothing.
841 */
842 if (cur_list == list) break;
843
844 /*
845 * We hit the end of *A* list. Go to the parent
846 * VP, and then find its list.
847 *
848 * We still then have to go to the next attribute
849 * in the parent list, as we've checked all of the
850 * children of this VP.
851 */
852 find--;
853 vp = fr_pair_list_parent(cur_list);
854 cur_list = fr_pair_parent_list(vp);
855 }
856
857 /*
858 * We have the right list, go find the da.
859 */
860 return fr_pair_find_by_da(list, NULL, da);
861}
862
863/** Find the pair with the matching child attribute at a given index
864 *
865 * @param[in] list in which to search.
866 * @param[in] parent attribute in which to lookup child.
867 * @param[in] attr id of child.
868 * @param[in] idx Instance of the attribute to return.
869 * @return
870 * - first matching value pair.
871 * - NULL if no pair found.
872 */
874 fr_dict_attr_t const *parent, unsigned int attr, unsigned int idx)
875{
876 fr_dict_attr_t const *da;
877
878 /* List head may be NULL if it contains no VPs */
879 if (fr_pair_list_empty(list)) return NULL;
880
881 PAIR_LIST_VERIFY(list);
882
884 if (!da) return NULL;
885
886 return fr_pair_find_by_da_idx(list, da, idx);
887}
888
889/** Get the child list of a group
890 *
891 * @param[in] vp which MUST be of a type
892 * that can contain children.
893 * @return
894 * - NULL on error
895 * - pointer to head of the child list.
896 */
898{
899 if (!fr_type_is_structural(vp->vp_type)) return NULL;
900
901 return &vp->vp_group;
902}
903
904/** Return a pointer to the parent pair list
905 *
906 */
908{
909 return fr_pair_order_list_parent_list(vp);
910}
911
912/** Return a pointer to the parent pair.
913 *
914 */
916{
918
919 if (!list) return NULL;
920
921 if (!list->is_child) return NULL;
922
923 return (fr_pair_t *) (UNCONST(uint8_t *, list) - offsetof(fr_pair_t, vp_group));
924}
925
926/** Return a pointer to the parent pair which contains this list.
927 *
928 */
930{
931 if (!list) return NULL;
932
933 if (!list->is_child) return NULL;
934
935 return (fr_pair_t *) (UNCONST(uint8_t *, list) - offsetof(fr_pair_t, vp_group));
936}
937
938/** Keep attr tree and sublists synced on cursor insert
939 *
940 * @param[in] cursor the cursor being modified
941 * @param[in] to_insert fr_pair_t being inserted.
942 * @param[in] uctx fr_pair_list_t containing the order list.
943 * @return
944 * - 0 on success.
945 */
946static int _pair_list_dcursor_insert(fr_dcursor_t *cursor, void *to_insert, UNUSED void *uctx)
947{
948 fr_pair_t *vp = to_insert;
949
950 /*
951 * Mark the pair as inserted into the list.
952 */
953 fr_pair_order_list_set_head_from_dlist(fr_dcursor_list(cursor), vp);
954
956
957 return 0;
958}
959
960/** Keep attr tree and sublists synced on cursor removal
961 *
962 * @param[in] cursor the cursor being modified
963 * @param[in] to_remove fr_pair_t being removed.
964 * @param[in] uctx fr_pair_list_t containing the order list.
965 * @return
966 * - 0 on success.
967 */
968static int _pair_list_dcursor_remove(NDEBUG_UNUSED fr_dcursor_t *cursor, void *to_remove, UNUSED void *uctx)
969{
970 fr_pair_t *vp = to_remove;
972
973 /*
974 * Mark the pair as removed from the list.
975 */
976 fr_pair_order_list_set_head(NULL, vp);
977
979
980 /*
981 * If the VP is in the cursor, then the cursor code will
982 * take care of removing it.
983 */
984 if (fr_pair_order_list_dlist_head(&parent->order) == fr_dcursor_list(cursor)) return 0;
985
987 return 1;
988}
989
990/** Iterates over the leaves of a list
991 *
992 * @param[in] list to iterate over.
993 * @param[in] vp the current CVP
994 * @return
995 * - NULL when done
996 * - vp - a leaf pair
997 */
999{
1000 fr_pair_t *next, *parent;
1001 fr_pair_list_t *parent_list;
1002
1003 /*
1004 * Start: return the head of the top-level list.
1005 */
1006 if (!vp) {
1007 vp = fr_pair_list_head(list);
1008 if (!vp) goto next_parent_sibling;
1009
1010 next_sibling:
1011 if (fr_type_is_leaf(vp->vp_type)) return vp;
1012
1014
1015 vp = fr_pair_list_iter_leaf(&vp->vp_group, NULL);
1016 if (vp) return vp;
1017
1018 /*
1019 * vp is NULL, so we've processed all of its children.
1020 */
1021 }
1022
1023 /*
1024 * Go to the next sibling in the parent list of vp.
1025 */
1026next_parent_sibling:
1027 parent_list = fr_pair_parent_list(vp);
1028 if (!parent_list) return NULL;
1029
1030 next = fr_pair_list_next(parent_list, vp);
1031 if (!next) {
1032 /*
1033 * We're done the top-level list.
1034 */
1035 if (parent_list == list) return NULL;
1036
1038 fr_assert(&parent->vp_group == parent_list);
1039 vp = parent;
1040 goto next_parent_sibling;
1041 }
1042
1043 /*
1044 * We do have a "next" attribute. Go check if we can return it.
1045 */
1046 vp = next;
1047 goto next_sibling;
1048}
1049
1050/** Initialises a special dcursor with callbacks that will maintain the attr sublists correctly
1051 *
1052 * @note This is the only way to use a dcursor in non-const mode with fr_pair_list_t.
1053 *
1054 * @param[out] cursor to initialise.
1055 * @param[in] list to iterate over.
1056 * @param[in] iter Iterator to use when filtering pairs.
1057 * @param[in] uctx To pass to iterator.
1058 * @param[in] is_const whether the fr_pair_list_t is const.
1059 * @return
1060 * - NULL if src does not point to any items.
1061 * - The first pair in the list.
1062 */
1064 fr_dcursor_iter_t iter, void const *uctx,
1065 bool is_const)
1066{
1067 return _fr_dcursor_init(cursor, fr_pair_order_list_dlist_head(&list->order),
1068 iter, NULL, uctx,
1070}
1071
1072/** Initialises a special dcursor with callbacks that will maintain the attr sublists correctly
1073 *
1074 * @note This is the only way to use a dcursor in non-const mode with fr_pair_list_t.
1075 *
1076 * @param[out] cursor to initialise.
1077 * @param[in] list to iterate over.
1078 * @param[in] is_const whether the fr_pair_list_t is const.
1079 * @return
1080 * - NULL if src does not point to any items.
1081 * - The first pair in the list.
1082 */
1084 bool is_const)
1085{
1086 return _fr_dcursor_init(cursor, fr_pair_order_list_dlist_head(&list->order),
1087 NULL, NULL, NULL,
1089}
1090
1091/** Initialise a cursor that will return only attributes matching the specified #fr_dict_attr_t
1092 *
1093 * @param[in] cursor to initialise.
1094 * @param[in] list to iterate over.
1095 * @param[in] da to search for.
1096 * @param[in] is_const whether the fr_pair_list_t is const.
1097 * @return
1098 * - The first matching pair.
1099 * - NULL if no pairs match.
1100 */
1102 fr_pair_list_t const *list, fr_dict_attr_t const *da,
1103 bool is_const)
1104{
1105 return _fr_dcursor_init(cursor, fr_pair_order_list_dlist_head(&list->order),
1106 fr_pair_iter_next_by_da, NULL, da,
1108}
1109
1110/** Initialise a cursor that will return only attributes descended from the specified #fr_dict_attr_t
1111 *
1112 * @param[in] cursor to initialise.
1113 * @param[in] list to iterate over.
1114 * @param[in] da who's descendants to search for.
1115 * @param[in] is_const whether the fr_pair_list_t is const.
1116 * @return
1117 * - The first matching pair.
1118 * - NULL if no pairs match.
1119 */
1121 fr_pair_list_t const *list, fr_dict_attr_t const *da,
1122 bool is_const)
1123{
1124 fr_pair_t *vp;
1125
1127
1128 /*
1129 * This function is only used by snmp.c and password.c. Once we've fully moved to
1130 * nested attributes, it should be removed.
1131 */
1132 fr_assert(da->parent->flags.is_root);
1133
1134 vp = fr_pair_find_by_da(list, NULL, da);
1135 if (vp) {
1136 list = &vp->vp_group;
1137
1138 return _fr_dcursor_init(cursor, fr_pair_order_list_dlist_head(&list->order),
1139 NULL, NULL, NULL,
1141 }
1142
1143 return _fr_dcursor_init(cursor, fr_pair_order_list_dlist_head(&list->order),
1146}
1147
1148/** Iterate over pairs
1149 *
1150 * @param[in] cursor to iterate over.
1151 * @param[in] current The fr_value_box_t cursor->current. Will be advanced and checked to
1152 * see if it matches the specified fr_dict_attr_t.
1153 * @param[in] uctx unused
1154 * @return
1155 * - Next matching fr_pair_t.
1156 * - NULL if not more matching fr_pair_ts could be found.
1157 */
1158static void *_fr_pair_iter_next_value(fr_dcursor_t *cursor, void *current, UNUSED void *uctx)
1159{
1160 fr_pair_t *vp;
1161
1162 if (!current) {
1163 vp = NULL;
1164 } else {
1165 vp = (fr_pair_t *) ((uint8_t *) current - offsetof(fr_pair_t, data));
1166 PAIR_VERIFY(vp);
1167 }
1168
1169 while ((vp = fr_dcursor_list_next(cursor, vp))) {
1170 PAIR_VERIFY(vp);
1171 if (fr_type_is_leaf(vp->vp_type)) return &vp->data;
1172 }
1173
1174 return NULL;
1175}
1176
1177/*
1178 * The value dcursor just runs the iterator, and never uses the dlist. Inserts and deletes are forbidden.
1179 *
1180 * However, the underlying dcursor code needs a dlist, so we create a fake one to pass it. In debug
1181 * builds, the dcursor code will do things like try to check talloc types. So we need to pass it an
1182 * empty dlist with no talloc types.
1183 */
1185 .offset = offsetof(fr_dlist_head_t, entry),
1186 .type = NULL,
1187 .num_elements = 0,
1188 .entry = {
1189 .prev = &value_dlist.entry,
1191 },
1192};
1193
1194/** Initialises a special dcursor over a #fr_pair_list_t, but which returns #fr_value_box_t
1195 *
1196 * @note This is the only way to use a dcursor in non-const mode with fr_pair_list_t.
1197 * @note - the list cannot be modified, and structural attributes are not returned.
1198 *
1199 * @param[out] cursor to initialise.
1200 * @return
1201 * - NULL if src does not point to any items.
1202 * - The first pair in the list.
1203 */
1205{
1206 return _fr_dcursor_init(cursor, &value_dlist,
1207 _fr_pair_iter_next_value, NULL, NULL, NULL, NULL, NULL, true);
1208}
1209
1210/** Iterate over pairs
1211 *
1212 * @param[in] cursor to iterate over.
1213 * @param[in] current The fr_value_box_t cursor->current. Will be advanced and checked to
1214 * see if it matches the specified fr_dict_attr_t.
1215 * @param[in] uctx The parent dcursor
1216 * @return
1217 * - Next matching fr_pair_t.
1218 * - NULL if not more matching fr_pair_ts could be found.
1219 */
1220static void *_fr_pair_iter_next_dcursor_value(UNUSED fr_dcursor_t *cursor, void *current, void *uctx)
1221{
1222 fr_pair_t *vp;
1223 fr_dcursor_t *parent = uctx;
1224
1225 if (!current) {
1227 if (!vp) return NULL;
1228 goto check;
1229 }
1230
1231 while ((vp = fr_dcursor_next(parent))) {
1232 check:
1233 PAIR_VERIFY(vp);
1234
1235 if (fr_type_is_leaf(vp->vp_type)) return &vp->data;
1236 }
1237
1238 return NULL;
1239}
1240
1241/** Initialises a special dcursor over another cursor which returns #fr_pair_t, but we return #fr_value_box_t
1242 *
1243 * @note - the list cannot be modified, and structural attributes are not returned.
1244 *
1245 * @param[out] cursor to initialise.
1246 * @param[in] parent to iterate over
1247 * @return
1248 * - NULL if src does not point to any items.
1249 * - The first pair in the list.
1250 */
1256
1257/** Add a VP to the start of the list.
1258 *
1259 * Links an additional VP 'add' at the beginning a list.
1260 *
1261 * @param[in] list VP in linked list. Will add new VP to this list.
1262 * @param[in] to_add VP to add to list.
1263 * @return
1264 * - 0 on success.
1265 * - -1 on failure (pair already in list).
1266 */
1268{
1269 PAIR_VERIFY(to_add);
1270
1271#ifdef WITH_VERIFY_PTR
1272 fr_assert(!fr_pair_order_list_in_a_list(to_add));
1273 list->verified = false;
1274#endif
1275
1276 if (fr_pair_order_list_in_a_list(to_add)) {
1278 return -1;
1279 }
1280
1281 fr_pair_order_list_insert_head(&list->order, to_add);
1282
1283 return 0;
1284}
1285
1286/** Add a VP to the end of the list.
1287 *
1288 * Links an additional VP 'to_add' at the end of a list.
1289 *
1290 * @param[in] list VP in linked list. Will add new VP to this list.
1291 * @param[in] to_add VP to add to list.
1292 * @return
1293 * - 0 on success.
1294 * - -1 on failure (pair already in list).
1295 *
1296 * @hidecallergraph
1297 */
1299{
1300#ifdef WITH_VERIFY_PTR
1301 fr_assert(!fr_pair_order_list_in_a_list(to_add));
1302 list->verified = false;
1303#endif
1304
1305 if (fr_pair_order_list_in_a_list(to_add)) {
1307 return -1;
1308 }
1309
1310 fr_pair_order_list_insert_tail(&list->order, to_add);
1311
1312 return 0;
1313}
1314
1315/** Add a VP after another VP.
1316 *
1317 * @param[in] list VP in linked list. Will add new VP to this list.
1318 * @param[in] pos to insert pair after.
1319 * @param[in] to_add VP to add to list.
1320 * @return
1321 * - 0 on success.
1322 * - -1 on failure (pair already in list).
1323 */
1325{
1326 PAIR_VERIFY(to_add);
1327
1328#ifdef WITH_VERIFY_PTR
1329 fr_assert(!fr_pair_order_list_in_a_list(to_add));
1330 list->verified = false;
1331#endif
1332
1333 if (fr_pair_order_list_in_a_list(to_add)) {
1335 return -1;
1336 }
1337
1338 if (pos && !fr_pair_order_list_in_list(&list->order, pos)) {
1340 return -1;
1341 }
1342
1343 fr_pair_order_list_insert_after(&list->order, pos, to_add);
1344
1345 return 0;
1346}
1347
1348/** Add a VP before another VP.
1349 *
1350 * @param[in] list VP in linked list. Will add new VP to this list.
1351 * @param[in] pos to insert pair after.
1352 * @param[in] to_add VP to add to list.
1353 * @return
1354 * - 0 on success.
1355 * - -1 on failure (pair already in list).
1356 */
1358{
1359 PAIR_VERIFY(to_add);
1360
1361#ifdef WITH_VERIFY_PTR
1362 fr_assert(!fr_pair_order_list_in_a_list(to_add));
1363 fr_assert(!pos || fr_pair_order_list_in_a_list(pos));
1364 list->verified = false;
1365#endif
1366
1367 if (fr_pair_order_list_in_a_list(to_add)) {
1369 return -1;
1370 }
1371
1372 if (pos && !fr_pair_order_list_in_list(&list->order, pos)) {
1374 return -1;
1375 }
1376
1377 fr_pair_order_list_insert_before(&list->order, pos, to_add);
1378
1379 return 0;
1380}
1381
1382/** Replace a given VP
1383 *
1384 * @note Memory used by the VP being replaced will be freed.
1385 *
1386 * @param[in,out] list pair list
1387 * @param[in] to_replace pair to replace and free, on list
1388 * @param[in] vp New pair to insert.
1389 */
1391{
1392 PAIR_VERIFY_WITH_LIST(list, to_replace);
1393 PAIR_VERIFY(vp);
1394
1395#ifdef WITH_VERIFY_PTR
1396 fr_assert(!fr_pair_order_list_in_a_list(vp));
1397 fr_assert(fr_pair_order_list_in_a_list(to_replace));
1398 list->verified = false;
1399#endif
1400
1401 fr_pair_insert_after(list, to_replace, vp);
1402 fr_pair_remove(list, to_replace);
1403 talloc_free(to_replace);
1404}
1405
1406/** Alloc a new fr_pair_t (and append)
1407 *
1408 * @param[in] ctx to allocate new #fr_pair_t in.
1409 * @param[out] out Pair we allocated. May be NULL if the caller doesn't
1410 * care about manipulating the fr_pair_t.
1411 * @param[in,out] list in which to append the pair.
1412 * @param[in] da of attribute to create.
1413 * @return
1414 * - 0 on success.
1415 * - -1 on failure.
1416 */
1417int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
1418{
1419 fr_pair_t *vp;
1420
1421 vp = fr_pair_afrom_da(ctx, da);
1422 if (unlikely(!vp)) {
1423 if (out) *out = NULL;
1424 return -1;
1425 }
1426
1427 fr_pair_append(list, vp);
1428 if (out) *out = vp;
1429
1430 return 0;
1431}
1432
1433/** Alloc a new fr_pair_t (and prepend)
1434 *
1435 * @param[in] ctx to allocate new #fr_pair_t in.
1436 * @param[out] out Pair we allocated. May be NULL if the caller doesn't
1437 * care about manipulating the fr_pair_t.
1438 * @param[in,out] list in which to prepend the pair.
1439 * @param[in] da of attribute to create.
1440 * @return
1441 * - 0 on success.
1442 * - -1 on failure.
1443 */
1444int fr_pair_prepend_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
1445{
1446 fr_pair_t *vp;
1447
1448 vp = fr_pair_afrom_da(ctx, da);
1449 if (unlikely(!vp)) {
1450 if (out) *out = NULL;
1451 return -1;
1452 }
1453
1454 fr_pair_prepend(list, vp);
1455 if (out) *out = vp;
1456
1457 return 0;
1458}
1459
1460/** Alloc a new fr_pair_t, adding the parent attributes if required
1461 *
1462 * A child pair will be added to the first available matching parent
1463 * found.
1464 *
1465 * @param[in] ctx to allocate new #fr_pair_t in
1466 * @param[out] out Pair we allocated. May be NULL if the caller doesn't
1467 * care about manipulating the fr_pair_t.
1468 * @param[in] list in which to insert the pair.
1469 * @param[in] da of the attribute to create.
1470 * @return
1471 * - 0 on success.
1472 * - -1 on failure.
1473 */
1475{
1476 fr_pair_t *vp = NULL;
1477 fr_da_stack_t da_stack;
1478 fr_dict_attr_t const **find;
1479 TALLOC_CTX *pair_ctx = ctx;
1480
1481 /*
1482 * Fast path for non-nested attributes
1483 */
1484 if (da->depth <= 1) return fr_pair_append_by_da(ctx, out, list, da);
1485
1486 fr_proto_da_stack_build(&da_stack, da);
1487 find = &da_stack.da[0];
1488
1489 /*
1490 * Walk down the da stack looking for candidate parent
1491 * attributes and then allocating the leaf.
1492 */
1493 while (true) {
1494 fr_assert((*find)->depth <= da->depth);
1495
1496 /*
1497 * We're not at the leaf, look for a potential parent
1498 */
1499 if ((*find) != da) vp = fr_pair_find_by_da(list, NULL, *find);
1500
1501 /*
1502 * Nothing found, create the pair
1503 */
1504 if (!vp) {
1505 if (fr_pair_append_by_da(pair_ctx, &vp, list, *find) < 0) {
1506 if (out) *out = NULL;
1507 return -1;
1508 }
1510 }
1511
1512 /*
1513 * We're at the leaf, return
1514 */
1515 if ((*find) == da) {
1516 if(out) *out = vp;
1517 return 0;
1518 }
1519
1520 /*
1521 * Prepare for next level
1522 */
1523 list = &vp->vp_group;
1524 pair_ctx = vp;
1525 vp = NULL;
1526 find++;
1527 }
1528}
1529
1530/** Return the first fr_pair_t matching the #fr_dict_attr_t or alloc a new fr_pair_t and its subtree (and append)
1531 *
1532 * @param[in] parent If parent->da is an ancestor of the specified
1533 * da, we continue building out the nested structure
1534 * from the parent.
1535 * If parent is NOT an ancestor, then it must be a group
1536 * attribute, and we will append the shallowest member
1537 * of the struct or TLV as a child, and build out everything
1538 * to the specified da.
1539 * @param[out] out Pair we allocated or found. May be NULL if the caller doesn't
1540 * care about manipulating the fr_pair_t.
1541 * @param[in] da of attribute to locate or alloc.
1542 * @return
1543 * - 1 if attribute already existed.
1544 * - 0 if we allocated a new attribute.
1545 * - -1 on memory allocation failure.
1546 * - -2 if the parent is not a group attribute.
1547 */
1549 fr_dict_attr_t const *da)
1550{
1551 fr_pair_t *vp = NULL;
1552 fr_da_stack_t da_stack;
1553 fr_dict_attr_t const **find; /* ** to allow us to iterate */
1554 TALLOC_CTX *pair_ctx = parent;
1555 fr_pair_list_t *list = &parent->vp_group;
1556
1557 /*
1558 * Fast path for non-nested attributes
1559 */
1560 if (da->depth <= 1) {
1561 vp = fr_pair_find_by_da(list, NULL, da);
1562 if (vp) {
1563 if (out) *out = vp;
1564 return 1;
1565 }
1566
1567 return fr_pair_append_by_da(parent, out, list, da);
1568 }
1569
1570 fr_proto_da_stack_build(&da_stack, da);
1571 /*
1572 * Is parent an ancestor of the attribute we're trying
1573 * to build? If so, we resume from the deepest pairs
1574 * already created.
1575 *
1576 * da stack excludes the root.
1577 */
1578 if ((parent->da->depth < da->depth) && (da_stack.da[parent->da->depth - 1] == parent->da)) {
1579 /*
1580 * Start our search from the parent's children
1581 */
1582 list = &parent->vp_group;
1583 find = &da_stack.da[parent->da->depth]; /* Next deepest attr than parent */
1584 /*
1585 * Disallow building one TLV tree into another
1586 */
1587 } else if (!fr_type_is_group(parent->da->type)) {
1588 fr_strerror_printf("Expected parent \"%s\" to be an ancestor of \"%s\" or a group. "
1589 "But it is not an ancestor and is of type %s", parent->da->name, da->name,
1590 fr_type_to_str(parent->da->type));
1591 return -2;
1592 } else {
1593 find = &da_stack.da[0];
1594 }
1595
1596 /*
1597 * Walk down the da stack looking for candidate parent
1598 * attributes and then allocating the leaf, and any
1599 * attributes between the leaf and parent.
1600 */
1601 while (true) {
1602 fr_assert((*find)->depth <= da->depth);
1603
1604 vp = fr_pair_find_by_da(list, NULL, *find);
1605 /*
1606 * Nothing found at this level, create the pair
1607 */
1608 if (!vp) {
1609 if (fr_pair_append_by_da(pair_ctx, &vp, list, *find) < 0) {
1610 if (out) *out = NULL;
1611 return -1;
1612 }
1614 }
1615
1616 /*
1617 * We're at the leaf, return
1618 */
1619 if ((*find) == da) {
1620 if (out) *out = vp;
1621 return 0;
1622 }
1623
1624 /*
1625 * Prepare for next level
1626 */
1627 list = &vp->vp_group;
1628 pair_ctx = vp;
1629 vp = NULL;
1630 find++;
1631 }
1632}
1633
1634/** Delete matching pairs from the specified list
1635 *
1636 * @param[in,out] list to search for attributes in or delete attributes from.
1637 * @param[in] da to match.
1638 * @return
1639 * - >0 the number of pairs deleted.
1640 * - 0 if no pairs were deleted.
1641 */
1643{
1644 int cnt = 0;
1645
1646 fr_pair_list_foreach(list, vp) {
1647 if (da == vp->da) {
1648 if (fr_pair_immutable(vp)) continue;
1649
1650 cnt++;
1651 fr_pair_delete(list, vp);
1652 }
1653 }
1654
1655 return cnt;
1656}
1657
1658/** Delete matching pairs from the specified list, and prune any empty branches
1659 *
1660 * @param[in,out] list to search for attributes in or delete attributes from.
1661 * @param[in] da to match.
1662 * @return
1663 * - >0 the number of pairs deleted.
1664 * - 0 if no pairs were deleted.
1665 */
1667{
1668 int cnt = 0;
1669 fr_pair_t *vp;
1670 fr_dict_attr_t const **find; /* DA currently being looked for */
1671 fr_pair_list_t *cur_list; /* Current list being searched */
1672 fr_da_stack_t da_stack;
1673
1674 /*
1675 * Fast path for non-nested attributes
1676 */
1677 if (da->depth <= 1) return fr_pair_delete_by_da(list, da);
1678
1679 /*
1680 * No pairs, fast path!
1681 */
1682 if (fr_pair_list_empty(list)) return 0;
1683
1684 /*
1685 * Similar to fr_pair_find_by_da_nested()
1686 */
1687 fr_proto_da_stack_build(&da_stack, da);
1688 cur_list = list;
1689 find = &da_stack.da[0];
1690 vp = NULL;
1691
1692 /*
1693 * Loop over the list at each level until we find a matching da.
1694 */
1695 while (true) {
1696 fr_pair_t *next;
1697
1698 fr_assert((*find)->depth <= da->depth);
1699
1700 /*
1701 * Find a vp which matches a given da. If found,
1702 * recurse into the child list to find the child
1703 * attribute.
1704 *
1705 */
1706 next = fr_pair_find_by_da(cur_list, vp, *find);
1707 if (next) {
1708 /*
1709 * We've found a match for the requested
1710 * da - delete it
1711 */
1712 if ((*find) == da) {
1713 do {
1714 fr_pair_delete(cur_list, next);
1715 cnt++;
1716 } while ((next = fr_pair_find_by_da(cur_list, vp, *find)) != NULL);
1717
1718 return cnt;
1719 }
1720
1721 /*
1722 * Prepare to search the next level.
1723 */
1724 cur_list = &next->vp_group;
1725 find++;
1726 vp = NULL;
1727 continue;
1728 }
1729
1730 /*
1731 * We hit the end of the top-level list. Therefore we found nothing.
1732 */
1733 if (cur_list == list) break;
1734
1735 /*
1736 * We hit the end of *A* list. Go to the parent
1737 * VP, and then find its list.
1738 *
1739 * We still then have to go to the next attribute
1740 * in the parent list, as we've checked all of the
1741 * children of this VP.
1742 */
1743 find--;
1744 vp = fr_pair_list_parent(cur_list);
1745 cur_list = fr_pair_parent_list(vp);
1746 }
1747
1748 return fr_pair_delete_by_da(list, da);
1749}
1750
1751/** Delete matching pairs from the specified list
1752 *
1753 * @param[in] list to delete attributes from.
1754 * @param[in] parent to match.
1755 * @param[in] attr to match.
1756 * @return
1757 * - >0 the number of pairs deleted.
1758 * - 0 if no pairs were delete.
1759 * - -1 if we couldn't resolve the attribute number.
1760 */
1762{
1763 fr_dict_attr_t const *da;
1764
1766 if (!da) return -1;
1767
1768 return fr_pair_delete_by_da(list, da);
1769}
1770
1771/** Remove fr_pair_t from a list and free
1772 *
1773 * @param[in] list of value pairs to remove VP from.
1774 * @param[in] vp to remove
1775 * @return
1776 * - <0 on error: pair wasn't deleted
1777 * - 0 on success
1778 */
1780{
1781 fr_pair_remove(list, vp);
1782 return talloc_free(vp);
1783}
1784
1785/** Order attributes by their da
1786 *
1787 * Useful where attributes need to be aggregated, but not necessarily
1788 * ordered by attribute number.
1789 *
1790 * @param[in] a first #fr_pair_t
1791 * @param[in] b second #fr_pair_t
1792 * @return
1793 * - +1 if a > b
1794 * - 0 if a == b
1795 * - -1 if a < b
1796 */
1797fr_cmp_ret_t fr_pair_cmp_by_da(void const *a, void const *b)
1798{
1799 fr_pair_t const *my_a = a;
1800 fr_pair_t const *my_b = b;
1801
1802 PAIR_VERIFY(my_a);
1803 PAIR_VERIFY(my_b);
1804
1805 return CMP(my_a->da, my_b->da);
1806}
1807
1808#ifdef WITH_VERIFY_PTR
1809/** Order attributes by their da, with minimal checks.
1810 *
1811 * This is a variant of #fr_pair_cmp_by_da, which does NOT call
1812 * talloc_get_type_abort(), or PAIR_VERIFY(). If the caller puts a
1813 * #fr_pair_t onto the stack, then it doesn't have a talloc type, so
1814 * we can't call talloc_get_type_abort().
1815 *
1816 * Alternatively, if the list is being modified, PAIR_VERIFY() walks
1817 * the list being modified, which can end up with problems. As a
1818 * result, this code takes more chances than fr_pair_cmp_by_da().
1819 *
1820 * @param[in] a first #fr_pair_t
1821 * @param[in] b second #fr_pair_t
1822 * @return
1823 * - +1 if a > b
1824 * - 0 if a == b
1825 * - -1 if a < b
1826 */
1827fr_cmp_ret_t fr_pair_cmp_by_da_mutable(void const *a, void const *b)
1828{
1829 fr_pair_t const *my_a = a;
1830 fr_pair_t const *my_b = b;
1831
1832 return CMP(my_a->da, my_b->da);
1833}
1834#endif
1835
1836/** Order attributes by their attribute number, and tag
1837 *
1838 * @param[in] a first dict_attr_t.
1839 * @param[in] b second dict_attr_t.
1840 * @return
1841 * - +1 if a > b
1842 * - 0 if a == b
1843 * - -1 if a < b
1844 */
1845static inline fr_cmp_ret_t pair_cmp_by_num(void const *a, void const *b)
1846{
1847 int8_t ret;
1848 unsigned int i, min;
1849 fr_pair_t const *my_a = a;
1850 fr_pair_t const *my_b = b;
1851 fr_da_stack_t da_stack_a, da_stack_b;
1852
1853 PAIR_VERIFY(my_a);
1854 PAIR_VERIFY(my_b);
1855
1856 fr_proto_da_stack_build(&da_stack_a, my_a->da);
1857 fr_proto_da_stack_build(&da_stack_b, my_b->da);
1858
1859 if (da_stack_a.depth <= da_stack_b.depth) {
1860 min = da_stack_a.depth;
1861 } else {
1862 min = da_stack_b.depth;
1863 }
1864
1865 for (i = 0; i < min; i++) {
1866 ret = CMP(da_stack_a.da[i]->attr, da_stack_b.da[i]->attr);
1867 if (ret != 0) return ret;
1868 }
1869
1870 /*
1871 * Sort attributes of similar depth together.
1872 *
1873 * What we really want to do is to sort by entire parent da_stack.
1874 */
1875 ret = CMP(my_a->da->depth, my_b->da->depth);
1876 if (ret != 0) return ret;
1877
1878 /*
1879 * Attributes of the same depth get sorted by their parents.
1880 */
1881 ret = CMP(my_a->da->parent->attr, my_b->da->parent->attr);
1882 if (ret != 0) return ret;
1883
1884 /*
1885 * If the attributes have the same parent, they get sorted by number.
1886 */
1887 return CMP(my_a->da->attr, my_b->da->attr);
1888}
1889
1890/** Order attributes by their parent(s), attribute number, and tag
1891 *
1892 * Useful for some protocols where attributes of the same number should by aggregated
1893 * within a packet or container TLV.
1894 *
1895 * @param[in] a first dict_attr_t.
1896 * @param[in] b second dict_attr_t.
1897 * @return
1898 * - +1 if a > b
1899 * - 0 if a == b
1900 * - -1 if a < b
1901 */
1902fr_cmp_ret_t fr_pair_cmp_by_parent_num(void const *a, void const *b)
1903{
1904 fr_pair_t const *vp_a = a;
1905 fr_pair_t const *vp_b = b;
1906 fr_dict_attr_t const *da_a = vp_a->da;
1907 fr_dict_attr_t const *da_b = vp_b->da;
1908 fr_da_stack_t da_stack_a;
1909 fr_da_stack_t da_stack_b;
1910 int8_t cmp;
1911 int i;
1912
1913 /*
1914 * Fast path (assuming attributes
1915 * are in the same dictionary).
1916 */
1917 if ((da_a->parent->flags.is_root) && (da_b->parent->flags.is_root)) return pair_cmp_by_num(vp_a, vp_b);
1918
1919 fr_proto_da_stack_build(&da_stack_a, da_a);
1920 fr_proto_da_stack_build(&da_stack_b, da_b);
1921
1922 for (i = 0; (da_a = da_stack_a.da[i]) && (da_b = da_stack_b.da[i]); i++) {
1923 cmp = CMP(da_a->attr, da_b->attr);
1924 if (cmp != 0) return cmp;
1925 }
1926
1927 /*
1928 * If a has a shallower attribute
1929 * hierarchy than b, it should come
1930 * before b.
1931 */
1932 return (da_a && !da_b) - (!da_a && da_b);
1933}
1934
1935/** Compare two pairs, using the operator from "a"
1936 *
1937 * i.e. given two attributes, it does:
1938 *
1939 * (b->data) (a->operator) (a->data)
1940 *
1941 * e.g. "foo" != "bar"
1942 *
1943 * @param[in] a the head attribute
1944 * @param[in] b the second attribute
1945 * @return
1946 * - 1 if true.
1947 * - 0 if false.
1948 * - -1 on failure.
1949 */
1950int fr_pair_cmp(fr_pair_t const *a, fr_pair_t const *b)
1951{
1952 if (!a) return -1;
1953
1954 PAIR_VERIFY(a);
1955 if (b) PAIR_VERIFY(b);
1956
1957 switch (a->op) {
1958 case T_OP_CMP_TRUE:
1959 return (b != NULL);
1960
1961 case T_OP_CMP_FALSE:
1962 return (b == NULL);
1963
1964 /*
1965 * a is a regex, compile it, print b to a string,
1966 * and then do string comparisons.
1967 */
1968 case T_OP_REG_EQ:
1969 case T_OP_REG_NE:
1970#ifndef HAVE_REGEX
1971 return -1;
1972#else
1973 if (!b) return false;
1974
1975 {
1976 ssize_t slen;
1977 regex_t *preg;
1978 char *value;
1979
1980 if (!fr_cond_assert(a->vp_type == FR_TYPE_STRING)) return -1;
1981
1982 slen = regex_compile(NULL, &preg, a->vp_strvalue, talloc_strlen(a->vp_strvalue),
1983 NULL, false, true);
1984 if (slen <= 0) {
1985 fr_strerror_printf_push("Error at offset %zd compiling regex for %s", -slen,
1986 a->da->name);
1987 return -1;
1988 }
1989 fr_pair_aprint(NULL, &value, NULL, b);
1990 if (!value) {
1991 talloc_free(preg);
1992 return -1;
1993 }
1994
1995 /*
1996 * Don't care about substring matches, oh well...
1997 */
1998 slen = regex_exec(preg, value, talloc_strlen(value), NULL);
1999 talloc_free(preg);
2001
2002 if (slen < 0) return -1;
2003 if (a->op == T_OP_REG_EQ) return (int)slen;
2004 return !slen;
2005 }
2006#endif
2007
2008 default: /* we're OK */
2009 if (!b) return false;
2010 break;
2011 }
2012
2013 return fr_pair_cmp_op(a->op, b, a);
2014}
2015
2016/** Determine equality of two lists
2017 *
2018 * This is useful for comparing lists of attributes inserted into a binary tree.
2019 *
2020 * @param a head list of #fr_pair_t.
2021 * @param b second list of #fr_pair_t.
2022 * @return
2023 * - CMP_LT if a < b.
2024 * - CMP_EQ if the two lists are equal.
2025 * - CMP_GT if a > b.
2026 * - CMP_ERR if the lists are not comparable, retrieve the error with fr_strerror.
2027 */
2029{
2030 fr_pair_t *a_p, *b_p;
2031
2032 for (a_p = fr_pair_list_head(a), b_p = fr_pair_list_head(b);
2033 a_p && b_p;
2034 a_p = fr_pair_list_next(a, a_p), b_p = fr_pair_list_next(b, b_p)) {
2035 fr_cmp_ret_t ret;
2036
2037 /* Same VP, no point doing expensive checks */
2038 if (a_p == b_p) continue;
2039
2040 ret = CMP(a_p->da, b_p->da);
2041 if (ret != 0) return ret;
2042
2043 switch (a_p->vp_type) {
2044 case FR_TYPE_STRUCTURAL:
2045 ret = fr_pair_list_cmp(&a_p->vp_group, &b_p->vp_group);
2046 if (ret != 0) return ret;
2047 break;
2048
2049 default:
2050 ret = fr_value_box_cmp(&a_p->data, &b_p->data);
2051 if (ret != 0) return ret;
2052 }
2053
2054 }
2055
2056 /*
2057 * If we've run off of the end of one of the lists.
2058 */
2059 return CMP(a_p, b_p);
2060}
2061
2062/** Write an error to the library errorbuff detailing the mismatch
2063 *
2064 * Retrieve output with fr_strerror();
2065 *
2066 * @todo add thread specific talloc contexts.
2067 *
2068 * @param failed pair of attributes which didn't match.
2069 */
2070void fr_pair_validate_debug(fr_pair_t const *failed[2])
2071{
2072 fr_pair_t const *filter = failed[0];
2073 fr_pair_t const *list = failed[1];
2074
2075 fr_strerror_clear(); /* Clear any existing messages */
2076
2077 if (!list) {
2078 if (!filter) {
2079 (void) fr_cond_assert(filter != NULL);
2080 return;
2081 }
2082 fr_strerror_printf("Attribute \"%s\" not found in list", filter->da->name);
2083 return;
2084 }
2085
2086 if (!filter || (filter->da != list->da)) {
2087 fr_strerror_printf("Attribute \"%s\" not found in filter", list->da->name);
2088 return;
2089 }
2090
2091 fr_strerror_printf("Attribute value: %pP didn't match filter: %pP", list, filter);
2092
2093 return;
2094}
2095
2096/** Uses fr_pair_cmp to verify all fr_pair_ts in list match the filter defined by check
2097 *
2098 * @note will sort both filter and list in place.
2099 *
2100 * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match.
2101 * May be NULL.
2102 * @param filter attributes to check list against.
2103 * @param list attributes, probably a request or reply
2104 */
2105bool fr_pair_validate(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list)
2106{
2107 fr_pair_t *check, *match;
2108
2109 if (fr_pair_list_empty(filter) && fr_pair_list_empty(list)) return true;
2110
2111 /*
2112 * This allows us to verify the sets of validate and reply are equal
2113 * i.e. we have a validate rule which matches every reply attribute.
2114 *
2115 * @todo this should be removed one we have sets and lists
2116 */
2119
2120 check = fr_pair_list_head(filter);
2121 match = fr_pair_list_head(list);
2122 while (match || check) {
2123 /*
2124 * Lists are of different lengths
2125 */
2126 if (!match || !check) goto mismatch;
2127
2128 /*
2129 * The lists are sorted, so if the head
2130 * attributes aren't of the same type, then we're
2131 * done.
2132 */
2133 if (!ATTRIBUTE_EQ(check, match)) goto mismatch;
2134
2135 /*
2136 * They're of the same type, but don't have the
2137 * same values. This is a problem.
2138 *
2139 * Note that the RFCs say that for attributes of
2140 * the same type, order is important.
2141 */
2142 switch (check->vp_type) {
2143 case FR_TYPE_STRUCTURAL:
2144 /*
2145 * Return from here on failure, so that the nested mismatch
2146 * information is preserved.
2147 */
2148 if (!fr_pair_validate(failed, &check->vp_group, &match->vp_group)) return false;
2149 break;
2150
2151 default:
2152 /*
2153 * This attribute passed the filter
2154 */
2155 if (!fr_pair_cmp(check, match)) goto mismatch;
2156 break;
2157 }
2158
2159 check = fr_pair_list_next(filter, check);
2160 match = fr_pair_list_next(list, match);
2161 }
2162
2163 return true;
2164
2165mismatch:
2166 if (failed) {
2167 failed[0] = check;
2168 failed[1] = match;
2169 }
2170 return false;
2171}
2172
2173/** Uses fr_pair_cmp to verify all fr_pair_ts in list match the filter defined by check
2174 *
2175 * @note will sort both filter and list in place.
2176 *
2177 * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match.
2178 * May be NULL.
2179 * @param filter attributes to check list against.
2180 * @param list attributes, probably a request or reply
2181 */
2183{
2184 fr_pair_t *last_check = NULL, *match = NULL;
2185
2186 if (fr_pair_list_empty(filter) && fr_pair_list_empty(list)) return true;
2187
2188 /*
2189 * This allows us to verify the sets of validate and reply are equal
2190 * i.e. we have a validate rule which matches every reply attribute.
2191 *
2192 * @todo this should be removed one we have sets and lists
2193 */
2196
2197 fr_pair_list_foreach(filter, check) {
2198 /*
2199 * Were processing check attributes of a new type.
2200 */
2201 if (!ATTRIBUTE_EQ(last_check, check)) {
2202 /*
2203 * Record the start of the matching attributes in the pair list
2204 * For every other operator we require the match to be present
2205 */
2206 while ((match = fr_pair_list_next(list, match))) {
2207 if (match->da == check->da) break;
2208 }
2209 if (!match) {
2210 if (check->op == T_OP_CMP_FALSE) continue;
2211 goto mismatch;
2212 }
2213
2214 last_check = check;
2215 } else {
2216 match = fr_pair_list_head(list);
2217 }
2218
2219 /*
2220 * Now iterate over all attributes of the same type.
2221 */
2222 for (;
2223 ATTRIBUTE_EQ(match, check);
2224 match = fr_pair_list_next(list, match)) {
2225 switch (check->vp_type) {
2226 case FR_TYPE_STRUCTURAL:
2227 if (!fr_pair_validate_relaxed(failed, &check->vp_group, &match->vp_group)) goto mismatch;
2228 break;
2229
2230 default:
2231 /*
2232 * This attribute passed the filter
2233 */
2234 if (!fr_pair_cmp(check, match)) {
2235 mismatch:
2236 if (failed) {
2237 failed[0] = check;
2238 failed[1] = match;
2239 }
2240 return false;
2241 }
2242 break;
2243 }
2244 }
2245 }
2246
2247 return true;
2248}
2249
2250/**
2251 *
2252 * @param[in] vp the pair to check
2253 * @return
2254 * - true the pair is immutable, or has an immutable child
2255 * - false the pair is not immutable, or has no immutable children.
2256 */
2258{
2259 if (fr_type_is_leaf(vp->vp_type)) return vp->vp_immutable;
2260
2262
2263 fr_pair_list_foreach(&vp->vp_group, child) {
2264 if (fr_type_is_leaf(child->vp_type)) {
2265 if (child->vp_immutable) return true;
2266
2267 continue;
2268 }
2269
2270 fr_assert(fr_type_is_structural(child->vp_type));
2271
2272 if (fr_pair_immutable(child)) return true;
2273 }
2274
2275 return false;
2276}
2277
2278/** Steal a list of pairs to a new context
2279 *
2280 */
2281void fr_pair_list_steal(TALLOC_CTX *ctx, fr_pair_list_t *list)
2282{
2283 fr_pair_list_foreach(list, vp) {
2284 (void) fr_pair_steal(ctx, vp);
2285 }
2286}
2287
2288/** Duplicate a list of pairs
2289 *
2290 * Copy all pairs from 'from' regardless of tag, attribute or vendor.
2291 *
2292 * @param[in] ctx for new #fr_pair_t (s) to be allocated in.
2293 * @param[in] to where to copy attributes to.
2294 * @param[in] from whence to copy #fr_pair_t (s).
2295 * @return
2296 * - >0 the number of attributes copied.
2297 * - 0 if no attributes copied.
2298 * - -1 on error.
2299 */
2300int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
2301{
2302 fr_pair_t *new_vp, *first_added = NULL;
2303 int cnt = 0;
2304
2305 fr_pair_list_foreach(from, vp) {
2306 cnt++;
2308
2309 new_vp = fr_pair_copy(ctx, vp);
2310 if (!new_vp) {
2311 fr_pair_order_list_talloc_free_to_tail(&to->order, first_added);
2312 return -1;
2313 }
2314
2315 if (!first_added) first_added = new_vp;
2316 fr_pair_append(to, new_vp);
2317 }
2318
2319 return cnt;
2320}
2321
2322
2323/** Copy the contents of a pair list to a set of value-boxes
2324 *
2325 * This function should be removed when the xlats use dcursors
2326 * of copying all of the boxes.
2327 *
2328 * @param[in] dst where boxes will be created
2329 * @param[in] from whence to copy #fr_pair_t (s).
2330 * @return
2331 * - >0 the number of boxes copied.
2332 * - 0 if no boxes copied.
2333 * - -1 on error.
2334 */
2336{
2337 int cnt = 0;
2338 fr_value_box_t *value, *first_added = NULL;
2339
2340 fr_assert(dst->type == FR_TYPE_GROUP);
2341
2342 fr_pair_list_foreach(from, vp) {
2343 cnt++;
2345
2346 if (fr_type_is_structural(vp->vp_type)) {
2348 if (!value) goto fail;
2349
2350 if (fr_pair_list_copy_to_box(value, &vp->vp_group) < 0) {
2352 goto fail;
2353 }
2354
2355 } else {
2356 value = fr_value_box_alloc(dst, vp->vp_type, vp->da);
2357 if (!value) {
2358 fail:
2359 fr_value_box_list_talloc_free_to_tail(&dst->vb_group, first_added);
2360 return -1;
2361 }
2362 if (unlikely(fr_value_box_copy(value, value, &vp->data) < 0)) {
2364 goto fail;
2365 }
2366 }
2367
2368 if (!first_added) first_added = value;
2369 fr_value_box_list_insert_tail(&dst->vb_group, value);
2370 }
2371
2372 return cnt;
2373}
2374
2375/** Duplicate pairs in a list matching the specified da
2376 *
2377 * Copy all pairs from 'from' matching the specified da.
2378 *
2379 * @param[in] ctx for new #fr_pair_t (s) to be allocated in.
2380 * @param[in] to where to copy attributes to.
2381 * @param[in] from whence to copy #fr_pair_t (s).
2382 * @param[in] da to match.
2383 * @return
2384 * - >0 the number of attributes copied.
2385 * - 0 if no attributes copied.
2386 * - -1 on error.
2387 */
2389 fr_pair_list_t const *from, fr_dict_attr_t const *da)
2390{
2391 fr_pair_t *new_vp;
2392 unsigned int cnt = 0;
2393 fr_pair_list_t new_list;
2394
2395 fr_pair_list_init(&new_list);
2396
2397 fr_pair_list_foreach(from, vp) {
2399
2400 if (vp->da != da) continue;
2401
2402 cnt++;
2403 new_vp = fr_pair_copy(ctx, vp);
2404 if (!new_vp) {
2405 fr_pair_list_free(&new_list);
2406 return -1;
2407 }
2408
2409 fr_pair_append(&new_list, new_vp);
2410 }
2411
2412 fr_pair_list_append(to, &new_list);
2413
2414 return cnt;
2415}
2416
2417/** Duplicate pairs in a list where the da is a descendant of parent_da
2418 *
2419 * Copy all pairs from 'from' which are descendants of the specified 'parent_da'.
2420 * This is particularly useful for copying attributes of a particular vendor, where the vendor
2421 * da is passed as parent_da.
2422 *
2423 * @param[in] ctx for new #fr_pair_t (s) to be allocated in.
2424 * @param[in] to where to copy attributes to.
2425 * @param[in] from whence to copy #fr_pair_t (s).
2426 * @param[in] parent_da to match.
2427 * @return
2428 * - >0 one or more attributes were copied
2429 * - 0 if no attributes copied.
2430 * - -1 on error.
2431 */
2433 fr_pair_list_t const *from, fr_dict_attr_t const *parent_da)
2434{
2435 fr_pair_t *tlv;
2436 bool found = false;
2437 fr_pair_list_t list;
2438
2439 if (!fr_type_is_structural(parent_da->type)) return -1;
2440
2441 /*
2442 * Allow for nested attributes.
2443 */
2444 tlv = fr_pair_find_by_da(from, NULL, parent_da);
2445 if (tlv) {
2446 fr_pair_t *vp;
2447
2448 vp = fr_pair_copy(ctx, tlv);
2449 if (!vp) return -1;
2450
2451 fr_pair_append(to, vp);
2452
2453 return 1;
2454 }
2455
2456 fr_pair_list_init(&list);
2457
2458 fr_pair_list_foreach(from, vp) {
2459 fr_pair_t *new_vp;
2460
2461 if (!fr_dict_attr_common_parent(parent_da, vp->da, true)) continue;
2462
2463 new_vp = fr_pair_copy(ctx, vp);
2464 if (unlikely(!new_vp)) {
2465 fr_pair_list_free(&list);
2466 return -1;
2467 }
2468
2469 fr_pair_append(&list, new_vp);
2470 found = true;
2471 }
2472
2473 fr_pair_list_append(to, &list);
2474
2475 return found;
2476}
2477
2478/** Free/zero out value (or children) of a given VP
2479 *
2480 * @param[in] vp to clear value from.
2481 */
2483{
2484 fr_pair_t *child;
2485
2486 switch (vp->vp_type) {
2487 default:
2489 break;
2490
2491 case FR_TYPE_STRUCTURAL:
2492 if (fr_pair_list_empty(&vp->vp_group)) return;
2493
2494 while ((child = fr_pair_order_list_pop_tail(&vp->vp_group.order))) {
2495 fr_pair_value_clear(child);
2496 talloc_free(child);
2497 }
2498 break;
2499 }
2500}
2501
2502/** Copy the value from one pair to another
2503 *
2504 * @param[out] dst where to copy the value to.
2505 * will clear assigned value.
2506 * @param[in] src where to copy the value from
2507 * Must have an assigned value.
2508 * @return
2509 * - 0 on success.
2510 * - -1 on failure.
2511 */
2513{
2514#ifdef WITH_VERIFY_PTR
2515 char const *file = dst->data.file; /* copying the value-box also copies these fields */
2516 int line = dst->data.line;
2517#endif
2518
2519 if (!fr_cond_assert(src->data.type != FR_TYPE_NULL)) return -1;
2520
2521 fr_value_box_clear_value(&dst->data);
2522 if (unlikely(fr_value_box_copy(dst, &dst->data, &src->data) < 0)) return -1;
2523
2524 /*
2525 * If either source or destination is secret, then this value is secret.
2526 */
2527 if (src->da->flags.secret || dst->da->flags.secret || fr_value_box_is_secret(&src->data)) {
2528 fr_value_box_set_secret(&dst->data, true);
2529 }
2530#ifdef WITH_VERIFY_PTR
2531 dst->data.file = file;
2532 dst->data.line = line;
2533#endif
2534 return 0;
2535}
2536
2537/** Convert string value to native attribute value
2538 *
2539 * @param[in] vp to assign value to.
2540 * @param[in] value string to convert. Binary safe for variable
2541 * length values if len is provided.
2542 * @param[in] inlen The length of the input string.
2543 * @param[in] uerules used to perform unescaping.
2544 * @param[in] tainted Whether the value came from a trusted source.
2545 * @return
2546 * - 0 on success.
2547 * - -1 on failure.
2548 */
2550 fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
2551{
2552 /*
2553 * This is not yet supported because the rest of the APIs
2554 * to parse pair names, etc. don't yet enforce "inlen".
2555 * This is likely not a problem in practice, but we
2556 * haven't yet audited the uses of this function for that
2557 * behavior.
2558 */
2559 switch (vp->vp_type) {
2560 case FR_TYPE_STRUCTURAL:
2561 fr_strerror_printf("Attributes of type '%s' are not yet supported",
2562 fr_type_to_str(vp->vp_type));
2563 return -1;
2564
2565 default:
2566 break;
2567 }
2568
2569 /*
2570 * We presume that the input data is from a double quoted
2571 * string, and needs unescaping
2572 */
2573 if (fr_value_box_from_str(vp, &vp->data, vp->vp_type, vp->da,
2574 value, inlen,
2575 uerules) < 0) return -1;
2576
2578
2579 PAIR_VERIFY(vp);
2580
2581 return 0;
2582}
2583
2584/** Copy data into an "string" data type.
2585 *
2586 * @note vp->da must be of type FR_TYPE_STRING.
2587 *
2588 * @param[in,out] vp to update
2589 * @param[in] src data to copy
2590 * @param[in] tainted Whether the value came from a trusted source.
2591 * @return
2592 * - 0 on success.
2593 * - -1 on failure.
2594 */
2595int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
2596{
2597 int ret;
2598
2599 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2600
2601 fr_value_box_clear(&vp->data); /* Free any existing buffers */
2602 ret = fr_value_box_strdup(vp, &vp->data, vp->da, src, tainted);
2603 if (ret == 0) {
2604 PAIR_VERIFY(vp);
2605 }
2606
2607 return ret;
2608}
2609
2610/** Assign a buffer containing a nul terminated string to a vp, but don't copy it
2611 *
2612 * @param[in] vp to assign string to.
2613 * @param[in] src to copy string from.
2614 * @param[in] tainted Whether the value came from a trusted source.
2615 * @return
2616 * - 0 on success.
2617 * - -1 on failure.
2618 */
2619int fr_pair_value_strdup_shallow(fr_pair_t *vp, char const *src, bool tainted)
2620{
2621 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2622
2623 fr_value_box_clear(&vp->data);
2624 fr_value_box_strdup_shallow(&vp->data, vp->da, src, tainted);
2625
2626 PAIR_VERIFY(vp);
2627
2628 return 0;
2629}
2630
2631/** Trim the length of the string buffer to match the length of the C string
2632 *
2633 * @param[in,out] vp to trim.
2634 * @return
2635 * - 0 on success.
2636 * - -1 on failure.
2637 */
2639{
2640 int ret;
2641
2642 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2643
2644 ret = fr_value_box_strtrim(vp, &vp->data);
2645 if (ret == 0) {
2646 PAIR_VERIFY(vp);
2647 }
2648
2649 return ret;
2650}
2651
2652/** Print data into an "string" data type.
2653 *
2654 * @note vp->da must be of type FR_TYPE_STRING.
2655 *
2656 * @param[in,out] vp to update
2657 * @param[in] fmt the format string
2658 */
2659int fr_pair_value_aprintf(fr_pair_t *vp, char const *fmt, ...)
2660{
2661 int ret;
2662 va_list ap;
2663
2664 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2665
2666 fr_value_box_clear(&vp->data);
2667 va_start(ap, fmt);
2668 ret = fr_value_box_vasprintf(vp, &vp->data, vp->da, false, fmt, ap);
2669 va_end(ap);
2670
2671 if (ret == 0) {
2672 PAIR_VERIFY(vp);
2673 }
2674
2675 return ret;
2676}
2677
2678/** Pre-allocate a memory buffer for a "string" type value pair
2679 *
2680 * @note Will clear existing values (including buffers).
2681 *
2682 * @param[in,out] vp to update
2683 * @param[out] out If non-null will be filled with a pointer to the
2684 * new buffer.
2685 * @param[in] size of the data.
2686 * @param[in] tainted Whether the value came from a trusted source.
2687 * @return
2688 * - 0 on success.
2689 * - -1 on failure.
2690 */
2691int fr_pair_value_bstr_alloc(fr_pair_t *vp, char **out, size_t size, bool tainted)
2692{
2693 int ret;
2694
2695 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2696
2697 fr_value_box_clear(&vp->data); /* Free any existing buffers */
2698 ret = fr_value_box_bstr_alloc(vp, out, &vp->data, vp->da, size, tainted);
2699 if (ret == 0) {
2700 PAIR_VERIFY(vp);
2701 }
2702
2703 return ret;
2704}
2705
2706/** Change the length of a buffer for a "string" type value pair
2707 *
2708 * @param[in,out] vp to update
2709 * @param[out] out If non-null will be filled with a pointer to the
2710 * new buffer.
2711 * @param[in] size of the data.
2712 * @return
2713 * - 0 on success.
2714 * - -1 on failure.
2715 */
2716int fr_pair_value_bstr_realloc(fr_pair_t *vp, char **out, size_t size)
2717{
2718 int ret;
2719
2720 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2721
2722 ret = fr_value_box_bstr_realloc(vp, out, &vp->data, size);
2723 if (ret == 0) {
2724 PAIR_VERIFY(vp);
2725 }
2726
2727 return ret;
2728}
2729
2730/** Copy data into a "string" type value pair
2731 *
2732 * @note This API will copy binary data, including embedded '\0'
2733 *
2734 * @note vp->da must be of type FR_TYPE_STRING.
2735 *
2736 * @param[in,out] vp to update.
2737 * @param[in] src data to copy.
2738 * @param[in] len of data to copy.
2739 * @param[in] tainted Whether the value came from a trusted source.
2740 * @return
2741 * - 0 on success.
2742 * - -1 on failure.
2743 */
2744int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
2745{
2746 int ret;
2747
2748 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2749
2750 fr_value_box_clear(&vp->data);
2751 ret = fr_value_box_bstrndup(vp, &vp->data, vp->da, src, len, tainted);
2752 if (ret == 0) {
2753 PAIR_VERIFY(vp);
2754 }
2755
2756 return ret;
2757}
2758
2759/** Copy a nul terminated talloced buffer a "string" type value pair
2760 *
2761 * The buffer must be \0 terminated, or an error will be returned.
2762 *
2763 * @param[in,out] vp to update.
2764 * @param[in] src a talloced nul terminated buffer.
2765 * @param[in] tainted Whether the value came from a trusted source.
2766 * @return
2767 * - 0 on success.
2768 * - -1 on failure.
2769 */
2770int fr_pair_value_bstrdup_buffer(fr_pair_t *vp, char const *src, bool tainted)
2771{
2772 int ret;
2773
2774 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2775
2776 fr_value_box_clear(&vp->data);
2777 ret = fr_value_box_bstrdup_buffer(vp, &vp->data, vp->da, src, tainted);
2778 if (ret == 0) {
2779 PAIR_VERIFY(vp);
2780 }
2781
2782 return ret;
2783}
2784
2785/** Assign a string to a "string" type value pair
2786 *
2787 * @param[in] vp to assign new buffer to.
2788 * @param[in] src a string.
2789 * @param[in] len of src.
2790 * @param[in] tainted Whether the value came from a trusted source.
2791 * @return
2792 * - 0 on success.
2793 * - -1 on failure.
2794 */
2795int fr_pair_value_bstrndup_shallow(fr_pair_t *vp, char const *src, size_t len, bool tainted)
2796{
2797 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2798
2799 fr_value_box_clear(&vp->data);
2800 fr_value_box_bstrndup_shallow(&vp->data, vp->da, src, len, tainted);
2801 PAIR_VERIFY(vp);
2802
2803 return 0;
2804}
2805
2806/** Assign a string to a "string" type value pair
2807 *
2808 * @param[in] vp to assign new buffer to.
2809 * @param[in] src a string.
2810 * @param[in] tainted Whether the value came from a trusted source.
2811 * @return
2812 * - 0 on success.
2813 * - -1 on failure.
2814 */
2815int fr_pair_value_bstrdup_buffer_shallow(fr_pair_t *vp, char const *src, bool tainted)
2816{
2817 int ret;
2818
2819 if (!fr_cond_assert(vp->vp_type == FR_TYPE_STRING)) return -1;
2820
2821 fr_value_box_clear(&vp->data);
2822 ret = fr_value_box_bstrdup_buffer_shallow(NULL, &vp->data, vp->da, src, tainted);
2823 if (ret == 0) {
2824 PAIR_VERIFY(vp);
2825 }
2826
2827 return ret;
2828}
2829
2830/** Pre-allocate a memory buffer for a "octets" type value pair
2831 *
2832 * @note Will clear existing values (including buffers).
2833 *
2834 * @param[in,out] vp to update
2835 * @param[out] out If non-null will be filled with a pointer to the
2836 * new buffer.
2837 * @param[in] size of the data.
2838 * @param[in] tainted Whether the value came from a trusted source.
2839 * @return
2840 * - 0 on success.
2841 * - -1 on failure.
2842 */
2843int fr_pair_value_mem_alloc(fr_pair_t *vp, uint8_t **out, size_t size, bool tainted)
2844{
2845 int ret;
2846
2847 if (!fr_cond_assert(vp->vp_type == FR_TYPE_OCTETS)) return -1;
2848
2849 fr_value_box_clear(&vp->data); /* Free any existing buffers */
2850 ret = fr_value_box_mem_alloc(vp, out, &vp->data, vp->da, size, tainted);
2851 if (ret == 0) {
2852 PAIR_VERIFY(vp);
2853 }
2854
2855 return ret;
2856}
2857
2858/** Change the length of a buffer for a "octets" type value pair
2859 *
2860 * @param[in,out] vp to update
2861 * @param[out] out If non-null will be filled with a pointer to the
2862 * new buffer.
2863 * @param[in] size of the data.
2864 * @return
2865 * - 0 on success.
2866 * - -1 on failure.
2867 */
2869{
2870 int ret;
2871
2872 if (!fr_cond_assert(vp->vp_type == FR_TYPE_OCTETS)) return -1;
2873
2874 ret = fr_value_box_mem_realloc(vp, out, &vp->data, size);
2875 if (ret == 0) {
2876 PAIR_VERIFY(vp);
2877 }
2878
2879 return ret;
2880}
2881
2882/** Copy data into an "octets" data type.
2883 *
2884 * @note Will clear existing values (including buffers).
2885 *
2886 * @param[in,out] vp to update
2887 * @param[in] src data to copy
2888 * @param[in] len of the data.
2889 * @param[in] tainted Whether the value came from a trusted source.
2890 * @return
2891 * - 0 on success.
2892 * - -1 on failure.
2893 */
2894int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
2895{
2896 int ret;
2897
2898 if (unlikely((len > 0) && !src)) {
2899 fr_strerror_printf("Invalid arguments to %s. Len > 0 (%zu) but src was NULL",
2900 __FUNCTION__, len);
2901 return -1;
2902 }
2903
2904 if (!fr_cond_assert(vp->vp_type == FR_TYPE_OCTETS)) return -1;
2905
2906 fr_value_box_clear_value(&vp->data); /* Free any existing buffers */
2907 ret = fr_value_box_memdup(vp, &vp->data, vp->da, src, len, tainted);
2908 if (ret == 0) PAIR_VERIFY(vp);
2909
2910 return ret;
2911}
2912
2913/** Copy data from a talloced buffer into an "octets" data type.
2914 *
2915 * @note Will clear existing values (including buffers).
2916 *
2917 * @param[in,out] vp to update
2918 * @param[in] src data to copy
2919 * @param[in] tainted Whether the value came from a trusted source.
2920 * @return
2921 * - 0 on success.
2922 * - -1 on failure.
2923 */
2924int fr_pair_value_memdup_buffer(fr_pair_t *vp, uint8_t const *src, bool tainted)
2925{
2926 int ret;
2927
2928 if (!fr_cond_assert(vp->vp_type == FR_TYPE_OCTETS)) return -1;
2929
2930 fr_value_box_clear(&vp->data); /* Free any existing buffers */
2931 ret = fr_value_box_memdup_buffer(vp, &vp->data, vp->da, src, tainted);
2932 if (ret == 0) {
2933 PAIR_VERIFY(vp);
2934 }
2935
2936 return ret;
2937}
2938
2939/** Assign a buffer to a "octets" type value pair
2940 *
2941 * @param[in] vp to assign new buffer to.
2942 * @param[in] src data to copy.
2943 * @param[in] len of src.
2944 * @param[in] tainted Whether the value came from a trusted source.
2945 * @return
2946 * - 0 on success.
2947 * - -1 on failure.
2948 */
2949int fr_pair_value_memdup_shallow(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
2950{
2951 if (!fr_cond_assert(vp->vp_type == FR_TYPE_OCTETS)) return -1;
2952
2953 fr_value_box_clear(&vp->data);
2954 fr_value_box_memdup_shallow(&vp->data, vp->da, src, len, tainted);
2955 PAIR_VERIFY(vp);
2956
2957 return 0;
2958}
2959
2960/** Assign a talloced buffer to a "octets" type value pair
2961 *
2962 * @param[in] vp to assign new buffer to.
2963 * @param[in] src data to copy.
2964 * @param[in] tainted Whether the value came from a trusted source.
2965 * @return
2966 * - 0 on success.
2967 * - -1 on failure.
2968 */
2970{
2971 if (!fr_cond_assert(vp->vp_type == FR_TYPE_OCTETS)) return -1;
2972
2973 fr_value_box_clear(&vp->data);
2974 fr_value_box_memdup_buffer_shallow(NULL, &vp->data, vp->da, src, tainted);
2975 PAIR_VERIFY(vp);
2976
2977 return 0;
2978}
2979
2980
2981/** Return a const buffer for an enum type attribute
2982 *
2983 * Where the vp type is numeric but does not have any enumv, or its value
2984 * does not map to an enumv, the integer value of the pair will be printed
2985 * to buff, and a pointer to buff will be returned.
2986 *
2987 * @param[in] vp to print.
2988 * @param[in] buff to print integer value to.
2989 * @return a talloced buffer.
2990 */
2991char const *fr_pair_value_enum(fr_pair_t const *vp, char buff[20])
2992{
2993 fr_dict_enum_value_t const *enumv;
2994
2995 if (!fr_box_is_numeric(&vp->data)) {
2996 fr_strerror_printf("Pair %s is not numeric", vp->da->name);
2997 return NULL;
2998 }
2999
3000 if (vp->da->flags.has_value) switch (vp->vp_type) {
3001 case FR_TYPE_BOOL:
3002 return vp->vp_bool ? "yes" : "no";
3003
3004 default:
3005 enumv = fr_dict_enum_by_value(vp->da, &vp->data);
3006 if (enumv) return enumv->name;
3007 break;
3008 }
3009
3011 return buff;
3012}
3013
3014/** Get value box of a VP, optionally prefer enum value.
3015 *
3016 * Get the data value box of the given VP. If 'e' is set to 1 and the VP has an
3017 * enum value, this will be returned instead. Otherwise it will be set to the
3018 * value box of the VP itself.
3019 *
3020 * @param[out] out pointer to a value box.
3021 * @param[in] vp to print.
3022 * @return 1 if the enum value has been used, 0 otherwise, -1 on error.
3023 */
3025{
3026 fr_dict_enum_value_t const *dv;
3027
3028 if (vp->da && vp->da->flags.has_value &&
3029 (dv = fr_dict_enum_by_value(vp->da, &vp->data))) {
3030 *out = dv->value;
3031 return 1;
3032 }
3033
3034 *out = &vp->data;
3035 return 0;
3036}
3037
3038#ifdef WITH_VERIFY_PTR
3039#define PAIR_NAME "fr_pair_t %s (from %s:%d)"
3040#define PAIR_NAME_LOCATION(_x) (_x)->da->name, (_x)->data.file ? (_x)->data.file : "", (_x)->data.line
3041
3042/*
3043 * Verify a fr_pair_t
3044 */
3045void fr_pair_verify(char const *file, int line, fr_dict_attr_t const *parent_da,
3046 fr_pair_list_t const *list, fr_pair_t const *vp, bool verify_values)
3047{
3049
3050 if (!vp->da) {
3051 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: fr_pair_t da pointer was NULL", file, line);
3052 }
3053
3054 fr_dict_attr_verify(file, line, vp->da);
3055
3056
3057 /*
3058 * Enforce correct parentage. If the parent exists, AND it's not a group (because groups break
3059 * the strict hierarchy), then check parentage.
3060 *
3061 * We also ignore parentage if either the expected parent or the vp is raw / unknown. We may
3062 * want to tighten that a little bit, as there are cases where we create raw / unknown
3063 * attributes, and the parent is also raw / unknown. In which case the parent_da _should_ be the
3064 * same as vp->da->parent.
3065 */
3066 if (parent_da && (parent_da->type != FR_TYPE_GROUP) &&
3067 !parent_da->flags.is_raw && !parent_da->flags.is_unknown &&
3068 !vp->da->flags.is_raw && !vp->da->flags.is_unknown) {
3069 fr_fatal_assert_msg(vp->da->parent == parent_da,
3070 "CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " does not have the correct parentage - "
3071 "expected parent da %s, found different parent da %s",
3072 file, line,
3073 PAIR_NAME_LOCATION(vp), parent_da->name, vp->da->parent->name);
3074 }
3075
3076 if (list) {
3077 fr_fatal_assert_msg(fr_pair_order_list_parent(vp) == &list->order,
3078 "CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " does not have the correct parentage",
3079 file, line,
3080 PAIR_NAME_LOCATION(vp));
3081 }
3082
3083 /*
3084 * This field is only valid for non-structural pairs
3085 */
3086 if (!fr_type_is_structural(vp->vp_type)) {
3088
3089 if (vp->data.enumv) fr_dict_attr_verify(file, line, vp->data.enumv);
3090
3091 if (parent && !fr_dict_attr_can_contain(parent->da, vp->da)) {
3092 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " should be parented by da %s, but is parented by da %s",
3093 file, line,
3094 PAIR_NAME_LOCATION(vp), vp->da->parent->name, parent->da->name);
3095 }
3096
3097 /*
3098 * The data types have to agree, except for comb-ip and combo-ipaddr.
3099 */
3100 if (vp->vp_type != vp->da->type) switch (vp->da->type) {
3102 if ((vp->vp_type == FR_TYPE_IPV4_ADDR) ||
3103 (vp->vp_type == FR_TYPE_IPV6_ADDR)) {
3104 break;
3105 }
3106 goto failed_type;
3107
3109 if ((vp->vp_type == FR_TYPE_IPV4_PREFIX) ||
3110 (vp->vp_type == FR_TYPE_IPV6_PREFIX)) {
3111 break;
3112 }
3114
3115 default:
3116 failed_type:
3117 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " has value of data type '%s', which disagrees with the dictionary data type '%s'",
3118 file, line,
3119 PAIR_NAME_LOCATION(vp), fr_type_to_str(vp->vp_type), fr_type_to_str(vp->da->type));
3120 }
3121
3122 /*
3123 * We would like to enable this, but there's a
3124 * lot of code like fr_pair_append_by_da() which
3125 * creates the #fr_pair_t with no value.
3126 */
3127 if (verify_values) fr_value_box_verify(file, line, &vp->data);
3128
3129 } else {
3131
3132 if (parent && (parent->vp_type != FR_TYPE_GROUP) && (parent->da == vp->da)) {
3133 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " structural (non-group) type contains itself",
3134 file, line,
3135 PAIR_NAME_LOCATION(vp));
3136 }
3137
3138 fr_pair_list_verify(file, line, vp, &vp->vp_group, verify_values);
3139 }
3140
3141 switch (vp->vp_type) {
3142 case FR_TYPE_OCTETS:
3143 {
3144 size_t len;
3145 TALLOC_CTX *parent;
3146
3147 if (!vp->vp_octets) break; /* We might be in the middle of initialisation */
3148
3149 if (!talloc_get_type(vp->vp_ptr, uint8_t)) {
3150 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " data buffer type should be "
3151 "uint8_t but is %s",
3152 file, line,
3153 PAIR_NAME_LOCATION(vp), talloc_get_name(vp->vp_ptr));
3154 }
3155
3156 len = talloc_array_length(vp->vp_octets);
3157 if (vp->vp_length > len) {
3158 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " length %zu is greater than "
3159 "uint8_t data buffer length %zu",
3160 file, line,
3161 PAIR_NAME_LOCATION(vp), vp->vp_length, len);
3162 }
3163
3164 parent = talloc_parent(vp->vp_ptr);
3165 if (parent != vp) {
3166 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " char buffer is not "
3167 "parented by fr_pair_t %p, instead parented by %p (%s)",
3168 file, line,
3169 PAIR_NAME_LOCATION(vp), vp,
3170 parent, parent ? talloc_get_name(parent) : "NULL");
3171 }
3172 }
3173 break;
3174
3175 case FR_TYPE_STRING:
3176 {
3177 size_t len;
3178 TALLOC_CTX *parent;
3179
3180 if (!vp->vp_octets) break; /* We might be in the middle of initialisation */
3181
3182 if (!talloc_get_type(vp->vp_ptr, char)) {
3183 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " data buffer type should be "
3184 "char but is %s",
3185 file, line,
3186 PAIR_NAME_LOCATION(vp), talloc_get_name(vp->vp_ptr));
3187 }
3188
3189 len = (talloc_strlen(vp->vp_strvalue));
3190 if (vp->vp_length > len) {
3191 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " length %zu is greater than "
3192 "char buffer length %zu",
3193 file, line,
3194 PAIR_NAME_LOCATION(vp), vp->vp_length, len);
3195 }
3196
3197 if (vp->vp_strvalue[vp->vp_length] != '\0') {
3198 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " char buffer not \\0 "
3199 "terminated",
3200 file, line,
3201 PAIR_NAME_LOCATION(vp));
3202 }
3203
3204 parent = talloc_parent(vp->vp_ptr);
3205 if (parent != vp) {
3206 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " char buffer is not "
3207 "parented by fr_pair_t %p, instead parented by %p (%s)",
3208 file, line,
3209 PAIR_NAME_LOCATION(vp), vp,
3210 parent, parent ? talloc_get_name(parent) : "NULL");
3211 }
3212 }
3213 break;
3214
3215 case FR_TYPE_IPV4_ADDR:
3216 if (vp->vp_ip.af != AF_INET) {
3217 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " address family is not "
3218 "set correctly for IPv4 address. Expected %i got %i",
3219 file, line,
3220 PAIR_NAME_LOCATION(vp),
3221 AF_INET, vp->vp_ip.af);
3222 }
3223 if (vp->vp_ip.prefix != 32) {
3224 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " address prefix "
3225 "not set correctly for IPv4 address. Expected %i got %i",
3226 file, line,
3227 PAIR_NAME_LOCATION(vp),
3228 32, vp->vp_ip.prefix);
3229 }
3230 break;
3231
3232 case FR_TYPE_IPV6_ADDR:
3233 if (vp->vp_ip.af != AF_INET6) {
3234 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " address family is not "
3235 "set correctly for IPv6 address. Expected %i got %i",
3236 file, line,
3237 PAIR_NAME_LOCATION(vp),
3238 AF_INET6, vp->vp_ip.af);
3239 }
3240 if (vp->vp_ip.prefix != 128) {
3241 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " address prefix "
3242 "set correctly for IPv6 address. Expected %i got %i",
3243 file, line,
3244 PAIR_NAME_LOCATION(vp),
3245 128, vp->vp_ip.prefix);
3246 }
3247 break;
3248
3249 case FR_TYPE_ATTR:
3250 if (!vp->vp_attr) {
3251 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " attribute pointer is NULL",
3252 file, line,
3253 PAIR_NAME_LOCATION(vp));
3254 }
3255 break;
3256
3257 case FR_TYPE_STRUCTURAL:
3258 {
3259 if (vp->vp_group.verified) break;
3260
3261 fr_pair_list_foreach(&vp->vp_group, child) {
3262 TALLOC_CTX *parent = talloc_parent(child);
3263
3265 "CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " should be parented "
3266 "by " PAIR_NAME ". Expected talloc parent %p (%s) got %p (%s)",
3267 file, line,
3268 PAIR_NAME_LOCATION(child), PAIR_NAME_LOCATION(vp),
3269 vp, talloc_get_name(vp),
3270 parent, talloc_get_name(parent));
3271
3272 /*
3273 * Check if the child can be in the parent.
3274 */
3276 "CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " should be parented "
3277 "by da %s, but it is instead parented by da %s",
3278 file, line,
3279 PAIR_NAME_LOCATION(child), child->da->parent->name, vp->da->name);
3280
3281 fr_pair_verify(file, line, vp->da, &vp->vp_group, child, verify_values);
3282 }
3283
3284 UNCONST(fr_pair_t *, vp)->vp_group.verified = true;
3285 }
3286 break;
3287
3288 default:
3289 break;
3290 }
3291
3292 if (vp->da->flags.is_unknown || vp->vp_raw) {
3294
3295 /*
3296 * Raw or unknown attributes can have specific data types. See DER and CBOR.
3297 */
3298
3299 } else if (fr_type_is_leaf(vp->vp_type) && (vp->vp_type != vp->da->type) &&
3300 !((vp->da->type == FR_TYPE_COMBO_IP_ADDR) && ((vp->vp_type == FR_TYPE_IPV4_ADDR) || (vp->vp_type == FR_TYPE_IPV6_ADDR))) &&
3301 !((vp->da->type == FR_TYPE_COMBO_IP_PREFIX) && ((vp->vp_type == FR_TYPE_IPV4_PREFIX) || (vp->vp_type == FR_TYPE_IPV6_PREFIX)))) {
3302 char data_type_int[10], da_type_int[10];
3303
3304 snprintf(data_type_int, sizeof(data_type_int), "%u", vp->vp_type);
3305 snprintf(da_type_int, sizeof(da_type_int), "%u", vp->da->type);
3306
3307 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: " PAIR_NAME " attribute "
3308 "data type (%s) does not match da type (%s)",
3309 file, line,
3310 PAIR_NAME_LOCATION(vp),
3311 fr_table_str_by_value(fr_type_table, vp->vp_type, data_type_int),
3312 fr_table_str_by_value(fr_type_table, vp->da->type, da_type_int));
3313 }
3314}
3315
3316/** Verify a pair list
3317 *
3318 * @param[in] file from which the verification is called
3319 * @param[in] line number in file
3320 * @param[in] expected talloc ctx pairs should have been allocated in
3321 * @param[in] list of fr_pair_ts to verify
3322 * @param[in] verify_values whether we verify the values, too.
3323 */
3324void fr_pair_list_verify(char const *file, int line, TALLOC_CTX const *expected, fr_pair_list_t const *list, bool verify_values)
3325{
3326 fr_pair_t *slow, *fast;
3327 TALLOC_CTX *parent;
3328
3329 if (fr_pair_list_empty(list)) return; /* Fast path */
3330
3331 /*
3332 * Only verify the list if it has been modified.
3333 */
3334 if (list->verified) return;
3335
3336 for (slow = fr_pair_list_head(list), fast = fr_pair_list_head(list);
3337 slow && fast;
3338 slow = fr_pair_list_next(list, slow), fast = fr_pair_list_next(list, fast)) {
3339 fr_pair_verify(__FILE__, __LINE__, NULL, list, slow, verify_values);
3340
3341 /*
3342 * Advances twice as fast as slow...
3343 */
3344 fast = fr_pair_list_next(list, fast);
3345 fr_fatal_assert_msg(fast != slow,
3346 "CONSISTENCY CHECK FAILED %s[%d]: Looping list found. Fast pointer hit "
3347 "slow pointer at \"%s\"",
3348 file, line, slow->da->name);
3349
3350 parent = talloc_parent(slow);
3351 if (expected && (parent != expected)) {
3352 bad_parent:
3353 fr_log_talloc_report(expected);
3355
3356 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%d]: Expected " PAIR_NAME " to be parented "
3357 "by %p (%s), instead parented by %p (%s)\n",
3358 file, line,
3359 PAIR_NAME_LOCATION(slow),
3360 expected, talloc_get_name(expected),
3361 parent, parent ? talloc_get_name(parent) : "NULL");
3362 }
3363 }
3364
3365 /*
3366 * Check the remaining pairs
3367 */
3368 for (; slow; slow = fr_pair_list_next(list, slow)) {
3369 fr_pair_verify(__FILE__, __LINE__, NULL, list, slow, verify_values);
3370
3371 parent = talloc_parent(slow);
3372 if (expected && (parent != expected)) goto bad_parent;
3373 }
3374
3375 UNCONST(fr_pair_list_t *, list)->verified = true;
3376}
3377#endif
3378
3379/** Mark up a list of VPs as tainted.
3380 *
3381 */
3383{
3384 if (fr_pair_list_empty(list)) return;
3385
3386 fr_pair_list_foreach(list, vp) {
3388
3389 switch (vp->vp_type) {
3390 case FR_TYPE_STRUCTURAL:
3391 fr_pair_list_tainted(&vp->vp_group);
3392 break;
3393
3394 default:
3395 break;
3396 }
3397
3398 vp->vp_tainted = true;
3399 }
3400}
3401
3402/** Evaluation function for matching if vp matches a given da
3403 *
3404 * @param item pointer to a fr_pair_t
3405 * @param uctx da to match
3406 *
3407 * @return true if the pair matches the da
3408 */
3409bool fr_pair_matches_da(void const *item, void const *uctx)
3410{
3411 fr_pair_t const *vp = item;
3412 fr_dict_attr_t const *da = uctx;
3413 return da == vp->da;
3414}
3415
3416/** Find or allocate a parent attribute.
3417 *
3418 * The input da is somewhere down inside of the da hierarchy. We
3419 * need to recursively find or create parent VPs which match the
3420 * given da.
3421 *
3422 * We find (or add) the VP into the "in" list. Any newly created VP
3423 * is inserted before "next". Or if "next==NULL", at the tail of
3424 * "in".
3425 *
3426 * @param[in] in the parent vp to look in
3427 * @param[in] item if we create a new vp, insert it before this item
3428 * @param[in] da look for vps in the parent which match this da
3429 * @return
3430 * - NULL on OOM
3431 * - parent vp we've found or allocated.
3432 */
3434{
3435 fr_pair_t *parent, *vp;
3436
3438
3439 /*
3440 * We're looking for a parent in the root of the
3441 * dictionary. Find the relevant VP in the current
3442 * container.
3443 *
3444 * If it's not found, allocate it, and insert it into the
3445 * list. Note that we insert it before the given "item"
3446 * vp so that we don't loop over the newly created pair
3447 * as we're processing the list.
3448 */
3449 if (da->flags.is_root || (da->parent == in->da)) {
3450 return in;
3451 }
3452
3453 /*
3454 * We're not at the root. Go find (or create) the parent
3455 * of this da.
3456 */
3457 parent = pair_alloc_parent(in, item, da->parent);
3458 if (!parent) return NULL;
3459
3460 /*
3461 * We have the parent attribute, maybe it already
3462 * contains the da we're looking for?
3463 */
3464 vp = fr_pair_find_by_da(&parent->vp_group, NULL, da);
3465 if (vp) return vp;
3466
3467 /*
3468 * Now that the entire set of parents has been created,
3469 * create the final VP. Make sure it's in the parent,
3470 * and return it.
3471 */
3472 vp = fr_pair_afrom_da(parent, da);
3473 if (!vp) return NULL;
3474
3475 /*
3476 * If we are at the root, and have been provided with
3477 * an entry to insert before, then do that.
3478 */
3479 if (item && da->parent->flags.is_root) {
3480 fr_pair_insert_before(&parent->vp_group, item, vp);
3481 } else {
3482 fr_pair_append(&parent->vp_group, vp);
3483 }
3484 return vp;
3485}
3486
3487/** Parse a list of VPs from a value box.
3488 *
3489 * @param[in] ctx to allocate new VPs in
3490 * @param[out] out list to add new pairs to
3491 * @param[in] dict to use in parsing
3492 * @param[in] box whose value is to be parsed
3493 */
3494void fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_t const *dict, fr_value_box_t *box)
3495{
3496 fr_pair_parse_t root, relative;
3497
3498 fr_assert(box->type == FR_TYPE_STRING);
3499
3500 root = (fr_pair_parse_t) {
3501 .ctx = ctx,
3502 .da = fr_dict_root(dict),
3503 .list = out,
3504 .dict = dict,
3505 .internal = fr_dict_internal(),
3506 .allow_crlf = true,
3507 .tainted = box->tainted,
3508 };
3509 relative = (fr_pair_parse_t) { };
3510
3511 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(box->vb_strvalue, box->vb_length)) < 0) {
3512 return;
3513 }
3514}
int const char * file
Definition acutest.h:702
va_end(args)
int n
Definition acutest.h:577
static int const char * fmt
Definition acutest.h:573
va_start(args, fmt)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define NDEBUG_UNUSED
Definition build.h:395
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#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:455
#define UNUSED
Definition build.h:384
fr_dict_t * dict
Definition common.c:31
static size_t min(size_t x, size_t y)
Definition dbuff.c:66
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
void *(* fr_dcursor_iter_t)(fr_dcursor_t *cursor, void *to_eval, void *uctx)
Callback for implementing custom iterators.
Definition dcursor.h:49
#define fr_dcursor_list_next(_cursor, _ptr)
Definition dcursor.h:810
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define fr_dcursor_list(_cursor)
Definition dcursor.h:812
static void * _fr_dcursor_init(fr_dcursor_t *cursor, fr_dlist_head_t const *head, fr_dcursor_iter_t iter, fr_dcursor_iter_t peek, void const *iter_uctx, fr_dcursor_insert_t insert, fr_dcursor_remove_t remove, void const *mod_uctx, bool is_const)
Setup a cursor to iterate over attribute items in dlists.
Definition dcursor.h:735
int fr_log_talloc_report(TALLOC_CTX const *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
Definition debug.c:975
#define fr_fatal_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:229
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:222
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2368
static fr_dict_attr_t * fr_dict_attr_unknown_copy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Definition dict.h:608
fr_dict_attr_t * fr_dict_attr_unknown_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Copy a known or unknown attribute to produce an unknown attribute with the specified name.
static fr_dict_attr_t * fr_dict_attr_unknown_raw_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
Definition dict.h:635
void fr_dict_attr_verify(char const *file, int line, fr_dict_attr_t const *da)
Definition dict_util.c:5207
bool fr_dict_attr_can_contain(fr_dict_attr_t const *parent, fr_dict_attr_t const *child)
See if a structural da is allowed to contain another da.
Definition dict_util.c:5297
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2720
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:281
void fr_dict_attr_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
fr_dict_enum_value_t const * fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition dict_util.c:3730
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:5036
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3668
char const * name
Enum name.
Definition dict.h:278
static fr_slen_t in
Definition dict.h:906
Value of an enumerated attribute.
Definition dict.h:277
Test enumeration values.
Definition dict_test.h:92
unsigned int offset
Positive offset from start of structure to fr_dlist_t.
Definition dlist.h:55
fr_dlist_t * next
Definition dlist.h:43
fr_dlist_t entry
Struct holding the head and tail of the list.
Definition dlist.h:52
Head of a doubly linked list.
Definition dlist.h:51
Definition dwarf.c:424
Definition dwarf.c:563
talloc_free(hp)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
fr_type_t
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
long int ssize_t
unsigned char uint8_t
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
void * memset_explicit(void *ptr, int ch, size_t len)
Definition missing.c:624
bool fr_pair_matches_da(void const *item, void const *uctx)
Evaluation function for matching if vp matches a given da.
Definition pair.c:3409
int fr_pair_insert_before(fr_pair_list_t *list, fr_pair_t *pos, fr_pair_t *to_add)
Add a VP before another VP.
Definition pair.c:1357
fr_pair_t * fr_pair_list_parent(fr_pair_list_t const *list)
Return a pointer to the parent pair which contains this list.
Definition pair.c:929
static int _pair_list_dcursor_remove(NDEBUG_UNUSED fr_dcursor_t *cursor, void *to_remove, UNUSED void *uctx)
Keep attr tree and sublists synced on cursor removal.
Definition pair.c:968
static void pair_init_from_da(fr_pair_t *vp, fr_dict_attr_t const *da)
Continue initialising an fr_pair_t assigning a da.
Definition pair.c:191
int fr_pair_value_memdup_buffer(fr_pair_t *vp, uint8_t const *src, bool tainted)
Copy data from a talloced buffer into an "octets" data type.
Definition pair.c:2924
fr_pair_t * fr_pair_afrom_da_depth_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da, unsigned int start)
Create a pair (and all intermediate parents), and append it to the list.
Definition pair.c:423
unsigned int fr_pair_count_by_da(fr_pair_list_t const *list, fr_dict_attr_t const *da)
Return the number of instances of a given da in the specified list.
Definition pair.c:685
void fr_pair_list_tainted(fr_pair_list_t *list)
Mark up a list of VPs as tainted.
Definition pair.c:3382
#define NOT_IN_THIS_LIST_MSG
Definition pair.c:552
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition pair.c:1417
int fr_pair_value_enum_box(fr_value_box_t const **out, fr_pair_t *vp)
Get value box of a VP, optionally prefer enum value.
Definition pair.c:3024
int fr_pair_value_aprintf(fr_pair_t *vp, char const *fmt,...)
Print data into an "string" data type.
Definition pair.c:2659
int fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list, and prune any empty branches.
Definition pair.c:1666
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:2300
int fr_pair_steal_prepend(TALLOC_CTX *list_ctx, fr_pair_list_t *list, fr_pair_t *vp)
Change a vp's talloc ctx and insert it into a new list.
Definition pair.c:586
fr_pair_t * fr_pair_root_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
A special allocation function which disables child autofree.
Definition pair.c:249
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition pair.c:2894
static void * _fr_pair_iter_next_value(fr_dcursor_t *cursor, void *current, UNUSED void *uctx)
Iterate over pairs.
Definition pair.c:1158
static void * fr_pair_iter_next_by_da(fr_dcursor_t *cursor, void *current, void *uctx)
Iterate over pairs with a specified da.
Definition pair.c:641
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, 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:783
fr_cmp_ret_t fr_pair_list_cmp(fr_pair_list_t const *a, fr_pair_list_t const *b)
Determine equality of two lists.
Definition pair.c:2028
void fr_pair_validate_debug(fr_pair_t const *failed[2])
Write an error to the library errorbuff detailing the mismatch.
Definition pair.c:2070
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2595
int fr_pair_value_bstrdup_buffer_shallow(fr_pair_t *vp, char const *src, bool tainted)
Assign a string to a "string" type value pair.
Definition pair.c:2815
void fr_pair_init_null(fr_pair_t *vp)
Initialise fields in an fr_pair_t without assigning a da.
Definition pair.c:150
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2549
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:907
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:708
fr_pair_t * fr_pair_find_by_child_num_idx(fr_pair_list_t const *list, fr_dict_attr_t const *parent, unsigned int attr, unsigned int idx)
Find the pair with the matching child attribute at a given index.
Definition pair.c:873
int fr_pair_cmp(fr_pair_t const *a, fr_pair_t const *b)
Compare two pairs, using the operator from "a".
Definition pair.c:1950
fr_pair_list_t * fr_pair_list_alloc(TALLOC_CTX *ctx)
Allocate a new pair list on the heap.
Definition pair.c:120
int fr_pair_value_bstrndup_shallow(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Assign a string to a "string" type value pair.
Definition pair.c:2795
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1298
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition pair.c:1642
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:915
fr_cmp_ret_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da.
Definition pair.c:1797
fr_pair_t * _fr_pair_dcursor_by_ancestor_init(fr_dcursor_t *cursor, fr_pair_list_t const *list, fr_dict_attr_t const *da, bool is_const)
Initialise a cursor that will return only attributes descended from the specified fr_dict_attr_t.
Definition pair.c:1120
static void pair_init_null(fr_pair_t *vp)
Initialise fields in an fr_pair_t without assigning a da.
Definition pair.c:136
void fr_pair_replace(fr_pair_list_t *list, fr_pair_t *to_replace, fr_pair_t *vp)
Replace a given VP.
Definition pair.c:1390
fr_pair_t * fr_pair_find_by_da_idx(fr_pair_list_t const *list, fr_dict_attr_t const *da, unsigned int idx)
Find a pair with a matching da at a given index.
Definition pair.c:755
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:291
static fr_cmp_ret_t pair_cmp_by_num(void const *a, void const *b)
Order attributes by their attribute number, and tag.
Definition pair.c:1845
static int _pair_list_dcursor_insert(fr_dcursor_t *cursor, void *to_insert, UNUSED void *uctx)
Keep attr tree and sublists synced on cursor insert.
Definition pair.c:946
int fr_pair_value_bstrdup_buffer(fr_pair_t *vp, char const *src, bool tainted)
Copy a nul terminated talloced buffer a "string" type value pair.
Definition pair.c:2770
int fr_pair_update_by_da_parent(fr_pair_t *parent, fr_pair_t **out, fr_dict_attr_t const *da)
Return the first fr_pair_t matching the fr_dict_attr_t or alloc a new fr_pair_t and its subtree (and ...
Definition pair.c:1548
int fr_pair_list_copy_by_ancestor(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from, fr_dict_attr_t const *parent_da)
Duplicate pairs in a list where the da is a descendant of parent_da.
Definition pair.c:2432
static int _fr_pair_free(fr_pair_t *vp)
Free a fr_pair_t.
Definition pair.c:70
bool fr_pair_validate(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list)
Uses fr_pair_cmp to verify all fr_pair_ts in list match the filter defined by check.
Definition pair.c:2105
int fr_pair_steal_append(TALLOC_CTX *list_ctx, fr_pair_list_t *list, fr_pair_t *vp)
Change a vp's talloc ctx and insert it into a new list.
Definition pair.c:563
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:47
static fr_pair_t * pair_alloc_parent(fr_pair_t *in, fr_pair_t *item, fr_dict_attr_t const *da)
Find or allocate a parent attribute.
Definition pair.c:3433
int fr_pair_value_mem_realloc(fr_pair_t *vp, uint8_t **out, size_t size)
Change the length of a buffer for a "octets" type value pair.
Definition pair.c:2868
fr_pair_t * fr_pair_find_last_by_da(fr_pair_list_t const *list, fr_dict_attr_t const *da)
Find the last pair with a matching da.
Definition pair.c:731
char const * fr_pair_value_enum(fr_pair_t const *vp, char buff[20])
Return a const buffer for an enum type attribute.
Definition pair.c:2991
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2744
int fr_pair_value_bstr_alloc(fr_pair_t *vp, char **out, size_t size, bool tainted)
Pre-allocate a memory buffer for a "string" type value pair.
Definition pair.c:2691
fr_pair_t * fr_pair_alloc_null(TALLOC_CTX *ctx)
Dynamically allocate a new attribute with no fr_dict_attr_t assigned.
Definition pair.c:170
int fr_pair_value_bstr_realloc(fr_pair_t *vp, char **out, size_t size)
Change the length of a buffer for a "string" type value pair.
Definition pair.c:2716
static void * fr_pair_iter_next_by_ancestor(fr_dcursor_t *cursor, void *current, void *uctx)
Iterate over pairs which are decedents of the specified da.
Definition pair.c:664
fr_cmp_ret_t fr_pair_cmp_by_parent_num(void const *a, void const *b)
Order attributes by their parent(s), attribute number, and tag.
Definition pair.c:1902
bool fr_pair_immutable(fr_pair_t const *vp)
Definition pair.c:2257
void fr_pair_value_clear(fr_pair_t *vp)
Free/zero out value (or children) of a given VP.
Definition pair.c:2482
int fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list and free.
Definition pair.c:1779
int fr_pair_append_by_da_parent(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t, adding the parent attributes if required.
Definition pair.c:1474
static fr_dlist_head_t value_dlist
Definition pair.c:1184
int fr_pair_delete_by_child_num(fr_pair_list_t *list, fr_dict_attr_t const *parent, unsigned int attr)
Delete matching pairs from the specified list.
Definition pair.c:1761
int fr_pair_value_copy(fr_pair_t *dst, fr_pair_t *src)
Copy the value from one pair to another.
Definition pair.c:2512
fr_pair_t * _fr_pair_dcursor_init(fr_dcursor_t *cursor, fr_pair_list_t const *list, bool is_const)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.c:1083
int fr_pair_reinit_from_da(fr_pair_list_t *list, fr_pair_t *vp, fr_dict_attr_t const *da)
Re-initialise an attribute with a different da.
Definition pair.c:328
int fr_pair_steal(TALLOC_CTX *ctx, fr_pair_t *vp)
Steal one VP.
Definition pair.c:538
int fr_pair_value_memdup_shallow(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Assign a buffer to a "octets" type value pair.
Definition pair.c:2949
bool fr_pair_validate_relaxed(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list)
Uses fr_pair_cmp to verify all fr_pair_ts in list match the filter defined by check.
Definition pair.c:2182
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:504
fr_value_box_t * fr_pair_dcursor_nested_init(fr_dcursor_t *cursor, fr_dcursor_t *parent)
Initialises a special dcursor over another cursor which returns fr_pair_t, but we return fr_value_box...
Definition pair.c:1251
#define IN_A_LIST_MSG
Definition pair.c:551
fr_pair_t * _fr_pair_dcursor_by_da_init(fr_dcursor_t *cursor, fr_pair_list_t const *list, fr_dict_attr_t const *da, bool is_const)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition pair.c:1101
static void * _fr_pair_iter_next_dcursor_value(UNUSED fr_dcursor_t *cursor, void *current, void *uctx)
Iterate over pairs.
Definition pair.c:1220
int fr_pair_value_mem_alloc(fr_pair_t *vp, uint8_t **out, size_t size, bool tainted)
Pre-allocate a memory buffer for a "octets" type value pair.
Definition pair.c:2843
int fr_pair_value_strtrim(fr_pair_t *vp)
Trim the length of the string buffer to match the length of the C string.
Definition pair.c:2638
int fr_pair_insert_after(fr_pair_list_t *list, fr_pair_t *pos, fr_pair_t *to_add)
Add a VP after another VP.
Definition pair.c:1324
fr_pair_t * fr_pair_afrom_da_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da)
Create a pair (and all intermediate parents), and append it to the list.
Definition pair.c:481
fr_pair_list_t * fr_pair_children(fr_pair_t *vp)
Get the child list of a group.
Definition pair.c:897
int fr_pair_prepend(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the start of the list.
Definition pair.c:1267
fr_value_box_t * fr_pair_dcursor_value_init(fr_dcursor_t *cursor)
Initialises a special dcursor over a fr_pair_list_t, but which returns fr_value_box_t.
Definition pair.c:1204
void fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_t const *dict, fr_value_box_t *box)
Parse a list of VPs from a value box.
Definition pair.c:3494
fr_pair_t * fr_pair_list_iter_leaf(fr_pair_list_t *list, fr_pair_t *vp)
Iterates over the leaves of a list.
Definition pair.c:998
void fr_pair_list_steal(TALLOC_CTX *ctx, fr_pair_list_t *list)
Steal a list of pairs to a new context.
Definition pair.c:2281
int fr_pair_list_copy_by_da(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from, fr_dict_attr_t const *da)
Duplicate pairs in a list matching the specified da.
Definition pair.c:2388
int fr_pair_raw_afrom_pair(fr_pair_t *vp, uint8_t const *data, size_t data_len)
Mark malformed attribute as raw.
Definition pair.c:610
int fr_pair_value_memdup_buffer_shallow(fr_pair_t *vp, uint8_t const *src, bool tainted)
Assign a talloced buffer to a "octets" type value pair.
Definition pair.c:2969
int fr_pair_value_strdup_shallow(fr_pair_t *vp, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a vp, but don't copy it.
Definition pair.c:2619
int fr_pair_prepend_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and prepend)
Definition pair.c:1444
fr_pair_t * _fr_pair_dcursor_iter_init(fr_dcursor_t *cursor, fr_pair_list_t const *list, fr_dcursor_iter_t iter, void const *uctx, bool is_const)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.c:1063
int fr_pair_list_copy_to_box(fr_value_box_t *dst, fr_pair_list_t *from)
Copy the contents of a pair list to a set of value-boxes.
Definition pair.c:2335
fr_pair_t * fr_pair_afrom_child_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
Create a new valuepair.
Definition pair.c:385
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
TALLOC_CTX * ctx
Definition pair_legacy.h:43
void fr_proto_da_stack_build(fr_da_stack_t *stack, fr_dict_attr_t const *da)
Build a complete DA stack from the da back to the root.
Definition proto.c:118
#define fr_assert(_expr)
Definition rad_assert.h:37
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
Set of parsing rules for *unescape_until functions.
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_pair_t * vp
bool _CONST is_child
is a child of a VP
Definition pair.h:55
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
#define talloc_get_type_abort_const
Definition talloc.h:117
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
void check(const char *name, int index, const struct info *all, int want_lineno, const char *want_function, const char *want_file, int *failed)
Definition testlib.c:72
#define FR_TLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for tlists.
Definition tlist.h:800
#define FR_TLIST_PARENT_FUNCS(_name, _element_type, _list_type, _list_field)
Define a type specific function to find the structure which contains a tlist head.
Definition tlist.h:940
@ T_OP_CMP_TRUE
Definition token.h:102
@ T_BARE_WORD
Definition token.h:118
@ T_OP_EQ
Definition token.h:81
@ T_OP_CMP_FALSE
Definition token.h:103
@ T_OP_REG_EQ
Definition token.h:100
@ T_OP_REG_NE
Definition token.h:101
static unsigned count
Definition unittest.c:47
#define ATTRIBUTE_EQ(_x, _y)
Definition pair.h:150
#define fr_pair_cmp_op(_op, _a, _b)
Compare two attributes using and operator.
Definition pair.h:674
#define PAIR_ALLOCED(_x)
Definition pair.h:213
static fr_slen_t fr_pair_aprint(TALLOC_CTX *ctx, char **out, fr_dict_attr_t const *parent, fr_pair_t const *vp) 1(fr_pair_print
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:205
#define fr_pair_cmp_by_da_mutable
Definition pair.h:687
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:69
#define vp_group
Definition pair.h:139
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
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:281
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
#define PAIR_VERIFY_WITH_LIST(_l, _x)
Definition pair.h:206
int fr_pair_list_sort(fr_pair_list_t *list, fr_cmp_t cmp)
Sort a doubly linked list of fr_pair_ts using merge sort.
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition pair_print.c:59
#define PAIR_LIST_VERIFY(_x)
Definition pair.h:208
fr_pair_t * fr_pair_list_prev(fr_pair_list_t const *list, fr_pair_t const *item))
Get the previous item in a valuepair list before a specific entry.
Definition pair_inline.c:82
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
size_t fr_pair_list_num_elements(fr_pair_list_t const *list)
Get the length of a list of fr_pair_t.
static fr_slen_t parent
Definition pair.h:860
uint8_t depth
Deepest attribute in the stack.
Definition proto.h:56
fr_dict_attr_t const * da[FR_DICT_MAX_TLV_STACK+1]
The stack.
Definition proto.h:57
Structure for holding the stack of dictionary attributes being encoded.
Definition proto.h:55
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
#define fr_type_is_group(_x)
Definition types.h:376
#define fr_type_is_structural(_x)
Definition types.h:392
#define FR_TYPE_STRUCTURAL
Definition types.h:316
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
fr_cmp_ret_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:761
void fr_value_box_memdup_buffer_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, bool tainted)
Assign a talloced buffer to a box, but don't copy it.
Definition value.c:5243
fr_slen_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:6132
int fr_value_box_vasprintf(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, bool tainted, char const *fmt, va_list ap)
Print a formatted string using our internal printf wrapper and assign it to a value box.
Definition value.c:4706
int fr_value_box_strtrim(TALLOC_CTX *ctx, fr_value_box_t *vb)
Trim the length of the string buffer to match the length of the C string.
Definition value.c:4675
int fr_value_box_mem_alloc(TALLOC_CTX *ctx, uint8_t **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Pre-allocate an octets buffer for filling by the caller.
Definition value.c:5047
int fr_value_box_memdup_buffer(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, bool tainted)
Copy a talloced buffer to a fr_value_box_t.
Definition value.c:5203
int fr_value_box_bstrdup_buffer(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated talloced buffer to a fr_value_box_t.
Definition value.c:4963
int fr_value_box_mem_realloc(TALLOC_CTX *ctx, uint8_t **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition value.c:5080
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4422
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:4224
void fr_value_box_memdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Assign a buffer to a box, but don't copy it.
Definition value.c:5225
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4359
void fr_value_box_verify(char const *file, int line, fr_value_box_t const *vb)
Validation function to check that a fr_value_box_t is correctly initialised.
Definition value.c:7280
void fr_value_box_set_secret(fr_value_box_t *box, bool secret)
Mark a box as holding a secret, or not.
Definition value.c:7510
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4649
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4759
int fr_value_box_bstr_alloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Alloc and assign an empty \0 terminated string to a fr_value_box_t.
Definition value.c:4826
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4405
int fr_value_box_bstr_realloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition value.c:4859
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4900
int fr_value_box_bstrdup_buffer_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a talloced buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:5008
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition value.c:4987
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:5141
#define fr_box_is_numeric(_x)
Definition value.h:484
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:669
static bool fr_value_box_is_secret(fr_value_box_t const *box)
Definition value.h:1144
static fr_slen_t data
Definition value.h:1367
#define fr_value_box_is_safe_for_only(_box, _safe_for)
Definition value.h:1133
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1062
#define FR_VALUE_BOX_SAFE_FOR_NONE
Definition value.h:172
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:635
static size_t char ** out
Definition value.h:1062