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