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