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