Coverage Report

Created: 2026-02-26 06:38

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