All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dict.c
Go to the documentation of this file.
1 /*
2  * dict.c Routines to read the dictionary file.
3  *
4  * Version: $Id: af14c46e0d104472a393f8602663ea3f5f65f862 $
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006 The FreeRADIUS server project
21  */
22 RCSID("$Id: af14c46e0d104472a393f8602663ea3f5f65f862 $")
23 
24 #include <freeradius-devel/libradius.h>
25 
26 #ifdef WITH_DHCP
27 # include <freeradius-devel/dhcp.h>
28 #endif
29 
30 #include <ctype.h>
31 
32 #ifdef HAVE_SYS_STAT_H
33 # include <sys/stat.h>
34 #endif
35 
36 #define MAX_ARGV (16)
37 
38 /*
39  * For faster HUP's, we cache the stat information for
40  * files we've $INCLUDEd
41  */
42 typedef struct dict_stat_t {
43  struct dict_stat_t *next;
44  struct stat stat_buf;
45 } dict_stat_t;
46 
47 typedef struct value_fixup_t {
52 
53 /** Vendors and attribute names
54  *
55  * It's very likely that the same vendors will operate in multiple
56  * protocol spaces, but number their attributes differently, so we need
57  * per protocol dictionaries.
58  *
59  * There would also be conflicts for DHCP(v6)/RADIUS attributes etc...
60  */
61 struct fr_dict {
63 
66 
67  fr_hash_table_t *vendors_by_name; //!< Lookup vendor by name.
68  fr_hash_table_t *vendors_by_num; //!< Lookup vendor by PEN.
69 
70  fr_hash_table_t *attributes_by_name; //!< Allow attribute lookup by unique name.
71 
72  fr_hash_table_t *attributes_combo; //!< Lookup variants of polymorphic attributes.
73 
74  fr_hash_table_t *values_by_da; //!< Lookup an attribute enum value by integer value.
75  fr_hash_table_t *values_by_name; //!< Lookup an attribute enum value by name.
76 
77  fr_dict_attr_t *root; //!< Root attribute of this dictionary.
78  TALLOC_CTX *pool; //!< Talloc memory pool to reduce mallocs.
79 };
80 
81 fr_dict_t *fr_dict_internal = NULL; //!< Internal server dictionary.
82 
83 /** Map data types to names representing those types
84  */
86  { "integer", PW_TYPE_INTEGER },
87  { "string", PW_TYPE_STRING },
88  { "ipaddr", PW_TYPE_IPV4_ADDR },
89  { "date", PW_TYPE_DATE },
90  { "abinary", PW_TYPE_ABINARY },
91  { "octets", PW_TYPE_OCTETS },
92  { "ifid", PW_TYPE_IFID },
93  { "ipv6addr", PW_TYPE_IPV6_ADDR },
94  { "ipv6prefix", PW_TYPE_IPV6_PREFIX },
95  { "byte", PW_TYPE_BYTE },
96  { "short", PW_TYPE_SHORT },
97  { "ether", PW_TYPE_ETHERNET },
98  { "combo-ip", PW_TYPE_COMBO_IP_ADDR },
99  { "tlv", PW_TYPE_TLV },
100  { "signed", PW_TYPE_SIGNED },
101  { "extended", PW_TYPE_EXTENDED },
102  { "long-extended", PW_TYPE_LONG_EXTENDED },
103  { "evs", PW_TYPE_EVS },
104  { "uint8", PW_TYPE_BYTE },
105  { "uint16", PW_TYPE_SHORT },
106  { "uint32", PW_TYPE_INTEGER },
107  { "int32", PW_TYPE_SIGNED },
108  { "integer64", PW_TYPE_INTEGER64 },
109  { "uint64", PW_TYPE_INTEGER64 },
110  { "ipv4prefix", PW_TYPE_IPV4_PREFIX },
111  { "cidr", PW_TYPE_IPV4_PREFIX },
112  { "vsa", PW_TYPE_VSA },
113  { "vendor", PW_TYPE_VENDOR },
114  { NULL, 0 }
115 };
116 
117 /** Map data types to min / max data sizes
118  */
119 const size_t dict_attr_sizes[PW_TYPE_MAX][2] = {
120  [PW_TYPE_INVALID] = {~0, 0},
121  [PW_TYPE_STRING] = {0, ~0},
122  [PW_TYPE_INTEGER] = {4, 4 },
123  [PW_TYPE_IPV4_ADDR] = {4, 4},
124  [PW_TYPE_DATE] = {4, 4},
125  [PW_TYPE_ABINARY] = {32, ~0},
126  [PW_TYPE_OCTETS] = {0, ~0},
127  [PW_TYPE_IFID] = {8, 8},
128  [PW_TYPE_IPV6_ADDR] = {16, 16},
129  [PW_TYPE_IPV6_PREFIX] = {2, 18},
130  [PW_TYPE_BYTE] = {1, 1},
131  [PW_TYPE_SHORT] = {2, 2},
132  [PW_TYPE_ETHERNET] = {6, 6},
133  [PW_TYPE_SIGNED] = {4, 4},
134  [PW_TYPE_COMBO_IP_ADDR] = {4, 16},
135  [PW_TYPE_TLV] = {2, ~0},
136  [PW_TYPE_EXTENDED] = {2, ~0},
137  [PW_TYPE_LONG_EXTENDED] = {3, ~0},
138  [PW_TYPE_EVS] = {6, ~0},
139  [PW_TYPE_INTEGER64] = {8, 8},
140  [PW_TYPE_IPV4_PREFIX] = {6, 6},
141  [PW_TYPE_VSA] = {4, ~0},
142  [PW_TYPE_VENDOR] = {0, 0}
143 };
144 
145 const int fr_dict_attr_allowed_chars[256] = {
146 /* 0x 0 1 2 3 4 5 6 7 8 9 a b c d e f */
147 /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148 /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149 /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
150 /* 3 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
151 /* 4 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
152 /* 5 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
153 /* 6 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
154 /* 7 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
155 /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
158 /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160 /* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 /* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162 /* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
163 };
164 
165 /*
166  * Create the hash of the name.
167  *
168  * We copy the hash function here because it's substantially faster.
169  */
170 #define FNV_MAGIC_INIT (0x811c9dc5)
171 #define FNV_MAGIC_PRIME (0x01000193)
172 
173 #ifdef __clang_analyzer__
174 # define INTERNAL_IF_NULL(_dict) do {\
175  if (!_dict) _dict = fr_dict_internal; \
176  if (!_dict) return NULL; \
177 } while (0)
178 #else
179 # define INTERNAL_IF_NULL(_dict) if (!_dict) _dict = fr_dict_internal
180 #endif
181 
182 /*
183  * Empty callback for hash table initialization.
184  */
185 static int hash_null_callback(UNUSED void *ctx, UNUSED void *data)
186 {
187  return 0;
188 }
189 
190 static void hash_pool_free(void *to_free)
191 {
192  talloc_free(to_free);
193 }
194 
195 static uint32_t dict_hash_name(char const *name)
196 {
197  uint32_t hash = FNV_MAGIC_INIT;
198  char const *p;
199 
200  for (p = name; *p != '\0'; p++) {
201  int c = *(unsigned char const *)p;
202  if (isalpha(c)) c = tolower(c);
203 
204  hash *= FNV_MAGIC_PRIME;
205  hash ^= (uint32_t)(c & 0xff);
206  }
207 
208  return hash;
209 }
210 
211 /*
212  * Hash callback functions.
213  */
214 static uint32_t dict_attr_name_hash(void const *data)
215 {
216  return dict_hash_name(((fr_dict_attr_t const *)data)->name);
217 }
218 
219 static int dict_attr_name_cmp(void const *one, void const *two)
220 {
221  fr_dict_attr_t const *a = one;
222  fr_dict_attr_t const *b = two;
223 
224  return strcasecmp(a->name, b->name);
225 }
226 
227 static uint32_t dict_attr_combo_hash(void const *data)
228 {
229  uint32_t hash;
230  fr_dict_attr_t const *attr = data;
231 
232  hash = fr_hash(&attr->vendor, sizeof(attr->vendor));
233  hash = fr_hash_update(&attr->type, sizeof(attr->type), hash);
234  return fr_hash_update(&attr->attr, sizeof(attr->attr), hash);
235 }
236 
237 static int dict_attr_combo_cmp(void const *one, void const *two)
238 {
239  fr_dict_attr_t const *a = one;
240  fr_dict_attr_t const *b = two;
241 
242  if (a->type < b->type) return -1;
243  if (a->type > b->type) return +1;
244 
245  if (a->vendor < b->vendor) return -1;
246  if (a->vendor > b->vendor) return +1;
247 
248  return a->attr - b->attr;
249 }
250 
251 static uint32_t dict_vendor_name_hash(void const *data)
252 {
253  return dict_hash_name(((fr_dict_vendor_t const *)data)->name);
254 }
255 
256 static int dict_vendor_name_cmp(void const *one, void const *two)
257 {
258  fr_dict_vendor_t const *a = one;
259  fr_dict_vendor_t const *b = two;
260 
261  return strcasecmp(a->name, b->name);
262 }
263 
264 static uint32_t dict_vendor_value_hash(void const *data)
265 {
266  return fr_hash(&(((fr_dict_vendor_t const *)data)->vendorpec),
267  sizeof(((fr_dict_vendor_t const *)data)->vendorpec));
268 }
269 
270 static int dict_vendor_value_cmp(void const *one, void const *two)
271 {
272  fr_dict_vendor_t const *a = one;
273  fr_dict_vendor_t const *b = two;
274 
275  return a->vendorpec - b->vendorpec;
276 }
277 
278 static uint32_t dict_enum_name_hash(void const *data)
279 {
280  uint32_t hash;
281  fr_dict_enum_t const *dval = data;
282 
283  hash = dict_hash_name(dval->name);
284  return fr_hash_update(&dval->da, sizeof(dval->da), hash);
285 }
286 
287 static int dict_enum_name_cmp(void const *one, void const *two)
288 {
289  int rcode;
290  fr_dict_enum_t const *a = one;
291  fr_dict_enum_t const *b = two;
292 
293  rcode = a->da - b->da;
294  if (rcode != 0) return rcode;
295 
296  return strcasecmp(a->name, b->name);
297 }
298 
299 static uint32_t dict_enum_value_hash(void const *data)
300 {
301  uint32_t hash = 0;
302  fr_dict_enum_t const *dval = data;
303 
304  hash = fr_hash_update(&dval->da, sizeof(dval->da), hash);
305  return fr_hash_update(&dval->value, sizeof(dval->value), hash);
306 }
307 
308 static int dict_enum_value_cmp(void const *one, void const *two)
309 {
310  int rcode;
311  fr_dict_enum_t const *a = one;
312  fr_dict_enum_t const *b = two;
313 
314  rcode = a->da - b->da;
315  if (rcode != 0) return rcode;
316 
317  return a->value - b->value;
318 }
319 
320 /** Add an entry to the list of stat buffers.
321  */
322 static void dict_stat_add(fr_dict_t *dict, struct stat const *stat_buf)
323 {
324  dict_stat_t *this;
325 
326  this = talloc_zero(dict, dict_stat_t);
327  if (!this) return;
328 
329  memcpy(&(this->stat_buf), stat_buf, sizeof(this->stat_buf));
330 
331  if (!dict->stat_head) {
332  dict->stat_head = dict->stat_tail = this;
333  } else {
334  dict->stat_tail->next = this;
335  dict->stat_tail = this;
336  }
337 }
338 
339 /** See if any dictionaries have changed. If not, don't do anything
340  */
341 static int dict_stat_check(fr_dict_t *dict, char const *dir, char const *file)
342 {
343  struct stat stat_buf;
344  dict_stat_t *this;
345  char buffer[2048];
346 
347  /*
348  * Nothing cached, all files are new.
349  */
350  if (!dict || !dict->stat_head) return 0;
351 
352  /*
353  * Stat the file.
354  */
355  snprintf(buffer, sizeof(buffer), "%s/%s", dir, file);
356  if (stat(buffer, &stat_buf) < 0) return 0;
357 
358  /*
359  * Find the cache entry.
360  * FIXME: use a hash table.
361  * FIXME: check dependencies, via children.
362  * if A loads B and B changes, we probably want
363  * to reload B at the minimum.
364  */
365  for (this = dict->stat_head; this != NULL; this = this->next) {
366  if (this->stat_buf.st_dev != stat_buf.st_dev) continue;
367  if (this->stat_buf.st_ino != stat_buf.st_ino) continue;
368 
369  /*
370  * The file has changed. Re-read it.
371  */
372  if (this->stat_buf.st_mtime < stat_buf.st_mtime) return 0;
373 
374  /*
375  * The file is the same. Ignore it.
376  */
377  return 1;
378  }
379 
380  /*
381  * Not in the cache.
382  */
383  return 0;
384 }
385 
386 /** Add a vendor to the dictionary
387  *
388  * Inserts a vendor entry into the vendor hash table. This must be done before adding
389  * attributes under a VSA.
390  *
391  * @param[in] dict of protocol context we're operating in. If NULL the internal
392  * dictionary will be used.
393  * @param[in] name of the vendor.
394  * @param[in] num Vendor's Private Enterprise Number.
395  * @return
396  * - 0 on success.
397  * - -1 on failure.
398  */
399 int fr_dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
400 {
401  size_t length;
402  fr_dict_vendor_t *dv;
403 
404  INTERNAL_IF_NULL(dict);
405 
406  if ((length = strlen(name)) >= FR_DICT_VENDOR_MAX_NAME_LEN) {
407  fr_strerror_printf("fr_dict_vendor_add: vendor name too long");
408  return -1;
409  }
410 
411  dv = (fr_dict_vendor_t *)talloc_zero_array(dict->pool, uint8_t, sizeof(*dv) + length);
412  if (dv == NULL) {
413  fr_strerror_printf("fr_dict_vendor_add: out of memory");
414  return -1;
415  }
416  talloc_set_type(dv, fr_dict_vendor_t);
417 
418  strcpy(dv->name, name);
419  dv->vendorpec = num;
420  dv->type = dv->length = 1; /* defaults */
421 
422  if (!fr_hash_table_insert(dict->vendors_by_name, dv)) {
423  fr_dict_vendor_t *old_dv;
424 
425  old_dv = fr_hash_table_finddata(dict->vendors_by_name, dv);
426  if (!old_dv) {
427  fr_strerror_printf("fr_dict_vendor_add: Failed inserting vendor name %s", name);
428  return -1;
429  }
430  if (old_dv->vendorpec != dv->vendorpec) {
431  fr_strerror_printf("fr_dict_vendor_add: Duplicate vendor name %s", name);
432  return -1;
433  }
434 
435  /*
436  * Already inserted. Discard the duplicate entry.
437  */
438  talloc_free(dv);
439  return 0;
440  }
441 
442  /*
443  * Insert the SAME pointer (not free'd when this table is
444  * deleted), into another table.
445  *
446  * We want this behaviour because we want OLD names for
447  * the attributes to be read from the configuration
448  * files, but when we're printing them, (and looking up
449  * by value) we want to use the NEW name.
450  */
451  if (!fr_hash_table_replace(dict->vendors_by_num, dv)) {
452  fr_strerror_printf("fr_dict_vendor_add: Failed inserting vendor %s", name);
453  return -1;
454  }
455 
456  return 0;
457 }
458 
459 /** Add a child to a parent.
460  *
461  * @param parent we're adding a child to.
462  * @param child to add to parent.
463  * @return
464  * - 0 on success.
465  * - -1 on failure (memory allocation error).
466  */
467 static inline int fr_dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
468 {
469  fr_dict_attr_t const * const *bin;
470  fr_dict_attr_t **this;
471 
472  /*
473  * Setup fields in the child
474  */
475  child->parent = parent;
476  child->depth = parent->depth + 1;
477 
478  /*
479  * We only allocate the pointer array *if* the parent has children.
480  */
481  if (!parent->children) parent->children = talloc_zero_array(parent, fr_dict_attr_t const *, UINT8_MAX + 1);
482  if (!parent->children) return -1;
483 
484  /*
485  * Treat the array as a hash of 255 bins, with attributes
486  * sorted into bins using num % 255.
487  *
488  * Although the various protocols may define numbers higher than 255:
489  *
490  * RADIUS/DHCPv4 - 1-255
491  * Diameter/Internal - 1-4294967295
492  * DHCPv6 - 1-65535
493  *
494  * In reality very few will ever use attribute numbers > 500, so for
495  * the majority of lookups we get O(1) performance.
496  *
497  * Attributes are inserted into the bin in order of their attribute
498  * numbers to allow slightly more efficient lookups.
499  */
500  bin = &parent->children[child->attr & 0xff];
501  for (;;) {
502  bool child_is_struct = false;
503  bool bin_is_struct = false;
504 
505  if (!*bin) break;
506 
507  /*
508  * Workaround for vendors that overload the RFC space.
509  * Structural attributes always take priority.
510  */
511  switch (child->type) {
512  case PW_TYPE_STRUCTURAL:
513  child_is_struct = true;
514  break;
515 
516  default:
517  break;
518  }
519 
520  switch ((*bin)->type) {
521  case PW_TYPE_STRUCTURAL:
522  bin_is_struct = true;
523  break;
524 
525  default:
526  break;
527  }
528 
529  if (child_is_struct && !bin_is_struct) break;
530  else if (child->vendor <= (*bin)->vendor) break; /* Prioritise RFC attributes */
531  else if (child->attr <= (*bin)->attr) break;
532 
533  bin = &(*bin)->next;
534  }
535 
536  memcpy(&this, &bin, sizeof(this));
537  child->next = *this;
538  *this = child;
539 
540  return 0;
541 }
542 
543 static fr_dict_attr_t *fr_dict_attr_alloc(TALLOC_CTX *ctx,
544  char const *name, unsigned int vendor, int attr,
545  PW_TYPE type, fr_dict_attr_flags_t flags)
546 {
547  fr_dict_attr_t *da;
548  size_t namelen = strlen(name);
549 
550  da = (fr_dict_attr_t *)talloc_zero_array(ctx, uint8_t, sizeof(*da) + namelen);
551  if (!da) {
552  fr_strerror_printf("Out of memory");
553  return NULL;
554  }
555  talloc_set_type(da, fr_dict_attr_t);
556 
557  memcpy(da->name, name, namelen);
558  da->name[namelen] = '\0';
559  da->attr = attr;
560  da->vendor = vendor;
561  da->type = type;
562  da->flags = flags;
563 
564  return da;
565 }
566 
567 /** Add an attribute to the dictionary
568  *
569  * @todo we need to check length of none vendor attributes.
570  *
571  * @param[in] dict of protocol context we're operating in. If NULL the internal
572  * dictionary will be used.
573  * @param[in] parent to add attribute under.
574  * @param[in] name of the attribute.
575  * @param[in] attr number.
576  * @param[in] type of attribute.
577  * @param[in] flags to set in the attribute.
578  * @return
579  * - 0 on success.
580  * - -1 on failure.
581  */
582 int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
583  char const *name, int attr, PW_TYPE type, fr_dict_attr_flags_t flags)
584 {
585  unsigned int vendor;
586  size_t namelen;
587  fr_dict_attr_t *n;
588  fr_dict_attr_t const *v;
589 
590  INTERNAL_IF_NULL(dict);
591 
592  if (!fr_assert(parent)) return -1;
593 
594  namelen = strlen(name);
595  if (namelen >= FR_DICT_ATTR_MAX_NAME_LEN) {
596  fr_strerror_printf("Attribute name too long");
597  error:
598  fr_strerror_printf("fr_dict_attr_add: Failed adding '%s': %s", name, fr_strerror());
599  return -1;
600  }
601 
602  if (fr_dict_valid_name(name) < 0) return -1;
603 
604  /*
605  * type_size is used to limit the maximum attribute number, so it's checked first.
606  */
607  if (flags.type_size) {
608  if ((type != PW_TYPE_TLV) && (type != PW_TYPE_VENDOR)) {
609  fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv'");
610  goto error;
611  }
612 
613  if ((flags.type_size != 1) &&
614  (flags.type_size != 2) &&
615  (flags.type_size != 4)) {
616  fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4");
617  goto error;
618  }
619  }
620 
621  /******************** sanity check attribute number ********************/
622 
623  if (parent->flags.is_root) {
624  static unsigned int max_attr = UINT8_MAX + 1;
625 
626  if (attr == -1) {
627  if (fr_dict_attr_by_name(dict, name)) return 0; /* exists, don't add it again */
628  attr = ++max_attr;
629  flags.internal = 1;
630 
631  } else if (attr <= 0) {
632  fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", attr);
633  goto error;
634 
635  } else if ((unsigned int) attr > max_attr) {
636  max_attr = attr;
637  }
638 
639  /*
640  * Auto-set internal flags for raddb/dictionary.
641  * So that the end user doesn't have to know
642  * about internal implementation of the server.
643  */
644  if ((parent->flags.type_size == 1) &&
645  (attr >= 3000) && (attr < 4000)) {
646  flags.internal = true;
647  }
648  }
649 
650  /*
651  * Any other negative attribute number is wrong.
652  */
653  if (attr < 0) {
654  fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", attr);
655  goto error;
656  }
657 
658  /*
659  * If attributes have number greater than 255, do sanity checks.
660  *
661  * We assume that the root attribute is of type TLV, with
662  * the appropriate flags set for attributes in this
663  * space.
664  */
665  if ((attr > UINT8_MAX) && !flags.internal) {
666  if (parent->flags.is_root && ((attr >= 0x2b00) && (attr < 0x2d00))) { /* @fixme: VMPS */
667  /* ignore it */
668  } else
669 
670  for (v = parent; v != NULL; v = v->parent) {
671  if ((v->type == PW_TYPE_TLV) || (v->type == PW_TYPE_VENDOR)) {
672  if ((v->flags.type_size < 4) &&
673  (attr >= (1 << (8 * v->flags.type_size)))) {
674  fr_strerror_printf("Attributes must have value between 1..%u",
675  (1 << (8 * v->flags.type_size)) - 1);
676  goto error;
677  }
678  break;
679  }
680  }
681  }
682 
683  /******************** sanity check flags ********************/
684 
685  /*
686  * virtual attributes are special.
687  */
688  if (flags.virtual) {
689  if (!parent->flags.is_root) {
690  fr_strerror_printf("The 'virtual' flag can only be used for normal attributes");
691  goto error;
692  }
693 
694  if (attr <= (1 << (8 * parent->flags.type_size))) {
695  fr_strerror_printf("The 'virtual' flag can only be used for non-protocol attributes");
696  goto error;
697  }
698  }
699 
700  /*
701  * Tags can only be used in a few limited situations.
702  */
703  if (flags.has_tag) {
704  if ((type != PW_TYPE_INTEGER) && (type != PW_TYPE_STRING)) {
705  fr_strerror_printf("The 'has_tag' flag can only be used for attributes of type 'integer' "
706  "or 'string'");
707  goto error;
708  }
709 
710  if (!(parent->flags.is_root ||
711  ((parent->type == PW_TYPE_VENDOR) &&
712  (parent->parent && parent->parent->type == PW_TYPE_VSA)))) {
713  fr_strerror_printf("The 'has_tag' flag can only be used with RFC and VSA attributes");
714  goto error;
715  }
716 
717  if (flags.array || flags.has_value || flags.concat || flags.virtual ||
718  flags.length) {
719  fr_strerror_printf("The 'has_tag' flag cannot be used any other flag");
720  goto error;
721  }
722 
723  if (flags.encrypt && (flags.encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
724  fr_strerror_printf("The 'has_tag' flag can only be used with 'encrypt=2'");
725  goto error;
726  }
727  }
728 
729  /*
730  * 'concat' can only be used in a few limited situations.
731  */
732  if (flags.concat) {
733  if (type != PW_TYPE_OCTETS) {
734  fr_strerror_printf("The 'concat' flag can only be used for attributes of type 'octets'");
735  goto error;
736  }
737 
738  if (!parent->flags.is_root) {
739  fr_strerror_printf("The 'concat' flag can only be used with RFC attributes");
740  goto error;
741  }
742 
743  if (flags.array || flags.internal || flags.has_value || flags.virtual ||
744  flags.encrypt || flags.length) {
745  fr_strerror_printf("The 'concat' flag cannot be used any other flag");
746  goto error;
747  }
748  }
749 
750  /*
751  * 'octets[n]' can only be used in a few limited situations.
752  */
753  if (flags.length) {
754  if (flags.array || flags.internal || flags.has_value || flags.virtual) {
755  fr_strerror_printf("The 'octets[...]' syntax cannot be used any other flag");
756  goto error;
757  }
758 
759  if (flags.length > 253) {
760  fr_strerror_printf("Invalid length %d", flags.length);
761  return -1;
762  }
763 
764  if ((type == PW_TYPE_TLV) || (type == PW_TYPE_VENDOR)) {
765  if ((flags.length != 1) &&
766  (flags.length != 2) &&
767  (flags.length != 4)) {
768  fr_strerror_printf("The 'length' flag can only be used with attributes of TLV lengths of 1,2 or 4");
769  goto error;
770  }
771 
772  } else if (type != PW_TYPE_OCTETS) {
773  fr_strerror_printf("The 'length' flag can only be set for attributes of type 'octets'");
774  goto error;
775  }
776  }
777 
778  /*
779  * DHCP options allow for packing multiple values into one option.
780  *
781  * We allow it for DHCP and FreeDHCP dictionaries. Not anywhere else.
782  */
783  if (flags.array) {
784  for (v = parent; v != NULL; v = v->parent) {
785  if (v->type != PW_TYPE_VENDOR) continue;
786 
787  if ((v->attr != 34673) && /* freedhcp */
788  (v->attr != DHCP_MAGIC_VENDOR)) {
789  fr_strerror_printf("The 'array' flag can only be used with DHCP options");
790  goto error;
791  }
792  break;
793  }
794 
795  switch (type) {
796  default:
797  fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
798  fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
799  goto error;
800 
801  case PW_TYPE_IPV4_ADDR:
802  case PW_TYPE_IPV6_ADDR:
803  case PW_TYPE_BYTE:
804  case PW_TYPE_SHORT:
805  case PW_TYPE_INTEGER:
806  case PW_TYPE_DATE:
807  case PW_TYPE_STRING:
808  break;
809  }
810 
811  if (flags.internal || flags.has_value || flags.encrypt || flags.virtual) {
812  fr_strerror_printf("The 'array' flag cannot be used any other flag");
813  goto error;
814  }
815  }
816 
817  /*
818  * 'has_value' should only be set internally. If the
819  * caller sets it, we still sanity check it.
820  */
821  if (flags.has_value) {
822  if (type != PW_TYPE_INTEGER) {
823  fr_strerror_printf("The 'has_value' flag can only be used with attributes "
824  "of type 'integer'");
825  goto error;
826  }
827 
828  if (flags.encrypt || flags.virtual) {
829  fr_strerror_printf("The 'has_value' flag cannot be used with any other flag");
830  goto error;
831  }
832  }
833 
834  if (flags.encrypt) {
835  /*
836  * Stupid hacks for MS-CHAP-MPPE-Keys. The User-Password
837  * encryption method has no provisions for encoding the
838  * length of the data. For User-Password, the data is
839  * (presumably) all printable non-zero data. For
840  * MS-CHAP-MPPE-Keys, the data is binary crap. So... we
841  * MUST specify a length in the dictionary.
842  */
843  if ((flags.encrypt == FLAG_ENCRYPT_USER_PASSWORD) && (type != PW_TYPE_STRING)) {
844  if (type != PW_TYPE_OCTETS) {
845  fr_strerror_printf("The 'encrypt=1' flag can only be used with "
846  "attributes of type 'string'");
847  goto error;
848  }
849 
850  if (flags.length == 0) {
851  fr_strerror_printf("The 'encrypt=1' flag MUST be used with an explicit length for "
852  "'octets' data types");
853  goto error;
854  }
855  }
856 
857  if (flags.encrypt > FLAG_ENCRYPT_ASCEND_SECRET) {
858  fr_strerror_printf("The 'encrypt' flag can only be 0..3");
859  goto error;
860  }
861 
862  switch (type) {
863  default:
864  encrypt_fail:
865  fr_strerror_printf("The 'encrypt' flag cannot be used with attributes of type '%s'",
866  fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
867  goto error;
868 
869  case PW_TYPE_IPV4_ADDR:
870  case PW_TYPE_INTEGER:
871  case PW_TYPE_OCTETS:
872  if (flags.encrypt == FLAG_ENCRYPT_ASCEND_SECRET) goto encrypt_fail;
873 
874  case PW_TYPE_STRING:
875  break;
876  }
877  }
878 
879  /******************** sanity check data types and parents ********************/
880 
881  /*
882  * Enforce restrictions on which data types can appear where.
883  */
884  switch (type) {
885  /*
886  * These types may only be parented from the root of the dictionary
887  */
888  case PW_TYPE_EXTENDED:
890  case PW_TYPE_VSA:
891  if (!parent->flags.is_root) {
892  fr_strerror_printf("Attributes of type '%s' can only be used in the RFC space",
893  fr_int2str(dict_attr_types, type, "?Unknown?"));
894  goto error;
895  }
896  break;
897 
898  /*
899  * EVS may only occur under extended and long extended.
900  */
901  case PW_TYPE_EVS:
902  if ((parent->type != PW_TYPE_EXTENDED) && (parent->type != PW_TYPE_LONG_EXTENDED)) {
903  fr_strerror_printf("Attributes of type 'evs' MUST have a parent of type 'extended', "
904  "instead of '%s'", fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
905  goto error;
906  }
907  break;
908 
909  case PW_TYPE_VENDOR:
910  if ((parent->type != PW_TYPE_VSA) && (parent->type != PW_TYPE_EVS)) {
911  fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' or "
912  "'evs', instead of '%s'",
913  fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
914  goto error;
915  }
916 
917  if (parent->type == PW_TYPE_VSA) {
918  fr_dict_vendor_t const *dv;
919 
920  dv = fr_dict_vendor_by_num(dict, attr);
921  if (dv) {
922  flags.type_size = dv->type;
923  flags.length = dv->length;
924  } else {
925  flags.type_size = 1;
926  flags.length = 1;
927  }
928  } else {
929  flags.type_size = 1;
930  flags.length = 1;
931  }
932  break;
933 
934  case PW_TYPE_TLV:
935  /*
936  * Ensure that type_size and length are set.
937  */
938  for (v = parent; v != NULL; v = v->parent) {
939  if ((v->type == PW_TYPE_TLV) || (v->type == PW_TYPE_VENDOR)) {
940  break;
941  }
942  }
943 
944  /*
945  * root is always PW_TYPE_TLV, so we're OK.
946  */
947  if (!v) {
948  fr_strerror_printf("Attributes of type '%s' require a parent attribute",
949  fr_int2str(dict_attr_types, type, "?Unknown?"));
950  goto error;
951  }
952 
953  /*
954  * Over-ride whatever was there before, so we
955  * don't have multiple formats of VSAs.
956  */
957  flags.type_size = v->flags.type_size;
958  flags.length = v->flags.length;
959  break;
960 
962  /*
963  * RFC 6929 says that this is a terrible idea.
964  */
965  for (v = parent; v != NULL; v = v->parent) {
966  if (v->type == PW_TYPE_VSA) {
967  break;
968  }
969  }
970 
971  if (!v) {
972  fr_strerror_printf("Attributes of type '%s' can only be used in VSA dictionaries",
973  fr_int2str(dict_attr_types, type, "?Unknown?"));
974  goto error;
975  }
976  break;
977 
978  case PW_TYPE_INVALID:
979  case PW_TYPE_TIMEVAL:
980  case PW_TYPE_BOOLEAN:
981  case PW_TYPE_DECIMAL:
983  fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
984  fr_int2str(dict_attr_types, type, "?Unknown?"));
985  goto error;
986 
987  default:
988  break;
989  }
990 
991  /*
992  * Force "length" for data types of fixed length;
993  */
994  switch (type) {
995  case PW_TYPE_BYTE:
996  flags.length = 1;
997  break;
998 
999  case PW_TYPE_SHORT:
1000  flags.length = 2;
1001  break;
1002 
1003  case PW_TYPE_DATE:
1004  case PW_TYPE_IPV4_ADDR:
1005  case PW_TYPE_INTEGER:
1006  case PW_TYPE_SIGNED:
1007  flags.length = 4;
1008  break;
1009 
1010  case PW_TYPE_INTEGER64:
1011  flags.length = 8;
1012  break;
1013 
1014  case PW_TYPE_ETHERNET:
1015  flags.length = 6;
1016  break;
1017 
1018  case PW_TYPE_IFID:
1019  flags.length = 8;
1020  break;
1021 
1022  case PW_TYPE_IPV6_ADDR:
1023  flags.length = 16;
1024  break;
1025 
1026  case PW_TYPE_EXTENDED:
1027  if (!parent->flags.is_root || (attr < 241)) {
1028  fr_strerror_printf("Attributes of type 'extended' MUST be "
1029  "RFC attributes with value >= 241.");
1030  goto error;
1031  }
1032  flags.length = 0;
1033  break;
1034 
1035  case PW_TYPE_LONG_EXTENDED:
1036  if (!parent->flags.is_root || (attr < 241)) {
1037  fr_strerror_printf("Attributes of type 'long-extended' MUST "
1038  "be RFC attributes with value >= 241.");
1039  goto error;
1040  }
1041 
1042  flags.length = 0;
1043  break;
1044 
1045  case PW_TYPE_EVS:
1046  if (attr != PW_VENDOR_SPECIFIC) {
1047  fr_strerror_printf("Attributes of type 'evs' MUST have attribute code 26, got %i", attr);
1048  goto error;
1049  }
1050 
1051  flags.length = 0;
1052  break;
1053 
1054  case PW_TYPE_STRING:
1055  case PW_TYPE_OCTETS:
1056  case PW_TYPE_TLV:
1057  flags.is_pointer = true;
1058  break;
1059 
1060  default:
1061  break;
1062  }
1063 
1064  /*
1065  * Propogate vendor down the attribute tree.
1066  */
1067  if (parent->type == PW_TYPE_VENDOR) {
1068  vendor = parent->attr;
1069  } else {
1070  vendor = parent->vendor;
1071  }
1072 
1073  n = fr_dict_attr_alloc(dict->pool, name, vendor, attr, type, flags);
1074  if (!n) {
1075  oom:
1076  fr_strerror_printf("Out of memory");
1077  goto error;
1078  }
1079 
1080  /*
1081  * Insert the attribute, only if it's not a duplicate.
1082  */
1083  if (!fr_hash_table_insert(dict->attributes_by_name, n)) {
1084  fr_dict_attr_t *a;
1085 
1086  /*
1087  * If the attribute has identical number, then
1088  * ignore the duplicate.
1089  */
1091  if (a && (strcasecmp(a->name, n->name) == 0)) {
1092  if (a->attr != n->attr) {
1093  fr_strerror_printf("Duplicate attribute name");
1094  talloc_free(n);
1095  goto error;
1096  }
1097  }
1098 
1099  if (!fr_hash_table_replace(dict->attributes_by_name, n)) {
1100  fr_strerror_printf("Internal error storing attribute");
1101  talloc_free(n);
1102  goto error;
1103  }
1104  }
1105 
1106  /*
1107  * Hacks for combo-IP
1108  */
1109  if (n->type == PW_TYPE_COMBO_IP_ADDR) {
1110  fr_dict_attr_t *v4, *v6;
1111 
1112  v4 = (fr_dict_attr_t *)talloc_zero_array(dict->pool, uint8_t, sizeof(*v4) + namelen);
1113  if (!v4) goto oom;
1114  talloc_set_type(v4, fr_dict_attr_t);
1115 
1116  v6 = (fr_dict_attr_t *)talloc_zero_array(dict->pool, uint8_t, sizeof(*v6) + namelen);
1117  if (!v6) goto oom;
1118  talloc_set_type(v6, fr_dict_attr_t);
1119 
1120  memcpy(v4, n, sizeof(*v4) + namelen);
1121  v4->type = PW_TYPE_IPV4_ADDR;
1122 
1123  memcpy(v6, n, sizeof(*v6) + namelen);
1124  v6->type = PW_TYPE_IPV6_ADDR;
1125  if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
1126  fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
1127  goto error;
1128  }
1129 
1130  if (!fr_hash_table_replace(dict->attributes_combo, v6)) {
1131  fr_strerror_printf("Failed inserting IPv6 version of combo attribute");
1132  goto error;
1133  }
1134  }
1135 
1136  /*
1137  * Setup parenting for the attribute
1138  */
1139  {
1140  fr_dict_attr_t *mutable;
1141 
1142  memcpy(&mutable, &parent, sizeof(mutable));
1143 
1144  if (fr_dict_attr_child_add(mutable, n) < 0) return -1;
1145  }
1146 
1147  return 0;
1148 }
1149 
1150 /*
1151  * Add a value for an attribute to the dictionary.
1152  */
1153 int fr_dict_enum_add(fr_dict_t *dict, char const *attr, char const *alias, int value)
1154 {
1155  size_t length;
1156  fr_dict_attr_t const *da;
1157  fr_dict_enum_t *dval;
1158 
1159  static fr_dict_attr_t const *last_attr = NULL;
1160 
1161  INTERNAL_IF_NULL(dict);
1162 
1163  if (!*alias) {
1164  fr_strerror_printf("fr_dict_enum_add: empty names are not permitted");
1165  return -1;
1166  }
1167 
1168  if ((length = strlen(alias)) >= FR_DICT_ENUM_MAX_NAME_LEN) {
1169  fr_strerror_printf("fr_dict_enum_add: value name too long");
1170  return -1;
1171  }
1172 
1173  dval = (fr_dict_enum_t *)talloc_zero_array(dict->pool, uint8_t, sizeof(*dval) + length);
1174  if (dval == NULL) {
1175  fr_strerror_printf("fr_dict_enum_add: out of memory");
1176  return -1;
1177  }
1178  talloc_set_type(dval, fr_dict_enum_t);
1179 
1180  strcpy(dval->name, alias);
1181  dval->value = value;
1182 
1183  /*
1184  * Most VALUEs are bunched together by ATTRIBUTE. We can
1185  * save a lot of lookups on dictionary initialization by
1186  * caching the last attribute.
1187  */
1188  if (last_attr && (strcasecmp(attr, last_attr->name) == 0)) {
1189  da = last_attr;
1190  } else {
1191  da = fr_dict_attr_by_name(dict, attr);
1192  last_attr = da;
1193  }
1194 
1195  /*
1196  * Remember which attribute is associated with this
1197  * value, if possible.
1198  */
1199  if (da) {
1200  dval->da = da;
1201 
1202  /*
1203  * Enforce valid values
1204  *
1205  * Don't worry about fixups...
1206  */
1207  switch (da->type) {
1208  case PW_TYPE_BYTE:
1209  if (value > UINT8_MAX) {
1210  talloc_free(dval);
1211  fr_strerror_printf("fr_dict_enum_add: ATTRIBUTEs of type 'byte' cannot have "
1212  "VALUEs larger than %i", UINT8_MAX);
1213  return -1;
1214  }
1215  break;
1216  case PW_TYPE_SHORT:
1217  if (value > UINT16_MAX) {
1218  talloc_free(dval);
1219  fr_strerror_printf("fr_dict_enum_add: ATTRIBUTEs of type 'short' cannot have "
1220  "VALUEs larger than %i", UINT16_MAX);
1221  return -1;
1222  }
1223  break;
1224 
1225  case PW_TYPE_INTEGER:
1226  break;
1227 
1228  default:
1229  talloc_free(dval);
1230  fr_strerror_printf("fr_dict_enum_add: VALUEs cannot be defined for attributes of type '%s'",
1231  fr_int2str(dict_attr_types, da->type, "?Unknown?"));
1232  return -1;
1233  }
1234  } else {
1235  value_fixup_t *fixup;
1236 
1237  fixup = (value_fixup_t *)malloc(sizeof(*fixup));
1238  if (!fixup) {
1239  talloc_free(dval);
1240  fr_strerror_printf("fr_dict_enum_add: out of memory");
1241  return -1;
1242  }
1243  memset(fixup, 0, sizeof(*fixup));
1244 
1245  strlcpy(fixup->attrstr, attr, sizeof(fixup->attrstr));
1246  fixup->dval = dval;
1247 
1248  /*
1249  * Insert to the head of the list.
1250  */
1251  fixup->next = dict->value_fixup;
1252  dict->value_fixup = fixup;
1253 
1254  return 0;
1255  }
1256 
1257  /*
1258  * Add the value into the dictionary.
1259  */
1260  {
1261  fr_dict_attr_t *tmp;
1262  memcpy(&tmp, &dval, sizeof(tmp));
1263 
1264  if (!fr_hash_table_insert(dict->values_by_name, tmp)) {
1265  if (da) {
1266  fr_dict_enum_t *old;
1267 
1268  /*
1269  * Suppress duplicates with the same
1270  * name and value. There are lots in
1271  * dictionary.ascend.
1272  */
1273  old = fr_dict_enum_by_name(dict, da, alias);
1274  if (old && (old->value == dval->value)) {
1275  talloc_free(dval);
1276  return 0;
1277  }
1278  }
1279 
1280  talloc_free(dval);
1281  fr_strerror_printf("fr_dict_enum_add: Duplicate value name %s for attribute %s", alias,
1282  attr);
1283  return -1;
1284  }
1285  }
1286 
1287  /*
1288  * There are multiple VALUE's, keyed by attribute, so we
1289  * take care of that here.
1290  */
1291  if (!fr_hash_table_replace(dict->values_by_da, dval)) {
1292  fr_strerror_printf("fr_dict_enum_add: Failed inserting value %s",
1293  alias);
1294  return -1;
1295  }
1296 
1297  return 0;
1298 }
1299 
1300 /*
1301  * String split routine. Splits an input string IN PLACE
1302  * into pieces, based on spaces.
1303  */
1304 int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
1305 {
1306  int argc = 0;
1307 
1308  while (*str) {
1309  if (argc >= max_argc) break;
1310 
1311  /*
1312  * Chop out comments early.
1313  */
1314  if (*str == '#') {
1315  *str = '\0';
1316  break;
1317  }
1318 
1319  while ((*str == ' ') ||
1320  (*str == '\t') ||
1321  (*str == '\r') ||
1322  (*str == '\n'))
1323  *(str++) = '\0';
1324 
1325  if (!*str) break;
1326 
1327  argv[argc] = str;
1328  argc++;
1329 
1330  while (*str &&
1331  (*str != ' ') &&
1332  (*str != '\t') &&
1333  (*str != '\r') &&
1334  (*str != '\n'))
1335  str++;
1336  }
1337 
1338  return argc;
1339 }
1340 
1341 static int dict_read_sscanf_i(char const *str, unsigned int *pvalue)
1342 {
1343  int rcode = 0;
1344  int base = 10;
1345  static char const *tab = "0123456789";
1346 
1347  if ((str[0] == '0') &&
1348  ((str[1] == 'x') || (str[1] == 'X'))) {
1349  tab = "0123456789abcdef";
1350  base = 16;
1351 
1352  str += 2;
1353  }
1354 
1355  while (*str) {
1356  char const *c;
1357 
1358  if (*str == '.') break;
1359 
1360  c = memchr(tab, tolower((int)*str), base);
1361  if (!c) return 0;
1362 
1363  rcode *= base;
1364  rcode += (c - tab);
1365  str++;
1366  }
1367 
1368  *pvalue = rcode;
1369  return 1;
1370 }
1371 
1372 /*
1373  * Process the ATTRIBUTE command
1374  */
1375 static int dict_read_process_attribute(fr_dict_t *dict, fr_dict_attr_t const *parent,
1376  unsigned int block_vendor, char **argv, int argc)
1377 {
1378  bool oid = false;
1379 
1380  unsigned int vendor = 0;
1381  unsigned int attr;
1382 
1383  int type;
1384  unsigned int length;
1385  fr_dict_attr_flags_t flags;
1386  char *p;
1387 
1388  if ((argc < 3) || (argc > 4)) {
1389  fr_strerror_printf("Invalid ATTRIBUTE syntax");
1390  return -1;
1391  }
1392 
1393  /*
1394  * Dictionaries need to have real names, not shitty ones.
1395  */
1396  if (strncmp(argv[0], "Attr-", 5) == 0) {
1397  fr_strerror_printf("Invalid ATTRIBUTE name");
1398  return -1;
1399  }
1400 
1401  memset(&flags, 0, sizeof(flags));
1402 
1403  /*
1404  * Look for OIDs before doing anything else.
1405  */
1406  if (!strchr(argv[1], '.')) {
1407  /*
1408  * Parse out the attribute number
1409  */
1410  if (!dict_read_sscanf_i(argv[1], &attr)) {
1411  fr_strerror_printf("Invalid ATTRIBUTE number");
1412  return -1;
1413  }
1414 
1415  /*
1416  * Got an OID string. Every attribute should exist other
1417  * than the leaf, which is the attribute we're defining.
1418  */
1419  } else {
1420  ssize_t slen;
1421 
1422  oid = true;
1423  vendor = block_vendor;
1424 
1425  slen = fr_dict_attr_by_oid(dict, &parent, &vendor, &attr, argv[1]);
1426  if (slen <= 0) {
1427  return -1;
1428  }
1429 
1430  if (!fr_assert(parent)) return -1; /* Should have provided us with a parent */
1431 
1432  block_vendor = vendor; /* Weird case where we're processing 26.<vid>.<tlv> */
1433  }
1434 
1435  if (strncmp(argv[2], "octets[", 7) != 0) {
1436  /*
1437  * find the type of the attribute.
1438  */
1439  type = fr_str2int(dict_attr_types, argv[2], -1);
1440  if (type < 0) {
1441  fr_strerror_printf("Unknown data type '%s'", argv[2]);
1442  return -1;
1443  }
1444 
1445  } else {
1446  type = PW_TYPE_OCTETS;
1447 
1448  p = strchr(argv[2] + 7, ']');
1449  if (!p) {
1450  fr_strerror_printf("Invalid format for 'octets'");
1451  return -1;
1452  }
1453 
1454  *p = 0;
1455 
1456  if (!dict_read_sscanf_i(argv[2] + 7, &length)) {
1457  fr_strerror_printf("Invalid length for 'octets'");
1458  return -1;
1459  }
1460 
1461  if ((length == 0) || (length > 253)) {
1462  fr_strerror_printf("Invalid length for 'octets'");
1463  return -1;
1464  }
1465 
1466  flags.length = length;
1467  }
1468 
1469  /*
1470  * Parse options.
1471  */
1472  if (argc >= 4) {
1473  char *key, *next, *last;
1474 
1475  key = argv[3];
1476  do {
1477  next = strchr(key, ',');
1478  if (next) *(next++) = '\0';
1479 
1480  /*
1481  * Boolean flag, means this is a tagged
1482  * attribute.
1483  */
1484  if ((strcmp(key, "has_tag") == 0) || (strcmp(key, "has_tag=1") == 0)) {
1485  flags.has_tag = 1;
1486 
1487  /*
1488  * Encryption method, defaults to 0 (none).
1489  * Currently valid is just type 2,
1490  * Tunnel-Password style, which can only
1491  * be applied to strings.
1492  */
1493  } else if (strncmp(key, "encrypt=", 8) == 0) {
1494  flags.encrypt = strtol(key + 8, &last, 0);
1495  if (*last) {
1496  fr_strerror_printf("Invalid option %s", key);
1497  return -1;
1498  }
1499 
1500  /*
1501  * Marks the attribute up as internal.
1502  * This means it can use numbers outside of the allowed
1503  * protocol range, and also means it will not be included
1504  * in replies or proxy requests.
1505  */
1506  } else if (strncmp(key, "internal", 9) == 0) {
1507  flags.internal = 1;
1508 
1509  } else if (strncmp(key, "array", 6) == 0) {
1510  flags.array = 1;
1511 
1512  } else if (strncmp(key, "concat", 7) == 0) {
1513  flags.concat = 1;
1514 
1515  } else if (strncmp(key, "virtual", 8) == 0) {
1516  flags.virtual = 1;
1517 
1518  /*
1519  * The only thing is the vendor name,
1520  * and it's a known name: allow it.
1521  */
1522  } else if ((key == argv[3]) && !next) {
1523  if (oid) {
1524  fr_strerror_printf("ATTRIBUTE cannot use a 'vendor' flag");
1525  return -1;
1526  }
1527 
1528  if (block_vendor) {
1529  fr_strerror_printf("Vendor flag inside of 'BEGIN-VENDOR' is not allowed");
1530  return -1;
1531  }
1532 
1533  vendor = fr_dict_vendor_by_name(dict, key);
1534  if (!vendor) goto unknown;
1535  break;
1536 
1537  } else {
1538  unknown:
1539  fr_strerror_printf("Unknown option '%s'", key);
1540  return -1;
1541  }
1542 
1543  key = next;
1544  if (key && !*key) break;
1545  } while (key);
1546  }
1547 
1548  if (block_vendor) vendor = block_vendor;
1549 
1550 #ifdef WITH_DICTIONARY_WARNINGS
1551  /*
1552  * Hack to help us discover which vendors have illegal
1553  * attributes.
1554  */
1555  if (!vendor && (attr < 256) &&
1556  !strstr(fn, "rfc") && !strstr(fn, "illegal")) {
1557  fprintf(stderr, "WARNING: Illegal Attribute %s in %s\n",
1558  argv[0], fn);
1559  }
1560 #endif
1561 
1562  /*
1563  * Add it in.
1564  */
1565  if (fr_dict_attr_add(dict, parent, argv[0], attr, type, flags) < 0) {
1566  return -1;
1567  }
1568 
1569  return 0;
1570 }
1571 
1572 /*
1573  * Process the VALUE command
1574  */
1575 static int dict_read_process_value(fr_dict_t *dict, char **argv, int argc)
1576 {
1577  unsigned int value;
1578 
1579  if (argc != 3) {
1580  fr_strerror_printf("Invalid VALUE syntax");
1581  return -1;
1582  }
1583 
1584  /*
1585  * Validate all entries
1586  */
1587  if (!dict_read_sscanf_i(argv[2], &value)) {
1588  fr_strerror_printf("Invalid number in VALUE");
1589  return -1;
1590  }
1591 
1592  if (fr_dict_enum_add(dict, argv[0], argv[1], value) < 0) return -1;
1593  return 0;
1594 }
1595 
1596 
1597 static int dict_read_parse_format(char const *format, unsigned int *pvalue, int *ptype, int *plength,
1598  bool *pcontinuation)
1599 {
1600  char const *p;
1601  int type, length;
1602  bool continuation = false;
1603 
1604  if (strncasecmp(format, "format=", 7) != 0) {
1605  fr_strerror_printf("Invalid format for VENDOR. Expected 'format=', got '%s'",
1606  format);
1607  return -1;
1608  }
1609 
1610  p = format + 7;
1611  if ((strlen(p) < 3) ||
1612  !isdigit((int)p[0]) ||
1613  (p[1] != ',') ||
1614  !isdigit((int)p[2]) ||
1615  (p[3] && (p[3] != ','))) {
1616  fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
1617  p);
1618  return -1;
1619  }
1620 
1621  type = (int)(p[0] - '0');
1622  length = (int)(p[2] - '0');
1623 
1624  if ((type != 1) && (type != 2) && (type != 4)) {
1625  fr_strerror_printf("Invalid type value %d for VENDOR", type);
1626  return -1;
1627  }
1628 
1629  if ((length != 0) && (length != 1) && (length != 2)) {
1630  fr_strerror_printf("Ivalid length value %d for VENDOR", length);
1631  return -1;
1632  }
1633 
1634  if (p[3] == ',') {
1635  if (!p[4]) {
1636  fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
1637  p);
1638  return -1;
1639  }
1640 
1641  if ((p[4] != 'c') ||
1642  (p[5] != '\0')) {
1643  fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
1644  p);
1645  return -1;
1646  }
1647  continuation = true;
1648 
1649  if ((*pvalue != VENDORPEC_WIMAX) ||
1650  (type != 1) || (length != 1)) {
1651  fr_strerror_printf("Only WiMAX VSAs can have continuations");
1652  return -1;
1653  }
1654  }
1655 
1656  *ptype = type;
1657  *plength = length;
1658  *pcontinuation = continuation;
1659  return 0;
1660 }
1661 
1662 /*
1663  * Process the VENDOR command
1664  */
1665 static int dict_read_process_vendor(fr_dict_t *dict, char **argv, int argc)
1666 {
1667  unsigned int value;
1668  int type, length;
1669  bool continuation = false;
1670  fr_dict_vendor_t const *dv;
1671  fr_dict_vendor_t *mutable;
1672 
1673  if ((argc < 2) || (argc > 3)) {
1674  fr_strerror_printf("Invalid VENDOR syntax");
1675  return -1;
1676  }
1677 
1678  /*
1679  * Validate all entries
1680  */
1681  if (!dict_read_sscanf_i(argv[1], &value)) {
1682  fr_strerror_printf("Invalid number in VENDOR");
1683  return -1;
1684  }
1685 
1686  /* Create a new VENDOR entry for the list */
1687  if (fr_dict_vendor_add(dict, argv[0], value) < 0) {
1688  return -1;
1689  }
1690 
1691  /*
1692  * Look for a format statement. Allow it to over-ride the hard-coded formats below.
1693  */
1694  if (argc == 3) {
1695  if (dict_read_parse_format(argv[2], &value, &type, &length, &continuation) < 0) {
1696  return -1;
1697  }
1698 
1699  } else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */
1700  type = 4;
1701  length = 0;
1702 
1703  } else if (value == VENDORPEC_LUCENT) {
1704  type = 2;
1705  length = 1;
1706 
1707  } else if (value == VENDORPEC_STARENT) {
1708  type = 2;
1709  length = 2;
1710 
1711  } else {
1712  type = length = 1;
1713  }
1714 
1715  dv = fr_dict_vendor_by_num(dict, value);
1716  if (!dv) {
1717  fr_strerror_printf("Failed adding format for VENDOR");
1718  return -1;
1719  }
1720 
1721  memcpy(&mutable, &dv, sizeof(mutable));
1722 
1723  mutable->type = type;
1724  mutable->length = length;
1725  mutable->flags = continuation;
1726 
1727  return 0;
1728 }
1729 
1730 /*
1731  * Initialize the dictionary.
1732  */
1733 static int dict_read_init(fr_dict_t *dict, char const *dir_name, char const *filename,
1734  char const *src_file, int src_line)
1735 {
1736  FILE *fp;
1737  char dir[256], fn[256];
1738  char buf[256];
1739  char *p;
1740  int line = 0;
1741  unsigned int vendor;
1742  unsigned int block_vendor;
1743  struct stat statbuf;
1744  char *argv[MAX_ARGV];
1745  int argc;
1746  fr_dict_attr_t const *da;
1747  int block_tlv_depth = 0;
1748  fr_dict_attr_t const *parent = dict->root;
1749  fr_dict_attr_t const *block_tlv[FR_DICT_TLV_NEST_MAX];
1750 
1751  if ((strlen(dir_name) + 3 + strlen(filename)) > sizeof(dir)) {
1752  fr_strerror_printf("fr_dict_init: filename name too long");
1753  return -1;
1754  }
1755 
1756  /*
1757  * If it's an absolute dir, forget the parent dir,
1758  * and remember the new one.
1759  *
1760  * If it's a relative dir, tack on the current filename
1761  * to the parent dir. And use that.
1762  */
1763  if (!FR_DIR_IS_RELATIVE(filename)) {
1764  strlcpy(dir, filename, sizeof(dir));
1765  p = strrchr(dir, FR_DIR_SEP);
1766  if (p) {
1767  p[1] = '\0';
1768  } else {
1769  strlcat(dir, "/", sizeof(dir));
1770  }
1771 
1772  strlcpy(fn, filename, sizeof(fn));
1773  } else {
1774  strlcpy(dir, dir_name, sizeof(dir));
1775  p = strrchr(dir, FR_DIR_SEP);
1776  if (p) {
1777  if (p[1]) strlcat(dir, "/", sizeof(dir));
1778  } else {
1779  strlcat(dir, "/", sizeof(dir));
1780  }
1781  strlcat(dir, filename, sizeof(dir));
1782  p = strrchr(dir, FR_DIR_SEP);
1783  if (p) {
1784  p[1] = '\0';
1785  } else {
1786  strlcat(dir, "/", sizeof(dir));
1787  }
1788 
1789  p = strrchr(filename, FR_DIR_SEP);
1790  if (p) {
1791  snprintf(fn, sizeof(fn), "%s%s", dir, p);
1792  } else {
1793  snprintf(fn, sizeof(fn), "%s%s", dir, filename);
1794  }
1795  }
1796 
1797  /*
1798  * Check if we've loaded this file before. If so, ignore it.
1799  */
1800  p = strrchr(fn, FR_DIR_SEP);
1801  if (p) {
1802  *p = '\0';
1803  if (dict_stat_check(dict, fn, p + 1)) {
1804  *p = FR_DIR_SEP;
1805  return 0;
1806  }
1807  *p = FR_DIR_SEP;
1808  }
1809 
1810  if ((fp = fopen(fn, "r")) == NULL) {
1811  if (!src_file) {
1812  fr_strerror_printf("fr_dict_init: Couldn't open dictionary '%s': %s",
1813  fn, fr_syserror(errno));
1814  } else {
1815  fr_strerror_printf("fr_dict_init: %s[%d]: Couldn't open dictionary '%s': %s",
1816  src_file, src_line, fn, fr_syserror(errno));
1817  }
1818  return -2;
1819  }
1820 
1821  /*
1822  * If fopen works, this works.
1823  */
1824  if (stat(fn, &statbuf) < 0) {
1825  fclose(fp);
1826  return -1;
1827  }
1828 
1829  if (!S_ISREG(statbuf.st_mode)) {
1830  fclose(fp);
1831  fr_strerror_printf("fr_dict_init: Dictionary '%s' is not a regular file", fn);
1832  return -1;
1833  }
1834 
1835  /*
1836  * Globally writable dictionaries means that users can control
1837  * the server configuration with little difficulty.
1838  */
1839 #ifdef S_IWOTH
1840  if ((statbuf.st_mode & S_IWOTH) != 0) {
1841  fclose(fp);
1842  fr_strerror_printf("fr_dict_init: Dictionary '%s' is globally writable. Refusing to start "
1843  "due to insecure configuration", fn);
1844  return -1;
1845  }
1846 #endif
1847 
1848  dict_stat_add(dict, &statbuf);
1849 
1850  /*
1851  * Seed the random pool with data.
1852  */
1853  fr_rand_seed(&statbuf, sizeof(statbuf));
1854 
1855  block_vendor = 0;
1856 
1857  while (fgets(buf, sizeof(buf), fp) != NULL) {
1858  line++;
1859 
1860  switch (buf[0]) {
1861  case '#':
1862  case '\0':
1863  case '\n':
1864  case '\r':
1865  continue;
1866  }
1867 
1868  /*
1869  * Comment characters should NOT be appearing anywhere but
1870  * as start of a comment;
1871  */
1872  p = strchr(buf, '#');
1873  if (p) *p = '\0';
1874 
1875  argc = fr_dict_str_to_argv(buf, argv, MAX_ARGV);
1876  if (argc == 0) continue;
1877 
1878  if (argc == 1) {
1879  fr_strerror_printf("Invalid entry");
1880 
1881  error:
1882  fr_strerror_printf("fr_dict_init: %s[%d]: %s", fn, line, fr_strerror());
1883  fclose(fp);
1884  return -1;
1885  }
1886 
1887  /*
1888  * Process VALUE lines.
1889  */
1890  if (strcasecmp(argv[0], "VALUE") == 0) {
1891  if (dict_read_process_value(dict, argv + 1, argc - 1) == -1) goto error;
1892  continue;
1893  }
1894 
1895  /*
1896  * Perhaps this is an attribute.
1897  */
1898  if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
1899  if (dict_read_process_attribute(dict, parent, block_vendor,
1900  argv + 1, argc - 1) == -1) goto error;
1901  continue;
1902  }
1903 
1904  /*
1905  * See if we need to import another dictionary.
1906  */
1907  if (strcasecmp(argv[0], "$INCLUDE") == 0) {
1908  if (dict_read_init(dict, dir, argv[1], fn, line) < 0) goto error;
1909  continue;
1910  } /* $INCLUDE */
1911 
1912  /*
1913  * Optionally include a dictionary
1914  */
1915  if (strcasecmp(argv[0], "$INCLUDE-") == 0) {
1916  int rcode = dict_read_init(dict, dir, argv[1], fn, line);
1917 
1918  if (rcode == -2) {
1919  fr_strerror_printf(NULL); /* reset error to nothing */
1920  continue;
1921  }
1922 
1923  if (rcode < 0) goto error;
1924  continue;
1925  } /* $INCLUDE- */
1926 
1927  /*
1928  * Process VENDOR lines.
1929  */
1930  if (strcasecmp(argv[0], "VENDOR") == 0) {
1931  if (dict_read_process_vendor(dict, argv + 1, argc - 1) == -1) goto error;
1932  continue;
1933  }
1934 
1935  if (strcasecmp(argv[0], "BEGIN-TLV") == 0) {
1936  fr_dict_attr_t const *common;
1937 
1938  if ((block_tlv_depth + 1) > FR_DICT_TLV_NEST_MAX) {
1939  fr_strerror_printf("TLVs are nested too deep");
1940  goto error;
1941  }
1942 
1943  if (argc != 2) {
1944  fr_strerror_printf("Invalid BEGIN-TLV entry");
1945  goto error;
1946  }
1947 
1948  da = fr_dict_attr_by_name(dict, argv[1]);
1949  if (!da) {
1950  fr_strerror_printf("Unknown attribute '%s'", argv[1]);
1951  goto error;
1952  }
1953 
1954  if (da->type != PW_TYPE_TLV) {
1955  fr_strerror_printf("Attribute '%s' should be a 'tlv', but is a '%s'",
1956  argv[1],
1957  fr_int2str(dict_attr_types, da->type, "?Unknown?"));
1958  goto error;
1959  }
1960 
1961  common = fr_dict_parent_common(parent, da, true);
1962  if (!common || common->flags.is_root ||
1963  (common->type == PW_TYPE_VSA) ||
1964  (common->type == PW_TYPE_EVS)) {
1965  fr_strerror_printf("Attribute '%s' is not a child of '%s'", argv[1], parent->name);
1966  goto error;
1967  }
1968  block_tlv[block_tlv_depth++] = parent;
1969  parent = da;
1970  continue;
1971  } /* BEGIN-TLV */
1972 
1973  if (strcasecmp(argv[0], "END-TLV") == 0) {
1974  if (--block_tlv_depth < 0) {
1975  fr_strerror_printf("Too many END-TLV entries. Mismatch at END-TLV %s", argv[1]);
1976  goto error;
1977  }
1978 
1979  if (argc != 2) {
1980  fr_strerror_printf("Invalid END-TLV entry");
1981  goto error;
1982  }
1983 
1984  da = fr_dict_attr_by_name(dict, argv[1]);
1985  if (!da) {
1986  fr_strerror_printf("Unknown attribute '%s'", argv[1]);
1987  goto error;
1988  }
1989 
1990  if (da != parent) {
1991  fr_strerror_printf("END-TLV %s does not match previous BEGIN-TLV %s", argv[1],
1992  parent->name);
1993  goto error;
1994  }
1995  parent = block_tlv[block_tlv_depth];
1996  continue;
1997  } /* END-VENDOR */
1998 
1999  if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) {
2000  fr_dict_attr_flags_t flags;
2001 
2002  fr_dict_attr_t const *vsa_da;
2003  fr_dict_attr_t *new;
2004  fr_dict_attr_t *mutable;
2005 
2006  if (argc < 2) {
2007  fr_strerror_printf("Invalid BEGIN-VENDOR entry");
2008  goto error;
2009  }
2010 
2011  vendor = fr_dict_vendor_by_name(dict, argv[1]);
2012  if (!vendor) {
2013  fr_strerror_printf("Unknown vendor '%s'", argv[1]);
2014  goto error;
2015  }
2016 
2017  /*
2018  * Check for extended attr VSAs
2019  *
2020  * BEGIN-VENDOR foo format=Foo-Encapsulation-Attr
2021  */
2022  if (argc > 2) {
2023  if (strncmp(argv[2], "format=", 7) != 0) {
2024  fr_strerror_printf("Invalid format %s", argv[2]);
2025  goto error;
2026  }
2027 
2028  p = argv[2] + 7;
2029  da = fr_dict_attr_by_name(dict, p);
2030  if (!da) {
2031  fr_strerror_printf("Invalid format for BEGIN-VENDOR: Unknown attribute '%s'",
2032  p);
2033  goto error;
2034  }
2035 
2036  if (da->type != PW_TYPE_EVS) {
2037  fr_strerror_printf("Invalid format for BEGIN-VENDOR. Attribute '%s' should "
2038  "be 'evs' but is '%s'", p,
2039  fr_int2str(dict_attr_types, da->type, "?Unknown?"));
2040  goto error;
2041  }
2042 
2043  vsa_da = da;
2044  } else {
2045  /*
2046  * Automagically create Attribute 26
2047  *
2048  * This should exist, but in case we're starting without
2049  * the RFC dictionaries we need to add it in the case
2050  * it doesn't.
2051  */
2052  vsa_da = fr_dict_attr_child_by_num(parent, PW_VENDOR_SPECIFIC);
2053  if (!vsa_da) {
2054  memset(&flags, 0, sizeof(flags));
2055 
2056  memcpy(&mutable, &parent, sizeof(mutable));
2057  new = fr_dict_attr_alloc(mutable, "Vendor-Specific", 0,
2058  PW_VENDOR_SPECIFIC, PW_TYPE_VSA, flags);
2059  fr_dict_attr_child_add(mutable, new);
2060  vsa_da = new;
2061  }
2062  }
2063 
2064  /*
2065  * Create a VENDOR attribute on the fly, either in the context
2066  * of the EVS attribute, or the VSA (26) attribute.
2067  */
2068  parent = fr_dict_attr_child_by_num(vsa_da, vendor);
2069  if (!parent) {
2070  memset(&flags, 0, sizeof(flags));
2071 
2072  if (vsa_da->type == PW_TYPE_VSA) {
2073  fr_dict_vendor_t const *dv;
2074 
2075  dv = fr_dict_vendor_by_num(dict, vendor);
2076  if (dv) {
2077  flags.type_size = dv->type;
2078  flags.length = dv->length;
2079 
2080  } else { /* unknown vendor, shouldn't happen */
2081  flags.type_size = 1;
2082  flags.length = 1;
2083  }
2084 
2085  } else { /* EVS are always "format=1,1" */
2086  flags.type_size = 1;
2087  flags.length = 1;
2088  }
2089 
2090  memcpy(&mutable, &vsa_da, sizeof(mutable));
2091  new = fr_dict_attr_alloc(mutable, argv[1], 0, vendor, PW_TYPE_VENDOR, flags);
2092  fr_dict_attr_child_add(mutable, new);
2093 
2094  parent = new;
2095  }
2096  block_vendor = vendor;
2097  continue;
2098  } /* BEGIN-VENDOR */
2099 
2100  if (strcasecmp(argv[0], "END-VENDOR") == 0) {
2101  if (argc != 2) {
2102  fr_strerror_printf("Invalid END-VENDOR entry");
2103  goto error;
2104  }
2105 
2106  vendor = fr_dict_vendor_by_name(dict, argv[1]);
2107  if (!vendor) {
2108  fr_strerror_printf("Unknown vendor '%s'", argv[1]);
2109  goto error;
2110  }
2111 
2112  if (vendor != block_vendor) {
2113  fr_strerror_printf("END-VENDOR '%s' does not match any previous BEGIN-VENDOR",
2114  argv[1]);
2115  goto error;
2116  }
2117  parent = dict->root;
2118  block_vendor = 0;
2119  continue;
2120  } /* END-VENDOR */
2121 
2122  /*
2123  * Any other string: We don't recognize it.
2124  */
2125  fr_strerror_printf("Invalid keyword '%s'", argv[0]);
2126  goto error;
2127  }
2128  fclose(fp);
2129  return 0;
2130 }
2131 
2132 /** (re)initialize a protocol dictionary
2133  *
2134  * Initialize the directory, then fix the attr member of all attributes.
2135  *
2136  * First dictionary initialised will be set as the default internal dictionary.
2137  *
2138  * @param[in] ctx to allocate the dictionary from.
2139  * @param[out] out Where to write a pointer to the new dictionary. Will free existing
2140  * dictionary if files have changed and *out is not NULL.
2141  * @param[in] dir to read dictionary files from.
2142  * @param[in] fn file name to read.
2143  * @param[in] name to use for the root attributes.
2144  * @return
2145  * - 0 on success.
2146  * - -1 on failure.
2147  */
2148 int fr_dict_init(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *fn, char const *name)
2149 {
2150  fr_dict_t *dict;
2151 
2152  if (!*out) {
2153  /* Pre-Allocate 5MB of pool memory for rapid startup */
2154  dict = talloc_zero(ctx, fr_dict_t);
2155  dict->pool = talloc_pool(dict, (1024 * 1024 * 5));
2156  } else {
2157  dict = *out;
2158  if (dict_stat_check(dict, dir, fn)) return 0;
2159  }
2160 
2161  /*
2162  * Free the old dictionaries
2163  */
2164  if (*out == fr_dict_internal) fr_dict_internal = dict;
2165  TALLOC_FREE(*out);
2166 
2167  /*
2168  * Remove this at some point...
2169  */
2170  if (!fr_dict_internal) fr_dict_internal = dict;
2171 
2172  /*
2173  * Create the table of vendor by name. There MAY NOT
2174  * be multiple vendors of the same name.
2175  *
2176  * Each vendor is malloc'd, so the free function is free.
2177  */
2179  if (!dict->vendors_by_name) {
2180  error:
2181  talloc_free(dict);
2182  return -1;
2183  }
2184 
2185  /*
2186  * Create the table of vendors by value. There MAY
2187  * be vendors of the same value. If there are, we
2188  * pick the latest one.
2189  */
2191  if (!dict->vendors_by_num) goto error;
2192 
2193  /*
2194  * Create the table of attributes by name. There MAY NOT
2195  * be multiple attributes of the same name.
2196  *
2197  * Each attribute is malloc'd, so the free function is free.
2198  */
2200  if (!dict->attributes_by_name) goto error;
2201 
2202  /*
2203  * Horrible hacks for combo-IP.
2204  */
2206  if (!dict->attributes_combo) goto error;
2207 
2209  if (!dict->values_by_name) goto error;
2210 
2212  if (!dict->values_by_da) goto error;
2213 
2214  /*
2215  * Magic dictionary root attribute
2216  */
2217  dict->root = (fr_dict_attr_t *)talloc_zero_array(dict, uint8_t, sizeof(fr_dict_attr_t) + strlen(name));
2218  strcpy(dict->root->name, name);
2219  talloc_set_type(dict->root, fr_dict_attr_t);
2220  dict->root->flags.is_root = 1;
2221  dict->root->type = PW_TYPE_TLV;
2222  dict->root->flags.type_size = 1;
2223  dict->root->flags.length = 1;
2224 
2225  dict->value_fixup = NULL; /* just to be safe. */
2226 
2227  if (dict_read_init(dict, dir, fn, NULL, 0) < 0) goto error;
2228 
2229  if (dict->value_fixup) {
2230  fr_dict_attr_t const *a;
2231  value_fixup_t *this, *next;
2232 
2233  for (this = dict->value_fixup; this != NULL; this = next) {
2234  next = this->next;
2235 
2236  a = fr_dict_attr_by_name(dict, this->attrstr);
2237  if (!a) {
2238  fr_strerror_printf("fr_dict_init: No ATTRIBUTE '%s' defined for VALUE '%s'",
2239  this->attrstr, this->dval->name);
2240  goto error; /* leak, but they should die... */
2241  }
2242 
2243  this->dval->da = a;
2244 
2245  /*
2246  * Add the value into the dictionary.
2247  */
2248  if (!fr_hash_table_replace(dict->values_by_name, this->dval)) {
2249  fr_strerror_printf("fr_dict_enum_add: Duplicate value name %s for attribute %s",
2250  this->dval->name, a->name);
2251  goto error;
2252  }
2253 
2254  /*
2255  * Allow them to use the old name, but
2256  * prefer the new name when printing
2257  * values.
2258  */
2259  if (a->parent->flags.is_root || ((a->parent->type == PW_TYPE_VENDOR) &&
2260  (a->parent->parent->type == PW_TYPE_VSA))) {
2261  if (!fr_hash_table_finddata(dict->values_by_da, this->dval)) {
2262  fr_hash_table_replace(dict->values_by_da, this->dval);
2263  }
2264  }
2265  free(this);
2266 
2267  /*
2268  * Just so we don't lose track of things.
2269  */
2270  dict->value_fixup = next;
2271  }
2272  }
2273 
2274  /*
2275  * Walk over all of the hash tables to ensure they're
2276  * initialized. We do this because the threads may perform
2277  * lookups, and we don't want multi-threaded re-ordering
2278  * of the table entries. That would be bad.
2279  */
2282 
2285 
2286  if (out) *out = dict;
2287 
2288  return 0;
2289 }
2290 
2291 int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
2292 {
2293  INTERNAL_IF_NULL(dict);
2294 
2295  if (!dict->attributes_by_name) {
2296  fr_strerror_printf("Must call fr_dict_init() before fr_dict_read()");
2297  return -1;
2298  }
2299 
2300  return dict_read_init(dict, dir, filename, NULL, 0);
2301 }
2302 
2303 /*
2304  * External API for testing
2305  */
2306 int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent, unsigned int vendor)
2307 {
2308  int argc;
2309  char *argv[MAX_ARGV];
2310 
2311  INTERNAL_IF_NULL(dict);
2312 
2313  argc = fr_dict_str_to_argv(buf, argv, MAX_ARGV);
2314  if (argc == 0) return 0;
2315 
2316  if (strcasecmp(argv[0], "VALUE") == 0) {
2317  return dict_read_process_value(dict, argv + 1, argc - 1);
2318  }
2319 
2320  if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
2321  if (!parent) parent = fr_dict_root(dict);
2322 
2323  return dict_read_process_attribute(dict, parent, vendor, argv + 1, argc - 1);
2324  }
2325 
2326  if (strcasecmp(argv[0], "VENDOR") == 0) {
2327  return dict_read_process_vendor(dict, argv + 1, argc - 1);
2328  }
2329 
2330  fr_strerror_printf("Invalid input '%s'", argv[0]);
2331  return -1;
2332 }
2333 
2334 /** Return the root attribute of a dictionary
2335  *
2336  * @param dict to return root for.
2337  * @return the root attribute of the dictionary.
2338  */
2340 {
2341  return dict->root;
2342 }
2343 
2344 /** Copy a known or unknown attribute to produce an unknown attribute
2345  *
2346  * Will copy the complete hierarchy down to the first known attribute.
2347  */
2348 static fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
2349 {
2350  fr_dict_attr_t *new, *new_parent = NULL;
2351  fr_dict_attr_t const *parent;
2352 
2353  if (da->parent->flags.is_unknown) {
2354  new_parent = fr_dict_unknown_acopy(ctx, da->parent);
2355  parent = new_parent;
2356  } else {
2357  parent = da->parent;
2358  }
2359 
2360  new = fr_dict_attr_alloc(ctx, da->name, da->vendor, da->attr, da->type, da->flags);
2361  new->flags.is_unknown = 1;
2362  new->parent = parent;
2363  new->depth = da->depth;
2364 
2365  /*
2366  * Inverted tallloc hierarchy.
2367  */
2368  if (new_parent) talloc_steal(new, parent);
2369 
2370  return new;
2371 }
2372 
2373 /** Converts an unknown to a known by adding it to the internal dictionaries.
2374  *
2375  * Does not free old #fr_dict_attr_t, that is left up to the caller.
2376  *
2377  * @param[in] dict of protocol context we're operating in. If NULL the internal
2378  * dictionary will be used.
2379  * @param[in] old unknown attribute to add.
2380  * @return
2381  * - Existing #fr_dict_attr_t if old was found in a dictionary.
2382  * - A new entry representing old.
2383  */
2385 {
2386  fr_dict_attr_t const *da;
2387  fr_dict_attr_t const *parent;
2388  fr_dict_attr_flags_t flags;
2389 
2390  if (!old) return NULL;
2391 
2392  /*
2393  * Define the complete unknown hierarchy
2394  */
2395  if (old->parent->flags.is_unknown) {
2396  parent = fr_dict_unknown_add(dict, old->parent);
2397  } else {
2398  parent = old->parent;
2399  }
2400 
2401  da = fr_dict_attr_child_by_num(parent, old->attr);
2402  if (da) return da;
2403 
2404  memcpy(&flags, &old->flags, sizeof(flags));
2405  flags.is_unknown = false;
2406 
2407  /*
2408  * Ensure the vendor is present in the
2409  * vendor hash.
2410  */
2411  if (old->type == PW_TYPE_VENDOR) if (fr_dict_vendor_add(dict, old->name, old->attr) < 0) return NULL;
2412 
2413  if (fr_dict_attr_add(dict, old->parent, old->name, old->attr, old->type, flags) < 0) return NULL;
2414 
2415  da = fr_dict_attr_child_by_num(parent, old->attr);
2416  return da;
2417 }
2418 
2419 /** Free dynamically allocated (unknown attributes)
2420  *
2421  * If the da was dynamically allocated it will be freed, else the function
2422  * will return without doing anything.
2423  *
2424  * @param da to free.
2425  */
2427 {
2428  fr_dict_attr_t **tmp;
2429 
2430  if (!da || !*da) return;
2431 
2432  /* Don't free real DAs */
2433  if (!(*da)->flags.is_unknown) {
2434  return;
2435  }
2436 
2437  memcpy(&tmp, &da, sizeof(*tmp));
2438  talloc_free(*tmp);
2439 
2440  *tmp = NULL;
2441 }
2442 
2443 /** Build an unknown vendor, parented by a VSA or EVS attribute
2444  *
2445  * This allows us to complete the path back to the dictionary root in the case
2446  * of unknown attributes with unknown vendors.
2447  *
2448  * @note Will return known vendors attributes where possible. Do not free directly,
2449  * use #fr_dict_unknown_free.
2450  *
2451  * @param[in] ctx to allocate the vendor attribute in.
2452  * @param[out] out Where to write point to new unknown dict attr representing the unknown vendor.
2453  * @param[in] parent of the vendor attribute, either an EVS or VSA attribute.
2454  * @param[in] vendor id.
2455  * @return
2456  * - 0 on success.
2457  * - -1 on failure.
2458  */
2459 int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const **out,
2460  fr_dict_attr_t const *parent, unsigned int vendor)
2461 {
2462  fr_dict_attr_flags_t flags;
2463  fr_dict_attr_t const *vendor_da;
2464  fr_dict_attr_t *new;
2465 
2466  *out = NULL;
2467 
2468  memset(&flags, 0, sizeof(flags));
2469  flags.is_unknown = 1;
2470  flags.type_size = 1;
2471  flags.length = 1;
2472 
2473  /*
2474  * Vendor attributes can occur under VSA or EVS attributes.
2475  */
2476  switch (parent->type) {
2477  case PW_TYPE_VSA:
2478  case PW_TYPE_EVS:
2479  if (!fr_assert(!parent->flags.is_unknown)) return -1;
2480 
2481  vendor_da = fr_dict_attr_child_by_num(parent, vendor);
2482  if (vendor_da) {
2483  if (!fr_assert(vendor_da->type == PW_TYPE_VENDOR)) return -1;
2484  *out = vendor_da;
2485  return 0;
2486  }
2487  break;
2488 
2489  /*
2490  * NOOP (maybe)
2491  */
2492  case PW_TYPE_VENDOR:
2493  if (!fr_assert(!parent->flags.is_unknown)) return -1;
2494 
2495  if (parent->attr == vendor) {
2496  *out = parent;
2497  return 0;
2498  }
2499  fr_strerror_printf("Unknown vendor cannot be parented by another vendor");
2500  return -1;
2501 
2502  default:
2503  fr_strerror_printf("Unknown vendors can only be parented by 'vsa' or 'evs' "
2504  "attributes, not '%s'", fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
2505  return -1;
2506  }
2507 
2508  new = fr_dict_attr_alloc(ctx, "unknown-vendor", 0, vendor, PW_TYPE_VENDOR, flags);
2509  new->parent = parent;
2510  new->depth = parent->depth + 1;
2511 
2512  *out = new;
2513 
2514  return 0;
2515 }
2516 
2517 /** Build the tlv_stack for the specified DA and encode the path in OID form
2518  *
2519  * @param[out] buffer Where to write the OID.
2520  * @param[in] outlen Length of the output buffer.
2521  * @param[in] ancestor If not NULL, only print OID portion between ancestor and da.
2522  * @param[in] da to print OID string for.
2523  * @return the number of bytes written to the buffer.
2524  */
2525 size_t dict_print_attr_oid(char *buffer, size_t outlen,
2526  fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da)
2527 {
2528  size_t len;
2529  char *p = buffer, *end = p + outlen;
2530  int i;
2531  int depth = 0;
2532  fr_dict_attr_t const *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
2533 
2534  fr_proto_tlv_stack_build(tlv_stack, da);
2535 
2536  if (ancestor) {
2537  depth = ancestor->depth - 1;
2538  if (tlv_stack[depth] != ancestor) {
2539  fr_strerror_printf("Attribute \"%s\" is not a descendent of \"%s\"", da->name, ancestor->name);
2540  return -1;
2541  }
2542  }
2543 
2544  len = snprintf(p, end - p, "%u", tlv_stack[depth]->attr);
2545  if ((p + len) >= end) return p - buffer;
2546  p += len;
2547 
2548  for (i = depth + 1; i < (int)da->depth; i++) {
2549  len = snprintf(p, end - p, ".%u", tlv_stack[i]->attr);
2550  if ((p + len) >= end) return p - buffer;
2551  p += len;
2552  }
2553 
2554  return p - buffer;
2555 }
2556 
2557 /** Initialises an unknown attribute
2558  *
2559  * Initialises a dict attr for an unknown attribute/vendor/type without adding
2560  * it to dictionary pools/hashes.
2561  *
2562  * Unknown attributes are used to transparently pass undecodeable attributes
2563  * when we proxy requests.
2564  *
2565  * @param[in,out] da struct to initialise, must be at least FR_DICT_ATTR_SIZE bytes.
2566  * @param[in] parent of the unknown attribute (may also be unknown).
2567  * @param[in] attr number.
2568  * @param[in] vendor number.
2569  * @return 0 on success.
2570  */
2571 int fr_dict_unknown_from_fields(fr_dict_attr_t *da, fr_dict_attr_t const *parent, unsigned int vendor,
2572  unsigned int attr)
2573 {
2574  char *p;
2575  size_t len = 0;
2576  size_t bufsize = FR_DICT_ATTR_MAX_NAME_LEN;
2577 
2578  memset(da, 0, FR_DICT_ATTR_SIZE);
2579 
2580  da->attr = attr;
2581  da->vendor = vendor;
2582  da->type = PW_TYPE_OCTETS;
2583  da->flags.is_unknown = true;
2584  da->flags.is_pointer = true;
2585  da->parent = parent;
2586  da->depth = parent->depth + 1;
2587 
2588  p = da->name;
2589 
2590  len = snprintf(p, bufsize, "Attr-");
2591  p += len;
2592  bufsize -= len;
2593 
2594  dict_print_attr_oid(p, bufsize, NULL, da);
2595 
2596  return 0;
2597 }
2598 
2599 /** Allocates an unknown attribute
2600  *
2601  * @copybrief fr_dict_unknown_from_fields
2602  *
2603  * @note If vendor != 0, an unknown vendor (may) also be created, parented by
2604  * the correct EVS or VSA attribute. This is accessible via vp->parent,
2605  * and will be use the unknown da as its talloc parent.
2606  *
2607  * @param[in] ctx to allocate DA in.
2608  * @param[in] parent of the unknown attribute (may also be unknown).
2609  * @param[in] attr number.
2610  * @param[in] vendor number.
2611  * @return 0 on success.
2612  */
2614  unsigned int vendor, unsigned int attr)
2615 {
2616  uint8_t *p;
2617  fr_dict_attr_t *da;
2618  fr_dict_attr_t const *new_parent = NULL;
2619 
2620  /*
2621  * If there's a vendor specified, we check to see
2622  * if the parent is a VSA or EVS, and if it is
2623  * we either lookup the vendor to get the correct
2624  * attribute, or bridge the gap in the tree, with an
2625  * unknown vendor.
2626  *
2627  * We need to do the check, as the parent could be
2628  * a TLV, in which case the vendor should be known
2629  * and we don't need to modify the parent.
2630  */
2631  if (vendor && ((parent->type == PW_TYPE_VSA) || (parent->type == PW_TYPE_EVS))) {
2632  new_parent = fr_dict_attr_child_by_num(parent, vendor);
2633  if (!new_parent && (fr_dict_unknown_vendor_afrom_num(ctx, &new_parent, parent, vendor) < 0)) {
2634  return NULL;
2635  }
2636  parent = new_parent;
2637  /*
2638  * Need to clone the unknown hierachy, as unknown
2639  * attributes must parent the complete heirachy,
2640  * and cannot share any parts with any other unknown
2641  * attributes.
2642  */
2643  } else if (parent->flags.is_unknown) {
2644  new_parent = fr_dict_unknown_acopy(ctx, parent);
2645  parent = new_parent;
2646  }
2647 
2648  p = talloc_zero_array(ctx, uint8_t, FR_DICT_ATTR_SIZE);
2649  if (!p) {
2650  fr_strerror_printf("Out of memory");
2651  fr_dict_unknown_free(&new_parent);
2652  return NULL;
2653  }
2654  da = (fr_dict_attr_t *)p;
2655  talloc_set_type(da, fr_dict_attr_t);
2656 
2657  if (!fr_assert(parent)) { /* coverity */
2658  talloc_free(p);
2659  return NULL;
2660  }
2661 
2662  if (fr_dict_unknown_from_fields(da, parent, vendor, attr) < 0) {
2663  talloc_free(p);
2664  fr_dict_unknown_free(&new_parent);
2665  return NULL;
2666  }
2667 
2668  /*
2669  * Ensure the parent is freed at the same time as the
2670  * unknown DA. This should be OK as we never parent
2671  * multiple unknown attributes off the same parent.
2672  */
2673  if (new_parent && new_parent->flags.is_unknown) talloc_steal(da, new_parent);
2674 
2675  return da;
2676 }
2677 
2678 /** Initialise a fr_dict_attr_t from an ASCII attribute and value
2679  *
2680  * Where the attribute name is in the form:
2681  * - Attr-%d
2682  * - Attr-%d.%d.%d...
2683  * - Vendor-%d-Attr-%d
2684  * - VendorName-Attr-%d
2685  *
2686  * @copybrief fr_dict_unknown_from_fields
2687  *
2688  * @note We can't validate attribute numbers here as a dictionary
2689  * lookup is required to determine if the attribute
2690  * has been marked as internal.
2691  * Even validating numbers based on dv_type which is the
2692  * length of the vendor field is wrong. Attribute number
2693  * checks must be done by the caller.
2694  *
2695  * @param[in] dict of protocol context we're operating in. If NULL the internal
2696  * dictionary will be used.
2697  * @param[in] vendor_da to initialise.
2698  * @param[in] da to initialise.
2699  * @param[in] parent of the unknown attribute (may also be unknown).
2700  * @param[in] name of attribute.
2701  * @return
2702  * - 0 on success.
2703  * - -1 on failure.
2704  */
2706  fr_dict_attr_t const *parent, char const *name)
2707 {
2708  unsigned int attr, vendor = 0;
2709  unsigned long num;
2710 
2711  char const *p = name;
2712  char *q;
2713 
2714  fr_dict_vendor_t const *dv = NULL;
2715  fr_dict_attr_t const *child;
2716 
2717  INTERNAL_IF_NULL(dict);
2718 
2719  if (fr_dict_valid_name(name) < 0) return -1;
2720 
2721  if (vendor_da) memset(vendor_da, 0, sizeof(*vendor_da));
2722  if (da) memset(da, 0, sizeof(*da));
2723 
2724  /*
2725  * Pull off vendor prefix first.
2726  */
2727  if (strncasecmp(p, "Attr-", 5) != 0) {
2728  if (strncasecmp(p, "Vendor-", 7) == 0) {
2729  num = strtoul(p + 7, &q, 10);
2730  if (!num || (num >= UINT_MAX)) {
2731  fr_strerror_printf("Invalid vendor value in attribute name '%s'", name);
2732 
2733  return -1;
2734  }
2735  vendor = num;
2736 
2737  p = q;
2738 
2739  /* must be vendor name */
2740  } else {
2741  char buffer[256];
2742 
2743  q = strchr(p, '-');
2744 
2745  if (!q) {
2746  fr_strerror_printf("Invalid vendor name in attribute name '%s'", name);
2747  return -1;
2748  }
2749 
2750  if ((size_t)(q - p) >= sizeof(buffer)) {
2751  fr_strerror_printf("Vendor name too long in attribute name '%s'", name);
2752 
2753  return -1;
2754  }
2755 
2756  memcpy(buffer, p, (q - p));
2757  buffer[q - p] = '\0';
2758 
2759  vendor = fr_dict_vendor_by_name(dict, buffer);
2760  if (!vendor) {
2761  fr_strerror_printf("Unknown name '%s'", name);
2762 
2763  return -1;
2764  }
2765 
2766  p = q;
2767  }
2768 
2769  /*
2770  * In both the above cases the context for the vendor
2771  * attribute has been omitted, so we need to fixup
2772  * the parent.
2773  */
2774  if (!parent->flags.is_root) {
2775  fr_strerror_printf("Vendor specified without context, but parent is not root");
2776  return -1;
2777  }
2778 
2779  /*
2780  * Assume the context is VSA (26)
2781  */
2782  child = fr_dict_attr_child_by_num(parent, PW_VENDOR_SPECIFIC);
2783  if (!child) {
2784  fr_strerror_printf("Missing definition for Vendor-Specific (26)");
2785  return -1;
2786  }
2787  parent = child;
2788 
2789  /*
2790  * The code below should resolve the vendor.
2791  */
2792 
2793  if (*p != '-') {
2794  fr_strerror_printf("Invalid text following vendor definition in attribute name '%s'", name);
2795 
2796  return -1;
2797  }
2798  p++;
2799  }
2800 
2801  /*
2802  * Attr-%d
2803  */
2804  if (strncasecmp(p, "Attr-", 5) != 0) {
2805  fr_strerror_printf("Unknown attribute '%s'", name);
2806 
2807  return -1;
2808  }
2809 
2810  num = strtoul(p + 5, &q, 10);
2811  if (!num || (num >= UINT_MAX)) {
2812  fr_strerror_printf("Invalid value in attribute name '%s'", name);
2813 
2814  return -1;
2815  }
2816  attr = num;
2817 
2818  p = q;
2819 
2820  /*
2821  * Vendor-%d-Attr-%d
2822  * VendorName-Attr-%d
2823  * Attr-%d
2824  * Attr-%d.
2825  *
2826  * Anything else is invalid.
2827  */
2828  if (((vendor != 0) && (*p != '\0')) ||
2829  ((vendor == 0) && *p && (*p != '.'))) {
2830  invalid:
2831  fr_strerror_printf("Invalid OID");
2832  return -1;
2833  }
2834 
2835  /*
2836  * Look for OIDs. Require the "Attr-26.Vendor-Id.type"
2837  * format, and disallow "Vendor-%d-Attr-%d" and
2838  * "VendorName-Attr-%d"
2839  *
2840  * This section parses the Vendor-Id portion of
2841  * Attr-%d.%d. where the first number is 26, *or* an
2842  * extended name of the "evs" foundta type.
2843  */
2844  if (*p == '.') {
2845  child = fr_dict_attr_child_by_num(parent, attr);
2846  if (!child) {
2847  fr_strerror_printf("Cannot parse names without dictionaries");
2848  return -1;
2849  }
2850 
2851  switch (child->type) {
2852  case PW_TYPE_STRUCTURAL:
2853  break;
2854 
2855  default:
2856  fr_strerror_printf("Standard attributes cannot use OIDs");
2857  return -1;
2858  }
2859 
2860  if ((child->type == PW_TYPE_VSA) || (child->type == PW_TYPE_EVS)) {
2861  num = strtoul(p + 1, &q, 10);
2862  if (!num || (num >= UINT_MAX)) {
2863  fr_strerror_printf("Invalid vendor");
2864 
2865  return -1;
2866  }
2867  vendor = num;
2868 
2869  if (*q != '.') goto invalid;
2870 
2871  p = q;
2872 
2873  attr = 0; /* Attr must exist beneath the vendor */
2874  } /* else the second number is a TLV number */
2875  parent = child;
2876  }
2877 
2878  /*
2879  * Get the expected maximum size of the name.
2880  */
2881  if (vendor) {
2882  dv = fr_dict_vendor_by_num(dict, vendor);
2883  if (dv) {
2884  /*
2885  * Parent needs to be EVS or VSA
2886  */
2887  if ((parent->type != PW_TYPE_VSA) && (parent->type != PW_TYPE_EVS)) {
2888  fr_strerror_printf("Vendor specified, but current parent is not 'evs' or 'vsa'");
2889  return -1;
2890  }
2891 
2892  child = fr_dict_attr_child_by_num(parent, vendor);
2893  if (!child) {
2894  fr_strerror_printf("Missing vendor attr for %i", vendor);
2895  return -1;
2896  }
2897  parent = child;
2898 
2899  /*
2900  * Build the unknown vendor, assuming it's a normal format.
2901  */
2902  } else if (vendor_da) {
2903  vendor_da->attr = vendor;
2904  vendor_da->type = PW_TYPE_VENDOR;
2905  vendor_da->parent = parent;
2906  vendor_da->depth = parent->depth + 1;
2907  vendor_da->flags.is_unknown = 1;
2908  vendor_da->flags.type_size = 1;
2909  vendor_da->flags.length = 1;
2910  snprintf(vendor_da->name, FR_DICT_ATTR_MAX_NAME_LEN, "Vendor-%u", vendor);
2911 
2912  parent = vendor_da;
2913  } else {
2914  fr_strerror_printf("Unknown vendor disallowed");
2915  return -1;
2916  }
2917  }
2918 
2919  if (*p == '.') if (fr_dict_attr_by_oid(dict, &parent, &vendor, &attr, p + 1) < 0) return -1;
2920 
2921  /*
2922  * If the caller doesn't provide a fr_dict_attr_t
2923  * we can't call fr_dict_unknown_from_fields.
2924  */
2925  if (!da) {
2926  fr_strerror_printf("Unknown attributes disallowed");
2927  return -1;
2928  }
2929 
2930  return fr_dict_unknown_from_fields(da, parent, vendor, attr);
2931 }
2932 
2933 /** Create a fr_dict_attr_t from an ASCII attribute and value
2934  *
2935  * Where the attribute name is in the form:
2936  * - Attr-%d
2937  * - Attr-%d.%d.%d...
2938  * - Vendor-%d-Attr-%d
2939  * - VendorName-Attr-%d
2940  *
2941  * @copybrief fr_dict_unknown_from_fields
2942  *
2943  * @note If vendor != 0, an unknown vendor (may) also be created, parented by
2944  * the correct EVS or VSA attribute. This is accessible via vp->parent,
2945  * and will be use the unknown da as its talloc parent.
2946  *
2947  * @param[in] dict of protocol context we're operating in. If NULL the internal
2948  * dictionary will be used.
2949  * @param[in] ctx to alloc new attribute in.
2950  * @param[in] parent Attribute to use as the root for resolving OIDs in. Usually
2951  * the root of a protocol dictionary.
2952  * @param[in] name of attribute.
2953  * @return
2954  * - 0 on success.
2955  * - -1 on failure.
2956  */
2958  fr_dict_attr_t const *parent, char const *name)
2959 {
2960  uint8_t *p;
2961  uint8_t vendor_buff[FR_DICT_ATTR_SIZE];
2962  fr_dict_attr_t *vendor = (fr_dict_attr_t *)&vendor_buff;
2963  fr_dict_attr_t *da;
2964  fr_dict_attr_t const *new_parent = NULL;
2965 
2966  p = talloc_zero_array(ctx, uint8_t, FR_DICT_ATTR_SIZE);
2967  if (!p) {
2968  fr_strerror_printf("Out of memory");
2969  return NULL;
2970  }
2971  da = (fr_dict_attr_t *)p;
2972  talloc_set_type(da, fr_dict_attr_t);
2973 
2974  if (fr_dict_unknown_from_oid(dict, vendor, da, parent, name) < 0) {
2975  talloc_free(p);
2976  return NULL;
2977  }
2978 
2979  /*
2980  * Unknown attributes are always rooted in known
2981  * attributes, so we don't need to clone anything
2982  * here.
2983  */
2984  if (vendor->flags.is_unknown) {
2985  new_parent = fr_dict_unknown_acopy(p, vendor);
2986  if (!new_parent) {
2987  talloc_free(p);
2988  return NULL;
2989  }
2990  da->parent = new_parent;
2991  /*
2992  * Need to clone the unknown hierachy, as unknown
2993  * attributes must parent the complete heirachy,
2994  * and cannot share any parts with any other unknown
2995  * attributes.
2996  */
2997  } else if (parent->flags.is_unknown) {
2998  new_parent = fr_dict_unknown_acopy(ctx, parent);
2999  da->parent = new_parent;
3000 
3001  /*
3002  * Ensure the parent is freed at the same time as the
3003  * unknown DA. This should be OK as we never parent
3004  * multiple unknown attributes off the same parent.
3005  */
3006  if (new_parent->flags.is_unknown) talloc_steal(da, new_parent);
3007  }
3008 
3009  VERIFY_DA(da);
3010 
3011  return da;
3012 }
3013 
3014 /** Create a dictionary attribute by name embedded in another string
3015  *
3016  * Find the first invalid attribute name char in the string pointed
3017  * to by name.
3018  *
3019  * Copy the characters between the start of the name string and the first
3020  * none dict_attr_allowed_char to a buffer and initialise da as an
3021  * unknown attribute.
3022  *
3023  * @param[in] dict of protocol context we're operating in. If NULL the internal
3024  * dictionary will be used.
3025  * @param[out] vendor_da will be filled in if a vendor is found.
3026  * @param[out] da will be filled in with the da at the end of the OID chain.
3027  * @param[in] parent Attribute to use as the root for resolving OIDs in. Usually
3028  * the root of a protocol dictionary.
3029  * @param[in,out] name string start.
3030  * @return
3031  * - 0 on success.
3032  * - -1 on failure.
3033  */
3035  fr_dict_attr_t const *parent, char const **name)
3036 {
3037  char const *p;
3038  size_t len;
3039  char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
3040 
3041  if (!name || !*name) return -1;
3042  INTERNAL_IF_NULL(dict);
3043 
3044  /*
3045  * Advance p until we get something that's not part of
3046  * the dictionary attribute name.
3047  */
3048  for (p = *name; fr_dict_attr_allowed_chars[(int)*p] || (*p == '.') || (*p == '-'); p++);
3049 
3050  len = p - *name;
3051  if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
3052  fr_strerror_printf("Attribute name too long");
3053  return -1;
3054  }
3055  if (len == 0) {
3056  fr_strerror_printf("Invalid attribute name");
3057  return -1;
3058  }
3059  strlcpy(buffer, *name, len + 1);
3060 
3061  if (fr_dict_unknown_from_oid(dict, vendor_da, da, parent, buffer) < 0) return -1;
3062 
3063  *name = p;
3064 
3065  return 0;
3066 }
3067 
3068 static void fr_dict_snprint_flags(char *out, size_t outlen, fr_dict_attr_flags_t flags)
3069 {
3070  char *p = out, *end = p + outlen;
3071  size_t len;
3072 
3073  out[0] = '\0';
3074 
3075 #define FLAG_SET(_flag) \
3076 do { \
3077  if (flags._flag) {\
3078  p += strlcpy(p, STRINGIFY(_flag)",", end - p);\
3079  if (p >= end) return;\
3080  }\
3081 } while (0)
3082 
3083  FLAG_SET(is_root);
3084  FLAG_SET(is_unknown);
3085  FLAG_SET(internal);
3086  FLAG_SET(has_tag);
3087  FLAG_SET(array);
3088  FLAG_SET(has_value);
3089  FLAG_SET(concat);
3090  FLAG_SET(is_pointer);
3091  FLAG_SET(virtual);
3092  FLAG_SET(compare);
3093 
3094  if (flags.encrypt) {
3095  p += snprintf(p, end - p, "encrypt=%i,", flags.encrypt);
3096  if (p >= end) return;
3097  }
3098 
3099  if (flags.length) {
3100  p += snprintf(p, end - p, "length=%i,", flags.length);
3101  if (p >= end) return;
3102  }
3103 
3104  if (!out[0]) return;
3105 
3106  /*
3107  * Trim the comma
3108  */
3109  len = strlen(out);
3110  if (out[len - 1] == ',') out[len - 1] = '\0';
3111 }
3112 
3113 void fr_dict_print(fr_dict_attr_t const *da, int depth)
3114 {
3115  char buff[256];
3116  unsigned int i;
3117  char const *name;
3118 
3119  fr_dict_snprint_flags(buff, sizeof(buff), da->flags);
3120 
3121  switch (da->type) {
3122  case PW_TYPE_VSA:
3123  name = "VSA";
3124  break;
3125 
3126  case PW_TYPE_EXTENDED:
3127  name = "EXTENDED";
3128  break;
3129 
3130  case PW_TYPE_TLV:
3131  name = "TLV";
3132  break;
3133 
3134  case PW_TYPE_EVS:
3135  name = "EVS";
3136  break;
3137 
3138  case PW_TYPE_VENDOR:
3139  name = "VENDOR";
3140  break;
3141 
3142  case PW_TYPE_LONG_EXTENDED:
3143  name = "LONG EXTENDED";
3144  break;
3145 
3146  default:
3147  name = "ATTRIBUTE";
3148  break;
3149  }
3150 
3151  printf("%u%.*s%s \"%s\" vendor: %x (%u), num: %x (%u), type: %s, flags: %s\n", da->depth, depth,
3152  "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t", name, da->name,
3153  da->vendor, da->vendor, da->attr, da->attr,
3154  fr_int2str(dict_attr_types, da->type, "?Unknown?"), buff);
3155 
3156  if (da->children) for (i = 0; i < talloc_array_length(da->children); i++) {
3157  if (da->children[i]) {
3158  fr_dict_attr_t const *bin;
3159 
3160  for (bin = da->children[i]; bin; bin = bin->next) fr_dict_print(bin, depth + 1);
3161  }
3162  }
3163 }
3164 
3165 /** Find a common ancestor that two TLV type attributes share
3166  *
3167  * @param a first TLV attribute.
3168  * @param b second TLV attribute.
3169  * @param is_ancestor Enforce a->b relationship (a is parent or ancestor of b).
3170  * @return
3171  * - Common ancestor if one exists.
3172  * - NULL if no common ancestor exists.
3173  */
3174 fr_dict_attr_t const *fr_dict_parent_common(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
3175 {
3176  unsigned int i;
3177  fr_dict_attr_t const *p_a, *p_b;
3178 
3179  if (!a || !b) return NULL;
3180 
3181  if (!a->parent || !b->parent) return NULL; /* Either are at the root */
3182 
3183  if (is_ancestor && (b->depth <= a->depth)) return NULL;
3184 
3185  /*
3186  * Find a common depth to work back from
3187  */
3188  if (a->depth > b->depth) {
3189  p_b = b;
3190  for (p_a = a, i = a->depth - b->depth; p_a && (i > 0); p_a = p_a->parent, i--);
3191  } else if (a->depth < b->depth) {
3192  p_a = a;
3193  for (p_b = b, i = b->depth - a->depth; p_b && (i > 0); p_b = p_b->parent, i--);
3194  } else {
3195  p_a = a;
3196  p_b = b;
3197  }
3198 
3199  while (p_a && p_b) {
3200  if (p_a == p_b) return p_a;
3201 
3202  p_a = p_a->parent;
3203  p_b = p_b->parent;
3204  }
3205 
3206  return NULL;
3207 }
3208 
3209 /** Process a single OID component
3210  *
3211  * @param[out] out Value of component.
3212  * @param[in] oid string to parse.
3213  * @return
3214  * - 0 on success.
3215  * - -1 on format error.
3216  */
3217 int fr_dict_oid_component(unsigned int *out, char const **oid)
3218 {
3219  char const *p = *oid;
3220  char *q;
3221  unsigned long num;
3222 
3223  *out = 0;
3224 
3225  num = strtoul(p, &q, 10);
3226  if ((p == q) || (num == ULONG_MAX)) {
3227  fr_strerror_printf("Invalid OID component \"%s\" (%lu)", p, num);
3228  return -1;
3229  }
3230 
3231  switch (*q) {
3232  case '\0':
3233  case '.':
3234  *oid = q;
3235  *out = (unsigned int)num;
3236 
3237  return 0;
3238 
3239  default:
3240  fr_strerror_printf("Unexpected text after OID component");
3241  *out = 0;
3242  return -1;
3243  }
3244 }
3245 
3246 /** Get the leaf attribute of an OID string
3247  *
3248  * @note On error, vendor will be set (if present), parent will be the
3249  * maximum depth we managed to resolve to, and attr will be the child
3250  * we failed to resolve.
3251  *
3252  * @param[in] dict of protocol context we're operating in. If NULL the internal
3253  * dictionary will be used.
3254  * @param[out] attr Number we parsed.
3255  * @param[in,out] vendor number of attribute.
3256  * @param[in,out] parent attribute (or root of dictionary). Will be updated to the parent
3257  * directly beneath the leaf.
3258  * @param[in] oid string to parse.
3259  * @return
3260  * - > 0 on success (number of bytes parsed).
3261  * - <= 0 on parse error (negative offset of parse error).
3262  */
3263 ssize_t fr_dict_attr_by_oid(fr_dict_t *dict, fr_dict_attr_t const **parent,
3264  unsigned int *vendor, unsigned int *attr, char const *oid)
3265 {
3266  char const *p = oid;
3267  unsigned int num = 0;
3268  ssize_t slen;
3269 
3270  if (!fr_assert(parent)) return 0;
3271  INTERNAL_IF_NULL(dict);
3272 
3273  *attr = 0;
3274 
3275  if (fr_dict_oid_component(&num, &p) < 0) return oid - p;
3276 
3277  /*
3278  * Record progress even if we error out.
3279  *
3280  * Don't change this, you will break things.
3281  */
3282  *attr = num;
3283 
3284  /*
3285  * Look for 26.VID.x.y
3286  *
3287  * This allows us to specify a VSA if our parent is the root
3288  * of the dictionary, and we're operating outside of a vendor
3289  * block.
3290  *
3291  * The additional code is because we need at least three components
3292  * the VSA attribute (26), the vendor ID, and actual attribute.
3293  */
3294  if (((*parent)->flags.is_root) && !*vendor && (num == PW_VENDOR_SPECIFIC)) {
3295  fr_dict_vendor_t const *dv;
3296 
3297  if (p[0] == '\0') {
3298  fr_strerror_printf("Vendor attribute must specify a VID");
3299  return oid - p;
3300  }
3301  p++;
3302 
3303  if (fr_dict_oid_component(&num, &p) < 0) return oid - p;
3304  if (p[0] == '\0') {
3305  fr_strerror_printf("Vendor attribute must specify a child");
3306  return oid - p;
3307  }
3308  p++;
3309 
3310  dv = fr_dict_vendor_by_num(dict, num);
3311  if (!dv) {
3312  fr_strerror_printf("Unknown vendor '%u' ", num);
3313  return oid - p;
3314  }
3315  *vendor = dv->vendorpec; /* Record vendor number */
3316 
3317  /*
3318  * Recurse to get the attribute.
3319  */
3320  slen = fr_dict_attr_by_oid(dict, parent, vendor, attr, p);
3321  if (slen <= 0) return slen - (p - oid);
3322 
3323  slen += p - oid;
3324  return slen;
3325  }
3326 
3327  /*
3328  * If it's not a vendor type, it must be between 0..8*type_size
3329  *
3330  * @fixme: find the TLV parent, and check it's size
3331  */
3332  if (((*parent)->type != PW_TYPE_VENDOR) && (num > UINT8_MAX)) {
3333  fr_strerror_printf("TLV attributes must be between 0-255 inclusive");
3334  return 0;
3335  }
3336 
3337  switch (p[0]) {
3338  /*
3339  * We've not hit the leaf yet, so the attribute must be
3340  * defined already.
3341  */
3342  case '.':
3343  {
3344  fr_dict_attr_t const *child;
3345  p++;
3346 
3347  child = fr_dict_attr_child_by_num(*parent, num);
3348  if (!child) {
3349  fr_strerror_printf("Unknown child attribute starting at \"%s\"", oid);
3350  return 0; /* We parsed nothing */
3351  }
3352  /*
3353  * Record progress even if we error out.
3354  *
3355  * Don't change this, you will break things.
3356  */
3357  *parent = child;
3358 
3359  slen = fr_dict_attr_by_oid(dict, parent, vendor, attr, p);
3360  if (slen <= 0) return slen - (p - oid);
3361  return slen + (p - oid);
3362  }
3363 
3364  /*
3365  * Hit the leaf, this is the attribute we need to define.
3366  */
3367  case '\0':
3368  *attr = num;
3369  return p - oid;
3370 
3371  default:
3372  fr_strerror_printf("Malformed OID string, got trailing garbage '%s'", p);
3373  return oid - p;
3374  }
3375 }
3376 
3377 /** Look up a vendor by its name
3378  *
3379  * @param[in] dict of protocol context we're operating in. If NULL the internal
3380  * dictionary will be used.
3381  * @param[in] name to search for.
3382  * @return
3383  * - The vendor.
3384  * - NULL if no vendor with that name was regitered for this protocol.
3385  */
3386 int fr_dict_vendor_by_name(fr_dict_t *dict, char const *name)
3387 {
3388  fr_dict_vendor_t *dv;
3389  size_t buffer[(sizeof(*dv) + FR_DICT_VENDOR_MAX_NAME_LEN + sizeof(size_t) - 1) / sizeof(size_t)];
3390 
3391  if (!name) return 0;
3392  INTERNAL_IF_NULL(dict);
3393 
3394  dv = (fr_dict_vendor_t *)buffer;
3395  strlcpy(dv->name, name, FR_DICT_VENDOR_MAX_NAME_LEN + 1);
3396 
3397  dv = fr_hash_table_finddata(dict->vendors_by_name, dv);
3398  if (!dv) return 0;
3399 
3400  return dv->vendorpec;
3401 }
3402 
3403 /** Look up a vendor by its PEN
3404  *
3405  * @param[in] dict of protocol context we're operating in. If NULL the internal
3406  * dictionary will be used.
3407  * @param[in] vendorpec to search for.
3408  * @return
3409  * - The vendor.
3410  * - NULL if no vendor with that number was regitered for this protocol.
3411  */
3413 {
3414  fr_dict_vendor_t dv;
3415 
3416  INTERNAL_IF_NULL(dict);
3417 
3418  dv.vendorpec = vendorpec;
3419 
3420  return fr_hash_table_finddata(dict->vendors_by_num, &dv);
3421 }
3422 
3423 /** Look up a dictionary attribute by a name embedded in another string
3424  *
3425  * Find the first invalid attribute name char in the string pointed
3426  * to by name.
3427  *
3428  * Copy the characters between the start of the name string and the first
3429  * none dict_attr_allowed_char to a buffer and perform a dictionary lookup
3430  * using that value.
3431  *
3432  * If the attribute exists, advance the pointer pointed to by name
3433  * to the first none dict_attr_allowed_char char, and return the DA.
3434  *
3435  * If the attribute does not exist, don't advance the pointer and return
3436  * NULL.
3437  *
3438  * @param[in] dict of protocol context we're operating in. If NULL the internal
3439  * dictionary will be used.
3440  * @param[in,out] name string start.
3441  * @return
3442  * - Attribute matching name.
3443  * - NULL if no matching attribute could be found.
3444  */
3446 {
3447  fr_dict_attr_t *find;
3448  fr_dict_attr_t const *da;
3449  char const *p;
3450  size_t len;
3451  uint32_t buffer[(sizeof(*find) + FR_DICT_ATTR_MAX_NAME_LEN + 3) / 4];
3452 
3453  if (!name || !*name) return NULL;
3454  INTERNAL_IF_NULL(dict);
3455 
3456  find = (fr_dict_attr_t *)buffer;
3457 
3458  /*
3459  * Advance p until we get something that's not part of
3460  * the dictionary attribute name.
3461  */
3462  for (p = *name; fr_dict_attr_allowed_chars[(int)*p]; p++);
3463 
3464  len = p - *name;
3465  if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
3466  fr_strerror_printf("Attribute name too long");
3467 
3468  return NULL;
3469  }
3470  strlcpy(find->name, *name, len + 1);
3471 
3472  da = fr_hash_table_finddata(dict->attributes_by_name, find);
3473  if (!da) {
3474  fr_strerror_printf("Unknown attribute '%s'", find->name);
3475  return NULL;
3476  }
3477  *name = p;
3478 
3479  return da;
3480 }
3481 
3482 /** Locate a #fr_dict_attr_t by its name
3483  *
3484  * @note Unlike attribute numbers, attribute names are unique to the dictionary.
3485  *
3486  * @param[in] dict of protocol context we're operating in. If NULL the internal
3487  * dictionary will be used.
3488  * @param[in] name of the attribute to locate.
3489  * @return
3490  * - Attribute matching name.
3491  * - NULL if no matching attribute could be found.
3492  */
3494 {
3495  fr_dict_attr_t *da;
3496  uint32_t buffer[(sizeof(*da) + FR_DICT_ATTR_MAX_NAME_LEN + 3) / 4];
3497 
3498  if (!name) return NULL;
3499  INTERNAL_IF_NULL(dict);
3500 
3501  da = (fr_dict_attr_t *)buffer;
3502  strlcpy(da->name, name, FR_DICT_ATTR_MAX_NAME_LEN + 1);
3503 
3504  return fr_hash_table_finddata(dict->attributes_by_name, da);
3505 }
3506 
3507 /** Lookup a #fr_dict_attr_t by its vendor and attribute numbers
3508  *
3509  * @note This is a deprecated function, new code should use #fr_dict_attr_child_by_num.
3510  *
3511  * @param[in] dict of protocol context we're operating in. If NULL the internal
3512  * dictionary will be used.
3513  * @param[in] vendor number of the attribute.
3514  * @param[in] attr number of the attribute.
3515  * @return
3516  * - Attribute matching vendor/attr.
3517  * - NULL if no matching attribute could be found.
3518  */
3519 fr_dict_attr_t const *fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
3520 {
3521  fr_dict_attr_t const *parent;
3522 
3523  INTERNAL_IF_NULL(dict);
3524 
3525  if (vendor == 0) return fr_dict_attr_child_by_num(dict->root, attr);
3526 
3527  parent = fr_dict_attr_child_by_num(dict->root, PW_VENDOR_SPECIFIC);
3528  if (!parent) return NULL;
3529 
3530  parent = fr_dict_attr_child_by_num(parent, vendor);
3531  if (!parent) return NULL;
3532 
3533  return fr_dict_attr_child_by_num(parent, attr);
3534 }
3535 
3536 /** Lookup a attribute by its its vendor and attribute numbers and data type
3537  *
3538  * @note Only works with PW_TYPE_COMBO_IP
3539  *
3540  * @param[in] dict of protocol context we're operating in. If NULL the internal
3541  * dictionary will be used.
3542  * @param[in] vendor number of the attribute.
3543  * @param[in] attr number of the attribute.
3544  * @param[in] type Variant of attribute to lookup.
3545  * @return
3546  * - Attribute matching vendor/attr/type.
3547  * - NULL if no matching attribute could be found.
3548  */
3549 fr_dict_attr_t const *fr_dict_attr_by_type(fr_dict_t *dict, unsigned int vendor, unsigned int attr, PW_TYPE type)
3550 {
3551  fr_dict_attr_t da;
3552 
3553  INTERNAL_IF_NULL(dict);
3554 
3555  da.attr = attr;
3556  da.vendor = vendor;
3557  da.type = type;
3558 
3559  return fr_hash_table_finddata(dict->attributes_combo, &da);
3560 }
3561 
3562 /** Check if a child attribute exists in a parent using a pointer (da)
3563  *
3564  * @param parent to check for child in.
3565  * @param child to look for.
3566  * @return
3567  * - The child attribute on success.
3568  * - NULL if the child attribute does not exist.
3569  */
3570 inline fr_dict_attr_t const *fr_dict_attr_child_by_da(fr_dict_attr_t const *parent, fr_dict_attr_t const *child)
3571 {
3572  fr_dict_attr_t const *bin;
3573 
3574  if (!parent->children) return NULL;
3575 
3576  /*
3577  * Only some types can have children
3578  */
3579  switch (parent->type) {
3580  default:
3581  return NULL;
3582 
3583  case PW_TYPE_STRUCTURAL:
3584  break;
3585  }
3586 
3587  /*
3588  * Child arrays may be trimmed back to save memory.
3589  * Check that so we don't SEGV.
3590  */
3591  if ((child->attr & 0xff) > talloc_array_length(parent->children)) return NULL;
3592 
3593  bin = parent->children[child->attr & 0xff];
3594  for (;;) {
3595  if (!bin) return NULL;
3596  if (bin == child) return bin;
3597  bin = bin->next;
3598  }
3599 
3600  return NULL;
3601 }
3602 
3603 /** Check if a child attribute exists in a parent using an attribute number
3604  *
3605  * @param parent to check for child in.
3606  * @param attr number to look for.
3607  * @return
3608  * - The child attribute on success.
3609  * - NULL if the child attribute does not exist.
3610  */
3611 inline fr_dict_attr_t const *fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
3612 {
3613  fr_dict_attr_t const *bin;
3614 
3615  if (!parent->children) return NULL;
3616 
3617  /*
3618  * Only some types can have children
3619  */
3620  switch (parent->type) {
3621  default:
3622  return NULL;
3623 
3624  case PW_TYPE_STRUCTURAL:
3625  break;
3626  }
3627 
3628  /*
3629  * Child arrays may be trimmed back to save memory.
3630  * Check that so we don't SEGV.
3631  */
3632  if ((attr & 0xff) > talloc_array_length(parent->children)) return NULL;
3633 
3634  bin = parent->children[attr & 0xff];
3635  for (;;) {
3636  if (!bin) return NULL;
3637  if (bin->attr == attr) return bin;
3638  bin = bin->next;
3639  }
3640 
3641  return NULL;
3642 }
3643 
3644 /** Lookup the structure representing an enum value in a #fr_dict_attr_t
3645  *
3646  * @param[in] dict of protocol context we're operating in. If NULL the internal
3647  * dictionary will be used.
3648  * @param[in] da to search in.
3649  * @param[in] value number to search for.
3650  * @return
3651  * - Matching #fr_dict_enum_t.
3652  * - NULL if no matching #fr_dict_enum_t could be found.
3653  */
3655 {
3656  fr_dict_enum_t dval, *dv;
3657 
3658  INTERNAL_IF_NULL(dict);
3659 
3660  /*
3661  * First, look up aliases.
3662  */
3663  dval.da = da;
3664  dval.name[0] = '\0';
3665 
3666  /*
3667  * Look up the attribute alias target, and use
3668  * the correct attribute number if found.
3669  */
3670  dv = fr_hash_table_finddata(dict->values_by_name, &dval);
3671  if (dv) dval.da = dv->da;
3672 
3673  dval.value = value;
3674 
3675  return fr_hash_table_finddata(dict->values_by_da, &dval);
3676 }
3677 
3678 /** Lookup the name of an enum value in a #fr_dict_attr_t
3679  *
3680  * @param[in] dict of protocol context we're operating in. If NULL the internal
3681  * dictionary will be used.
3682  * @param[in] da to search in.
3683  * @param[in] value number to search for.
3684  * @return
3685  * - Name of value.
3686  * - NULL if no matching value could be found.
3687  */
3688 char const *fr_dict_enum_name_by_da(fr_dict_t *dict, fr_dict_attr_t const *da, int value)
3689 {
3690  fr_dict_enum_t *dv;
3691 
3692  INTERNAL_IF_NULL(dict);
3693 
3694  dv = fr_dict_enum_by_da(dict, da, value);
3695  if (!dv) return "";
3696 
3697  return dv->name;
3698 }
3699 
3700 /*
3701  * Get a value by its name, keyed off of an attribute.
3702  */
3704 {
3705  fr_dict_enum_t *my_dv, *dv;
3706  uint32_t buffer[(sizeof(*my_dv) + FR_DICT_ENUM_MAX_NAME_LEN + 3) / 4];
3707 
3708  if (!name) return NULL;
3709  INTERNAL_IF_NULL(dict);
3710 
3711  my_dv = (fr_dict_enum_t *)buffer;
3712  my_dv->da = da;
3713  my_dv->name[0] = '\0';
3714 
3715  /*
3716  * Look up the attribute alias target, and use
3717  * the correct attribute number if found.
3718  */
3719  dv = fr_hash_table_finddata(dict->values_by_name, my_dv);
3720  if (dv) my_dv->da = dv->da;
3721 
3722  strlcpy(my_dv->name, name, FR_DICT_ENUM_MAX_NAME_LEN + 1);
3723 
3724  return fr_hash_table_finddata(dict->values_by_name, my_dv);
3725 }
3726 
3727 /*
3728  * [a-zA-Z0-9_-:.]+
3729  */
3730 int fr_dict_valid_name(char const *name)
3731 {
3732  uint8_t const *p;
3733 
3734  for (p = (uint8_t const *)name; *p != '\0'; p++) {
3735  if (!fr_dict_attr_allowed_chars[*p]) {
3736  char buff[5];
3737 
3738  fr_snprint(buff, sizeof(buff), (char const *)p, 1, '\'');
3739  fr_strerror_printf("Invalid character '%s' in attribute name", buff);
3740 
3741  return -(p - (uint8_t const *)name);
3742  }
3743  }
3744 
3745  return 0;
3746 }
3747 
3748 void fr_dict_verify(char const *file, int line, fr_dict_attr_t const *da)
3749 {
3750  int i;
3751  fr_dict_attr_t const *da_p;
3752 
3753  if (!da) {
3754  FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t pointer was NULL", file, line);
3755 
3756  fr_assert(0);
3757  fr_exit_now(1);
3758  }
3759 
3760  (void) talloc_get_type_abort(da, fr_dict_attr_t);
3761 
3762  if ((!da->flags.is_root) && (da->depth == 0)) {
3763  FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
3764  "Is not root, but depth is 0",
3765  file, line, da->name, da->vendor, da->attr);
3766 
3767  fr_assert(0);
3768  fr_exit_now(1);
3769  }
3770 
3771  if (da->depth > FR_DICT_MAX_TLV_STACK) {
3772  FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
3773  "Indicated depth (%u) greater than TLV stack depth (%u)",
3774  file, line, da->name, da->vendor, da->attr, da->depth, FR_DICT_MAX_TLV_STACK);
3775 
3776  fr_assert(0);
3777  fr_exit_now(1);
3778  }
3779 
3780  for (da_p = da; da_p; da_p = da_p->next) (void) talloc_get_type_abort(da_p, fr_dict_attr_t);
3781 
3782  for (i = da->depth, da_p = da; (i >= 0) && da; i--, da_p = da_p->parent) {
3783  if (i != (int)da_p->depth) {
3784  FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
3785  "Depth out of sequence, expected %i, got %u",
3786  file, line, da->name, da->vendor, da->attr, i, da_p->depth);
3787 
3788  fr_assert(0);
3789  fr_exit_now(1);
3790  }
3791 
3792  }
3793 
3794  if ((i + 1) < 0) {
3795  FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t top of hierarchy was not at depth 0",
3796  file, line);
3797 
3798  fr_assert(0);
3799  fr_exit_now(1);
3800  }
3801 }
dict_stat_t * stat_head
Definition: dict.c:64
fr_dict_attr_t const * fr_dict_attr_child_by_da(fr_dict_attr_t const *parent, fr_dict_attr_t const *child)
Check if a child attribute exists in a parent using a pointer (da)
Definition: dict.c:3570
const size_t dict_attr_sizes[PW_TYPE_MAX][2]
Map data types to min / max data sizes.
Definition: dict.c:119
128 Bit IPv6 Address.
Definition: radius.h:40
void fr_dict_print(fr_dict_attr_t const *da, int depth)
Definition: dict.c:3113
int fr_hash_table_insert(fr_hash_table_t *ht, void const *data)
Definition: hash.c:405
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_t *dict, char const *name)
Locate a fr_dict_attr_t by its name.
Definition: dict.c:3493
Time value (struct timeval), only for config items.
Definition: radius.h:55
void * fr_hash_table_finddata(fr_hash_table_t *ht, void const *data)
Definition: hash.c:496
unsigned int array
Pack multiples into 1 attr.
Definition: dict.h:47
fr_dict_attr_t const * fr_dict_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *old)
Converts an unknown to a known by adding it to the internal dictionaries.
Definition: dict.c:2384
void fr_dict_verify(char const *file, int line, fr_dict_attr_t const *da)
Definition: dict.c:3748
#define PW_TYPE_STRUCTURAL
Match all non value types in case statements.
Definition: radius.h:82
fr_dict_attr_t const ** children
Children of this attribute.
Definition: dict.h:83
Dictionary attribute.
Definition: dict.h:77
uint32_t fr_hash(void const *, size_t)
Definition: hash.c:727
static int dict_read_process_attribute(fr_dict_t *dict, fr_dict_attr_t const *parent, unsigned int block_vendor, char **argv, int argc)
Definition: dict.c:1375
Ascend binary format a packed data structure.
Definition: radius.h:37
32 Bit signed integer.
Definition: radius.h:45
static int dict_read_sscanf_i(char const *str, unsigned int *pvalue)
Definition: dict.c:1341
void fr_rand_seed(void const *, size_t)
Seed the random number generator.
Definition: radius.c:1569
static int dict_read_parse_format(char const *format, unsigned int *pvalue, int *ptype, int *plength, bool *pcontinuation)
Definition: dict.c:1597
WiMAX IPv4 or IPv6 address depending on length.
Definition: radius.h:46
static char const * name
uint8_t length
length of the attribute
Definition: dict.h:64
fr_dict_attr_t const * da
Dictionary attribute enum is associated with.
Definition: dict.h:95
fr_hash_table_t * vendors_by_name
Lookup vendor by name.
Definition: dict.c:67
fr_dict_attr_t * root
Root attribute of this dictionary.
Definition: dict.c:77
size_t length
Length of length data.
Definition: dict.h:105
struct value_fixup_t value_fixup_t
static int dict_attr_name_cmp(void const *one, void const *two)
Definition: dict.c:219
static uint32_t dict_attr_combo_hash(void const *data)
Definition: dict.c:227
static int dict_stat_check(fr_dict_t *dict, char const *dir, char const *file)
See if any dictionaries have changed.
Definition: dict.c:341
char name[1]
Vendor name.
Definition: dict.h:107
#define UNUSED
Definition: libradius.h:134
int fr_hash_table_replace(fr_hash_table_t *ht, void const *data)
Definition: hash.c:473
#define FR_DICT_MAX_TLV_STACK
Maximum TLV stack size.
Definition: dict.h:123
static fr_dict_attr_t * fr_dict_attr_alloc(TALLOC_CTX *ctx, char const *name, unsigned int vendor, int attr, PW_TYPE type, fr_dict_attr_flags_t flags)
Definition: dict.c:543
IPv6 Prefix.
Definition: radius.h:41
unsigned int is_pointer
data is a pointer
Definition: dict.h:51
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
Number of defined data types.
Definition: radius.h:59
Values of the encryption flags.
Definition: dict.h:40
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict.c:2339
uint8_t length
Definition: proto_bfd.c:203
Long extended attribute space attribute.
Definition: radius.h:49
fr_dict_enum_t * fr_dict_enum_by_da(fr_dict_t *dict, fr_dict_attr_t const *da, int value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition: dict.c:3654
fr_dict_attr_t const * fr_dict_attr_by_name_substr(fr_dict_t *dict, char const **name)
Look up a dictionary attribute by a name embedded in another string.
Definition: dict.c:3445
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, int attr, PW_TYPE type, fr_dict_attr_flags_t flags)
Add an attribute to the dictionary.
Definition: dict.c:582
unsigned int depth
Depth of nesting for this attribute.
Definition: dict.h:86
int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, unsigned int vendor)
Build an unknown vendor, parented by a VSA or EVS attribute.
Definition: dict.c:2459
fr_hash_table_t * values_by_da
Lookup an attribute enum value by integer value.
Definition: dict.c:74
fr_dict_attr_flags_t flags
Flags.
Definition: dict.h:88
int fr_dict_vendor_by_name(fr_dict_t *dict, char const *name)
Look up a vendor by its name.
Definition: dict.c:3386
size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
Escape any non printable or non-UTF8 characters in the input string.
Definition: print.c:179
static unsigned int hash(char const *username, unsigned int tablesize)
Definition: rlm_passwd.c:124
WiMAX IPv4 or IPv6 address prefix depending on length.
Definition: radius.h:57
fr_dict_attr_t * fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int vendor, unsigned int attr)
Allocates an unknown attribute.
Definition: dict.c:2613
uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
Definition: hash.c:761
Vendors and attribute names.
Definition: dict.c:61
ssize_t fr_dict_attr_by_oid(fr_dict_t *dict, fr_dict_attr_t const **parent, unsigned int *vendor, unsigned int *attr, char const *oid)
Get the leaf attribute of an OID string.
Definition: dict.c:3263
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
char const * fr_dict_enum_name_by_da(fr_dict_t *dict, fr_dict_attr_t const *da, int value)
Lookup the name of an enum value in a fr_dict_attr_t.
Definition: dict.c:3688
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
static uint32_t dict_hash_name(char const *name)
Definition: dict.c:195
8 Bit unsigned integer.
Definition: radius.h:42
unsigned int is_unknown
Attribute number or vendor is unknown.
Definition: dict.h:42
#define FLAG_SET(_flag)
int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
Definition: dict.c:1304
static uint32_t dict_attr_name_hash(void const *data)
Definition: dict.c:214
Interface ID.
Definition: radius.h:39
value_fixup_t * value_fixup
Definition: dict.c:62
int fr_dict_init(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *fn, char const *name)
(re)initialize a protocol dictionary
Definition: dict.c:2148
fr_hash_table_t * attributes_combo
Lookup variants of polymorphic attributes.
Definition: dict.c:72
Double precision floating point.
Definition: radius.h:58
int fr_dict_enum_add(fr_dict_t *dict, char const *attr, char const *alias, int value)
Definition: dict.c:1153
#define FR_FAULT_LOG(fmt,...)
Definition: libradius.h:462
int fr_dict_unknown_from_suboid(fr_dict_t *dict, fr_dict_attr_t *vendor_da, fr_dict_attr_t *da, fr_dict_attr_t const *parent, char const **name)
Create a dictionary attribute by name embedded in another string.
Definition: dict.c:3034
unsigned int attr
Attribute number.
Definition: dict.h:79
static uint32_t dict_enum_name_hash(void const *data)
Definition: dict.c:278
char attrstr[FR_DICT_ATTR_MAX_NAME_LEN]
Definition: dict.c:48
#define fr_assert(_x)
Definition: libradius.h:505
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition: dict.c:3611
const FR_NAME_NUMBER dict_attr_types[]
Map data types to names representing those types.
Definition: dict.c:85
fr_dict_attr_t const * fr_dict_parent_common(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition: dict.c:3174
static fr_dict_attr_t * fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Copy a known or unknown attribute to produce an unknown attribute.
Definition: dict.c:2348
static int dict_attr_combo_cmp(void const *one, void const *two)
Definition: dict.c:237
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
fr_dict_enum_t * dval
Definition: dict.c:49
48 Bit Mac-Address.
Definition: radius.h:44
void fr_dict_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
Definition: dict.c:2426
fr_dict_attr_t const * parent
Immediate parent of this attribute.
Definition: dict.h:82
#define FR_DICT_ATTR_SIZE
Maximum dictionary attribute size.
Definition: dict.h:127
fr_dict_attr_t const * fr_dict_unknown_afrom_oid(TALLOC_CTX *ctx, fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name)
Create a fr_dict_attr_t from an ASCII attribute and value.
Definition: dict.c:2957
int fr_dict_unknown_from_fields(fr_dict_attr_t *da, fr_dict_attr_t const *parent, unsigned int vendor, unsigned int attr)
Initialises an unknown attribute.
Definition: dict.c:2571
enum attr_flags::@0 encrypt
Attribute that represents a vendor in the attribute tree.
Definition: radius.h:54
static uint32_t dict_vendor_name_hash(void const *data)
Definition: dict.c:251
unsigned int is_root
Is root of a dictionary.
Definition: dict.h:41
unsigned int concat
concatenate multiple instances
Definition: dict.h:50
struct dict_stat_t * next
Definition: dict.c:43
int fr_dict_oid_component(unsigned int *out, char const **oid)
Process a single OID component.
Definition: dict.c:3217
#define FNV_MAGIC_INIT
Definition: dict.c:170
Invalid (uninitialised) attribute type.
Definition: radius.h:32
A truth value.
Definition: radius.h:56
static int dict_enum_name_cmp(void const *one, void const *two)
Definition: dict.c:287
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
Definition: dict.h:44
32 Bit unsigned integer.
Definition: radius.h:34
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
int strcasecmp(char *s1, char *s2)
Definition: missing.c:73
int fr_dict_unknown_from_oid(fr_dict_t *dict, fr_dict_attr_t *vendor_da, fr_dict_attr_t *da, fr_dict_attr_t const *parent, char const *name)
Initialise a fr_dict_attr_t from an ASCII attribute and value.
Definition: dict.c:2705
int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
Definition: dict.c:2291
fr_hash_table_t * values_by_name
Lookup an attribute enum value by name.
Definition: dict.c:75
Private enterprise.
Definition: dict.h:102
char name[1]
Enum name.
Definition: dict.h:97
64 Bit unsigned integer.
Definition: radius.h:51
fr_dict_attr_t const * next
Next child in bin.
Definition: dict.h:84
fr_dict_enum_t * fr_dict_enum_by_name(fr_dict_t *dict, fr_dict_attr_t const *da, char const *name)
Definition: dict.c:3703
Vendor-Specific, for RADIUS attribute 26.
Definition: radius.h:53
#define FR_DICT_ENUM_MAX_NAME_LEN
Definition: dict.h:113
char name[1]
Attribute name.
Definition: dict.h:89
uint8_t data[]
Definition: eap_pwd.h:625
static void fr_dict_snprint_flags(char *out, size_t outlen, fr_dict_attr_flags_t flags)
Definition: dict.c:3068
static int dict_enum_value_cmp(void const *one, void const *two)
Definition: dict.c:308
32 Bit Unix timestamp.
Definition: radius.h:36
static uint32_t dict_enum_value_hash(void const *data)
Definition: dict.c:299
Extended attribute space attribute.
Definition: radius.h:48
struct value_fixup_t * next
Definition: dict.c:50
static int dict_read_process_value(fr_dict_t *dict, char **argv, int argc)
Definition: dict.c:1575
fr_hash_table_t * vendors_by_num
Lookup vendor by PEN.
Definition: dict.c:68
int strncasecmp(char *s1, char *s2, int n)
Definition: missing.c:43
#define FNV_MAGIC_PRIME
Definition: dict.c:171
static void hash_pool_free(void *to_free)
Definition: dict.c:190
static int dict_vendor_name_cmp(void const *one, void const *two)
Definition: dict.c:256
fr_hash_table_t * fr_hash_table_create(TALLOC_CTX *ctx, fr_hash_table_hash_t hashNode, fr_hash_table_cmp_t cmpNode, fr_hash_table_free_t freeNode)
Definition: hash.c:279
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
#define fr_exit_now(_x)
Definition: libradius.h:511
unsigned int vendorpec
Private enterprise number.
Definition: dict.h:103
fr_dict_attr_t const * fr_dict_attr_by_type(fr_dict_t *dict, unsigned int vendor, unsigned int attr, PW_TYPE type)
Lookup a attribute by its its vendor and attribute numbers and data type.
Definition: dict.c:3549
#define VERIFY_DA(_x)
Definition: dict.h:35
int value
Enum value.
Definition: dict.h:96
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
static void concat(char *out, char *in1, char *in2, int l1, int l2)
Definition: smbdes.c:172
unsigned int has_tag
Tagged attribute.
Definition: dict.h:46
int fr_hash_table_walk(fr_hash_table_t *ht, fr_hash_table_walk_t callback, void *ctx)
Definition: hash.c:609
fr_dict_t * fr_dict_internal
Internal server dictionary.
Definition: dict.c:81
#define FR_DICT_TLV_NEST_MAX
Maximum level of TLV nesting allowed.
Definition: dict.h:119
int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent, unsigned int vendor)
Definition: dict.c:2306
#define DHCP_MAGIC_VENDOR
Definition: dhcp.h:97
dict_stat_t * stat_tail
Definition: dict.c:65
size_t type
Length of type data.
Definition: dict.h:104
static int hash_null_callback(UNUSED void *ctx, UNUSED void *data)
Definition: dict.c:185
#define FR_DICT_VENDOR_MAX_NAME_LEN
Definition: dict.h:114
IPv4 Prefix.
Definition: radius.h:52
const int fr_dict_attr_allowed_chars[256]
Definition: dict.c:145
struct dict_stat_t dict_stat_t
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
struct stat stat_buf
Definition: dict.c:44
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
uint8_t type_size
for TLV2, size of the type
Definition: dict.h:65
static uint32_t dict_vendor_value_hash(void const *data)
Definition: dict.c:264
static int dict_vendor_value_cmp(void const *one, void const *two)
Definition: dict.c:270
String of printable characters.
Definition: radius.h:33
Contains nested attributes.
Definition: radius.h:47
size_t dict_print_attr_oid(char *buffer, size_t outlen, fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da)
Build the tlv_stack for the specified DA and encode the path in OID form.
Definition: dict.c:2525
PW_TYPE type
Value type.
Definition: dict.h:80
#define RCSID(id)
Definition: build.h:135
TALLOC_CTX * pool
Talloc memory pool to reduce mallocs.
Definition: dict.c:78
void fr_proto_tlv_stack_build(fr_dict_attr_t const **tlv_stack, fr_dict_attr_t const *da)
Definition: proto.c:94
int fr_dict_valid_name(char const *name)
Definition: dict.c:3730
static void dict_stat_add(fr_dict_t *dict, struct stat const *stat_buf)
Add an entry to the list of stat buffers.
Definition: dict.c:322
32 Bit IPv4 Address.
Definition: radius.h:35
static int dict_read_init(fr_dict_t *dict, char const *dir_name, char const *filename, char const *src_file, int src_line)
Definition: dict.c:1733
#define FR_DICT_ATTR_MAX_NAME_LEN
Definition: dict.h:115
static int dict_read_process_vendor(fr_dict_t *dict, char **argv, int argc)
Definition: dict.c:1665
Value of an enumerated attribute.
Definition: dict.h:94
16 Bit unsigned integer.
Definition: radius.h:43
Raw octets.
Definition: radius.h:38
#define MAX_ARGV
Definition: dict.c:36
PW_TYPE
Internal data types used within libfreeradius.
Definition: radius.h:31
fr_hash_table_t * attributes_by_name
Allow attribute lookup by unique name.
Definition: dict.c:70
#define INTERNAL_IF_NULL(_dict)
Definition: dict.c:179
#define VENDORPEC_WIMAX
Definition: radius.h:202
static int fr_dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
Add a child to a parent.
Definition: dict.c:467
Extended attribute, vendor specific.
Definition: radius.h:50
int fr_dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
Add a vendor to the dictionary.
Definition: dict.c:399
fr_dict_vendor_t const * fr_dict_vendor_by_num(fr_dict_t *dict, int vendorpec)
Look up a vendor by its PEN.
Definition: dict.c:3412
unsigned int has_value
Has a value.
Definition: dict.h:48
size_t strlcat(char *dst, char const *src, size_t siz)
Definition: strlcat.c:40