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