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