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