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