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