Coverage Report

Created: 2026-09-28 06:27

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