Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/dict_validate.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program 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
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/** Validation framework to allow protocols to set custom validation rules
18
 *
19
 * @file src/lib/util/dict_validate.c
20
 *
21
 * @copyright 2019 The FreeRADIUS server project
22
 * @copyright 2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23
 */
24
RCSID("$Id: e489b55d26d15fb0ce5b4d1c0a71196a2ab7dc85 $")
25
26
#include <freeradius-devel/util/dict_priv.h>
27
28
/** Validate a set of flags
29
 *
30
 * @param[in] da    to check.
31
 * @return
32
 *  - true if attribute definition is valid.
33
 *  - false if attribute definition is not valid.
34
 */
35
bool dict_attr_flags_valid(fr_dict_attr_t *da)
36
28.5k
{
37
28.5k
  int bit;
38
28.5k
  uint32_t all_flags;
39
28.5k
  uint32_t shift_is_root, shift_internal;
40
28.5k
  uint32_t shift_array, shift_has_value;
41
28.5k
  uint32_t shift_subtype, shift_extra;
42
28.5k
  uint32_t shift_counter;
43
28.5k
  fr_dict_t   *dict = da->dict;
44
28.5k
  fr_dict_attr_t const  *parent = da->parent;
45
28.5k
  char const    *name = da->name;
46
28.5k
  int     attr = da->attr;
47
28.5k
  fr_type_t   type = da->type;
48
28.5k
  fr_dict_attr_flags_t  *flags = &da->flags;
49
50
  /*
51
   *  Convert the 1-bit fields into bits numbers, so that we
52
   *  can check them in parallel.
53
   */
54
28.5k
  all_flags = 0;
55
28.5k
  bit = -1;
56
57
199k
#define SET_FLAG(_flag) do { shift_ ## _flag = 1 << ++bit; if (flags->_flag) all_flags |= (1 << bit); } while (0)
58
28.5k
  SET_FLAG(is_root);
59
28.5k
  SET_FLAG(internal);
60
28.5k
  SET_FLAG(array);
61
28.5k
  SET_FLAG(has_value);
62
28.5k
  SET_FLAG(extra);
63
28.5k
  SET_FLAG(counter);
64
28.5k
  SET_FLAG(subtype);
65
66
28.5k
#define FORBID_OTHER_FLAGS(_flag, _allowed) \
67
28.5k
  do { \
68
742
    if (all_flags & ~shift_ ## _flag & ~(_allowed)) { \
69
0
      fr_strerror_printf("The '" STRINGIFY(_flag) "' flag cannot be used with any other flag (%u) %s[%d]", all_flags, da->filename, da->line); \
70
0
      return false; \
71
0
    } \
72
742
  } while (0)
73
74
28.5k
#define ALLOW_FLAG(_flag) do { all_flags &= ~shift_ ## _flag; } while (0)
75
76
  // is_root
77
  // is_unknown
78
  // internal
79
  // array
80
  // has_value
81
  // extra
82
  // encrypt
83
  // length
84
  // type_size
85
86
28.5k
  if (flags->is_root) {
87
0
    FORBID_OTHER_FLAGS(is_root, 0);
88
0
  }
89
90
28.5k
  if (flags->is_unknown) {
91
0
    fr_strerror_const("The 'unknown' flag cannot be set for attributes in the dictionary.");
92
0
    return false;
93
0
  }
94
95
28.5k
  if (flags->local != parent->flags.local) {
96
0
    fr_strerror_const("Cannot mix local variables with non-local attributes");
97
0
    return false;
98
0
  }
99
100
28.5k
  if (flags->local && (flags->is_unknown || flags->is_raw)) {
101
0
    fr_strerror_const("Local variables cannot be 'raw' or unknown");
102
0
    return false;
103
0
  }
104
105
  /*
106
   *  "flat" attributes can only go into a group.
107
   */
108
28.5k
  if ((flags->allow_flat) && (type != FR_TYPE_GROUP)) {
109
0
    fr_strerror_printf("Cannot set the 'flat' flag for data type %s", fr_type_to_str(type));
110
0
    return false;
111
0
  }
112
113
  /*
114
   *  Only some data types can be in arrays, because we need
115
   *  to be able to decode the various array members.
116
   */
117
28.5k
  if (flags->array) {
118
286
    if (!flags->is_known_width) switch (type) {
119
0
    default:
120
0
      fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
121
0
             fr_type_to_str(type));
122
0
      return false;
123
124
0
    case FR_TYPE_IPV4_ADDR:
125
0
    case FR_TYPE_IPV4_PREFIX:
126
0
    case FR_TYPE_IPV6_ADDR:
127
0
    case FR_TYPE_IPV6_PREFIX:
128
0
    case FR_TYPE_UINT8:
129
0
    case FR_TYPE_UINT16:
130
0
    case FR_TYPE_UINT32:
131
0
    case FR_TYPE_DATE:
132
0
    case FR_TYPE_TIME_DELTA:
133
0
      break;
134
135
0
    case FR_TYPE_ATTR:
136
0
      flags->is_known_width = 1;
137
0
      break;
138
139
0
    case FR_TYPE_STRING:
140
0
    case FR_TYPE_OCTETS:
141
0
      if (!flags->length) {
142
0
        fr_strerror_const("Variable length attributes cannot be marked as 'array'");
143
0
        return false;
144
0
      }
145
146
0
      flags->is_known_width = 1;
147
0
      break;
148
149
0
    case FR_TYPE_STRUCT:
150
      /*
151
       *  If we have arrays of structs, then the structure MUST be known width.
152
       */
153
0
      flags->is_known_width = 1;
154
0
      break;
155
0
    }
156
157
    /*
158
     *  DHCPv6 has arrays of string / octets, prefixed
159
     *  with a uint16 field of "length".  Also, arrays of dns_labels.
160
     */
161
286
    ALLOW_FLAG(extra);
162
286
    ALLOW_FLAG(subtype);
163
164
286
    FORBID_OTHER_FLAGS(array, 0);
165
286
  }
166
167
  /*
168
   *  'has_value' should only be set internally.  If the
169
   *  caller sets it, we still sanity check it.
170
   */
171
28.5k
  if (flags->has_value) {
172
0
    if (type != FR_TYPE_UINT32) {
173
0
      fr_strerror_printf("The 'has_value' flag can only be used with attributes "
174
0
             "of type 'integer'");
175
0
      return false;
176
0
    }
177
178
0
    FORBID_OTHER_FLAGS(has_value, shift_internal);
179
0
  }
180
181
  /*
182
   *  Sanity check aliases.
183
   */
184
28.5k
  if (flags->is_alias) {
185
0
    fr_dict_attr_ext_ref_t *ext;
186
187
0
    ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
188
0
    if (!ext) {
189
0
      fr_strerror_const("ALIAS is missing extension");
190
0
      return false;
191
0
    }
192
193
0
    if (!ext->ref) {
194
0
      fr_strerror_const("ALIAS is missing ref");
195
0
      return false;
196
0
    }
197
198
0
    if (da->parent->type == FR_TYPE_STRUCT) {
199
0
      fr_strerror_const("ALIAS cannot be added to a data type 'struct'");
200
0
      return false;
201
0
    }
202
203
0
    fr_assert(!da->flags.is_unknown);
204
0
    fr_assert(!da->flags.is_raw);
205
0
    fr_assert(!da->flags.array);
206
0
    fr_assert(!da->flags.is_known_width);
207
0
    fr_assert(!da->flags.has_value);
208
0
    fr_assert(!da->flags.counter);
209
0
    fr_assert(!da->flags.secret);
210
0
    fr_assert(!da->flags.unsafe);
211
0
    fr_assert(!da->flags.is_ref_target);
212
0
    fr_assert(!da->flags.local);
213
0
    fr_assert(!da->flags.has_fixup);
214
0
  }
215
216
  /*
217
   *  The "extra" flag is a grab-bag of stuff, depending on
218
   *  the data type.
219
   */
220
28.5k
  if (flags->extra) {
221
456
    if (!fr_dict_attr_is_key_field(da) && !da_is_length_field(da) && !da_is_bit_field(da)) {
222
0
      fr_strerror_const("The 'key' and 'length' flags cannot be used with any other flags.");
223
0
      return false;
224
0
    }
225
226
456
    switch (type) {
227
202
    case FR_TYPE_BOOL:
228
310
    case FR_TYPE_UINT8:
229
332
    case FR_TYPE_UINT16:
230
346
    case FR_TYPE_UINT32:
231
366
    case FR_TYPE_UINT64:
232
366
      if ((flags->subtype != FLAG_KEY_FIELD) && (flags->subtype != FLAG_BIT_FIELD)) {
233
0
        fr_strerror_const("Invalid type (not 'key' field or 'bit' field) for extra flag.");
234
0
        return false;
235
0
      }
236
237
366
      if (parent->type != FR_TYPE_STRUCT) {
238
0
        fr_strerror_const("The 'key' flag can only be used inside of a 'struct'.");
239
0
        return false;
240
0
      }
241
242
366
      ALLOW_FLAG(extra);
243
366
      ALLOW_FLAG(subtype);
244
366
      break;
245
246
4
    case FR_TYPE_OCTETS:
247
54
    case FR_TYPE_STRING:
248
54
      if (flags->length != 0) {
249
0
        fr_strerror_const("Cannot use [..] and length=uint...");
250
0
        return false;
251
0
      }
252
253
      /*
254
       *  We can do arrays of variable-length types, so long as they have a "length="
255
       *  modifier.
256
       *
257
       *  But any other modifier is foridden, including the use of "length=" outside of
258
       *  the context of arrays.
259
       */
260
54
      if (flags->array) {
261
54
        ALLOW_FLAG(array);
262
263
54
        if (!da_is_length_field(da)) {
264
0
          goto invalid_extra;
265
0
        }
266
267
54
      } else if (da_is_length_field(da)) {
268
        /* this is allowed */
269
270
0
      } else if (flags->subtype) {
271
0
      invalid_extra:
272
0
        fr_strerror_const("Invalid type (not 'length=...') for extra flag.");
273
0
        return false;
274
0
      }
275
276
54
      ALLOW_FLAG(extra);
277
54
      ALLOW_FLAG(subtype);
278
54
      break;
279
280
36
    case FR_TYPE_STRUCT:
281
36
      if (!da_is_length_field(da)) {
282
0
        fr_strerror_const("Invalid type (not 'length=...') for extra flag.");
283
0
        return false;
284
0
      }
285
286
36
      ALLOW_FLAG(extra);
287
36
      ALLOW_FLAG(subtype);
288
36
      ALLOW_FLAG(array);
289
36
      break;
290
291
0
    case FR_TYPE_TLV:
292
0
      ALLOW_FLAG(extra);
293
      /* @todo - allow arrays of struct? */
294
0
      ALLOW_FLAG(subtype);
295
0
      break;
296
297
0
    default:
298
0
      fr_strerror_printf("Type %s cannot hold extra flags",
299
0
             fr_type_to_str(type));
300
0
      return false;
301
456
    }
302
303
456
    if (da_is_length_field(da) &&
304
90
        ((type != FR_TYPE_STRING) && (type != FR_TYPE_OCTETS) && (type != FR_TYPE_STRUCT))) {
305
0
      fr_strerror_printf("The 'length' flag cannot be used with type %s",
306
0
             fr_type_to_str(type));
307
0
      return false;
308
0
    }
309
310
456
    FORBID_OTHER_FLAGS(extra, 0);
311
456
  }
312
313
  /*
314
   *  Force "length" for fixed-size data types which aren't
315
   *  bit fields.  Check / set "length" and "type_size" for
316
   *  other types.
317
   */
318
28.5k
  if (!flags->extra || (flags->subtype != FLAG_BIT_FIELD)) switch (type) {
319
0
  case FR_TYPE_INT8:
320
656
  case FR_TYPE_UINT8:
321
1.03k
  case FR_TYPE_BOOL:
322
1.03k
    flags->length = 1;
323
1.03k
    break;
324
325
0
  case FR_TYPE_INT16:
326
534
  case FR_TYPE_UINT16:
327
534
    flags->length = 2;
328
534
    break;
329
330
508
  case FR_TYPE_DATE:
331
986
  case FR_TYPE_TIME_DELTA:
332
986
    if (!flags->length) flags->length = 4;
333
334
986
    if ((flags->length != 2) && (flags->length != 4) && (flags->length != 8)) {
335
0
      fr_strerror_printf("Invalid length %u for attribute of type '%s'",
336
0
             flags->length, fr_type_to_str(type));
337
0
      return false;
338
0
    }
339
986
    break;
340
341
1.18k
  case FR_TYPE_IPV4_ADDR:
342
1.42k
  case FR_TYPE_INT32:
343
8.70k
  case FR_TYPE_UINT32:
344
8.90k
  case FR_TYPE_FLOAT32:
345
8.90k
    flags->length = 4;
346
8.90k
    break;
347
348
214
  case FR_TYPE_INT64:
349
738
  case FR_TYPE_UINT64:
350
738
  case FR_TYPE_FLOAT64:
351
738
    flags->length = 8;
352
738
    break;
353
354
0
  case FR_TYPE_SIZE:
355
0
    flags->length = sizeof(size_t);
356
0
    break;
357
358
220
  case FR_TYPE_ETHERNET:
359
220
    flags->length = 6;
360
220
    break;
361
362
18
  case FR_TYPE_IFID:
363
18
    flags->length = 8;
364
18
    break;
365
366
260
  case FR_TYPE_IPV6_ADDR:
367
304
  case FR_TYPE_COMBO_IP_ADDR:
368
304
    flags->length = 16;
369
304
    break;
370
371
70
  case FR_TYPE_IPV6_PREFIX:
372
70
  case FR_TYPE_COMBO_IP_PREFIX:
373
70
    flags->length = 17;
374
70
    break;
375
376
444
  case FR_TYPE_STRUCT:
377
444
    ALLOW_FLAG(internal);
378
444
    ALLOW_FLAG(array);
379
444
    if (all_flags) {
380
0
      fr_strerror_const("Invalid flag for attribute of type 'struct'");
381
0
      return false;
382
0
    }
383
444
    break;
384
385
2.31k
  case FR_TYPE_VENDOR:
386
2.31k
    if (dict->string_based) break;
387
388
2.31k
    if (parent->type != FR_TYPE_VSA) {
389
0
      fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' "
390
0
             "instead of '%s'",
391
0
             fr_type_to_str(parent->type));
392
0
      return false;
393
0
    }
394
395
2.31k
    if ((flags->length != 1) &&
396
0
        (flags->length != 2) &&
397
0
        (flags->length != 4)) {
398
0
      fr_strerror_const("The 'length' flag can only be used for attributes of type 'vendor' with lengths of 1,2 or 4");
399
0
      return false;
400
0
    }
401
2.31k
    break;
402
403
2.31k
  case FR_TYPE_TLV:
404
694
    if ((flags->length != 1) &&
405
486
        (flags->length != 2) &&
406
178
        (flags->length != 4)) {
407
0
      fr_strerror_const("The 'length' flag can only be used for attributes of type 'tlv' with lengths of 1,2 or 4");
408
0
      return false;
409
0
    }
410
694
    break;
411
412
    /*
413
     *  'octets[n]' can only be used in a few limited situations.
414
     */
415
2.68k
  case FR_TYPE_OCTETS:
416
2.68k
    if (flags->length) {
417
      /*
418
       *  Internal attributes can use octets[n]
419
       *  MS-MPPE-Keys use octets[18],encrypt=User-Password
420
       *  EAP-SIM-RAND uses array
421
       */
422
168
      ALLOW_FLAG(internal);
423
168
      ALLOW_FLAG(subtype);
424
168
      ALLOW_FLAG(array);
425
426
168
      if (all_flags) {
427
0
        fr_strerror_const("The 'octets[...]' syntax cannot be used any other flag");
428
0
        return false;
429
0
      }
430
431
168
      if (flags->length > 253) {
432
0
        fr_strerror_printf("Invalid length %d", flags->length);
433
0
        return false;
434
0
      }
435
168
    }
436
2.68k
    break;
437
438
2.68k
  case FR_TYPE_UNION:
439
20
    if (parent->type != FR_TYPE_STRUCT) {
440
0
      fr_strerror_printf("Attributes of type 'union' must have a parent of type 'struct', not of type '%s'",
441
0
             fr_type_to_str(parent->type));
442
0
      return false;
443
0
    }
444
445
    /*
446
     *  If the UNION is missing a key extension, then the children of the UNION cannot find
447
     *  the key field in the parent STRUCT.
448
     */
449
20
    if (!fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_KEY)) {
450
0
      fr_strerror_const("Attribute of type 'union' is missing 'key=...'");
451
0
      return false;
452
0
    }
453
20
    break;
454
455
20
  case FR_TYPE_NULL:
456
0
  case FR_TYPE_INTERNAL:
457
0
    fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
458
0
           fr_type_to_str(type));
459
0
    return false;
460
461
    /*
462
     *  These types are encoded differently in each protocol.
463
     */
464
40
  case FR_TYPE_IPV4_PREFIX:
465
66
  case FR_TYPE_ATTR:
466
8.68k
  case FR_TYPE_STRING:
467
8.70k
  case FR_TYPE_VSA:
468
9.23k
  case FR_TYPE_GROUP:
469
9.23k
    break;
470
28.1k
  }
471
472
  /*
473
   *  type_size is used to limit the maximum attribute number, so it's checked first.
474
   */
475
28.5k
  if (flags->type_size) {
476
3.49k
    if ((type == FR_TYPE_DATE) || (type == FR_TYPE_TIME_DELTA)) {
477
      /*
478
       *  Allow all time res here
479
       */
480
3.27k
    } else if (!flags->extra) {
481
3.00k
      if ((type != FR_TYPE_TLV) && (type != FR_TYPE_VENDOR)) {
482
0
        fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv', and not type '%s'", fr_type_to_str(type));
483
0
        return false;
484
0
      }
485
486
3.00k
      if ((flags->type_size != 1) &&
487
486
          (flags->type_size != 2) &&
488
178
          (flags->type_size != 4)) {
489
0
        fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4, not %i", flags->type_size);
490
0
        return false;
491
0
      }
492
3.00k
    }
493
3.49k
  }
494
495
  /*
496
   *  Counters can be time deltas, or unsigned integers.
497
   *  For other data types, we don't know how to
498
   *  automatically add two counters.
499
   */
500
28.5k
  if (flags->counter) {
501
38
    if ((type == FR_TYPE_TIME_DELTA) || (fr_type_is_integer(type) && !fr_type_is_signed(type))) {
502
38
      ALLOW_FLAG(counter);
503
38
    } else {
504
0
      fr_strerror_printf("The 'counter' flag cannot be used with '%s'", fr_type_to_str(type));
505
0
      return false;
506
0
    }
507
38
  }
508
509
  /*
510
   *  Check flags against the parent attribute.
511
   */
512
28.5k
  switch (parent->type) {
513
1.32k
  case FR_TYPE_STRUCT:
514
1.32k
    ALLOW_FLAG(extra);
515
1.32k
    ALLOW_FLAG(subtype);
516
517
    /*
518
     *  If our parent is known width, then the children have to be known width, UNLESS
519
     *  either this child or its parent has a "length" prefix.
520
     */
521
1.32k
    if (parent->flags.is_known_width && !flags->is_known_width && !flags->length &&
522
6
        !da_is_length_field(da) && !da_is_length_field(parent)) {
523
0
      fr_strerror_const("Variable-sized fields cannot be used within a 'struct' which is 'array'");
524
0
      return false;
525
0
    }
526
527
1.32k
    if (flags->array) {
528
28
      switch (type) {
529
12
      case FR_TYPE_FIXED_SIZE:
530
12
        ALLOW_FLAG(array);
531
12
        break;
532
533
16
      default:
534
16
        if (flags->is_known_width) ALLOW_FLAG(array);
535
16
        break;
536
28
      }
537
28
    }
538
539
1.32k
    if (all_flags) {
540
0
      fr_strerror_const("Invalid flag for attribute inside of a 'struct'");
541
0
      return false;
542
0
    }
543
544
1.32k
    if (!attr) break;
545
546
    /*
547
     *  If we have keyed structs, then the first
548
     *  member can be variable length.
549
     *
550
     *  For subsequent children, have each one check
551
     *  the previous child.
552
     */
553
1.32k
    if (attr != 1) {
554
888
      int i;
555
888
      fr_dict_attr_t const *sibling;
556
557
888
      sibling = fr_dict_attr_child_by_num(parent, (attr) - 1);
558
559
      /*
560
       *  sibling might not exist, if it's a deferred 'tlv clone=...'
561
       */
562
563
      /*
564
       *  Variable sized elements cannot have anything after them in a struct.
565
       */
566
888
      if (sibling && !sibling->flags.length && !sibling->flags.is_known_width) {
567
0
        fr_strerror_const("No other field can follow a struct MEMBER which is variable sized");
568
0
        return false;
569
0
      }
570
571
      /*
572
       *  The same goes for arrays.
573
       */
574
888
      if (sibling && sibling->flags.array) {
575
0
        fr_strerror_const("No other field can follow a struct MEMBER which is 'array'");
576
0
        return false;
577
0
      }
578
579
      /*
580
       *  Check for bad key fields, or multiple
581
       *  key fields.  Yes, this is O(N^2), but
582
       *  the structs are small.
583
       */
584
888
      if (fr_dict_attr_is_key_field(da)) {
585
46
        for (i = 1; i < attr; i++) {
586
38
          sibling = fr_dict_attr_child_by_num(parent, i);
587
38
          if (!sibling) {
588
0
            fr_strerror_printf("Child %d of 'struct' type attribute %s does not exist.",
589
0
                   i, parent->name);
590
0
            return false;
591
0
          }
592
593
38
          if (!fr_dict_attr_is_key_field(sibling)) continue;
594
595
0
          fr_strerror_printf("Duplicate key attributes '%s' and '%s' in 'struct' type attribute %s are forbidden",
596
0
                 name, sibling->name, parent->name);
597
0
          return false;
598
38
        }
599
8
      }
600
888
    }
601
1.32k
    break;
602
603
2.31k
  case FR_TYPE_VSA:
604
2.31k
    if ((type != FR_TYPE_VENDOR) && !flags->internal) {
605
0
      fr_strerror_printf("Attributes of type '%s' cannot be children of the 'vsa' type",
606
0
             fr_type_to_str(type));
607
0
      return false;
608
0
    }
609
2.31k
    break;
610
611
11.1k
  case FR_TYPE_TLV:
612
24.7k
  case FR_TYPE_VENDOR:
613
24.7k
    break;
614
615
146
  case FR_TYPE_UNION:
616
146
    if (!((da->type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_TLV) || fr_type_is_leaf(da->type))) {
617
0
      fr_strerror_printf("Attributes of type '%s' cannot be children of the 'union' type",
618
0
             fr_type_to_str(type));
619
0
      return false;
620
0
    }
621
146
    break;
622
623
146
  default:
624
0
    fr_strerror_printf("Attributes of type '%s' cannot have child attributes",
625
0
           fr_type_to_str(parent->type));
626
0
    return false;
627
28.5k
  }
628
629
28.5k
  return true;
630
28.5k
}
631
632
633
/** Validate a new attribute definition
634
 *
635
 * @todo we need to check length of none vendor attributes.
636
 *
637
 * @param[in] da  to validate.
638
 * @return
639
 *  - true if attribute definition is valid.
640
 *  - false if attribute definition is not valid.
641
 */
642
bool dict_attr_valid(fr_dict_attr_t *da)
643
28.5k
{
644
28.5k
  if (!fr_cond_assert(da->parent)) return false;
645
646
28.5k
  if (fr_dict_valid_name(da->name, -1) <= 0) return false;
647
648
  /*
649
   *  Run protocol-specific validation functions, BEFORE we
650
   *  do the rest of the checks.
651
   */
652
28.5k
  if (da->dict->proto->attr.valid && !da->dict->proto->attr.valid(da)) return false;
653
654
  /*
655
   *  Check the flags, data types, and parent data types and flags.
656
   */
657
28.5k
  if (!dict_attr_flags_valid(da)) return false;
658
659
28.5k
  return true;
660
28.5k
}