Use FR_DIR_SEP
[freeradius.git] / src / lib / dict.c
1 /*
2  * dict.c       Routines to read the dictionary file.
3  *
4  * Version:     $Id$
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
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/libradius.h>
27
28 #include        <ctype.h>
29
30 #ifdef HAVE_MALLOC_H
31 #include        <malloc.h>
32 #endif
33
34 #ifdef HAVE_SYS_STAT_H
35 #include        <sys/stat.h>
36 #endif
37
38 #define DICT_VALUE_MAX_NAME_LEN (128)
39 #define DICT_VENDOR_MAX_NAME_LEN (128)
40
41 static fr_hash_table_t *vendors_byname = NULL;
42 static fr_hash_table_t *vendors_byvalue = NULL;
43
44 static fr_hash_table_t *attributes_byname = NULL;
45 static fr_hash_table_t *attributes_byvalue = NULL;
46
47 static fr_hash_table_t *values_byvalue = NULL;
48 static fr_hash_table_t *values_byname = NULL;
49
50 static DICT_ATTR *dict_base_attrs[256];
51
52 /*
53  *      For faster HUP's, we cache the stat information for
54  *      files we've $INCLUDEd
55  */
56 typedef struct dict_stat_t {
57         struct dict_stat_t *next;
58         char               *name;
59         time_t             mtime;
60 } dict_stat_t;
61
62 static char *stat_root_dir = NULL;
63 static char *stat_root_file = NULL;
64
65 static dict_stat_t *stat_head = NULL;
66 static dict_stat_t *stat_tail = NULL;
67
68 typedef struct value_fixup_t {
69         char            attrstr[40];
70         DICT_VALUE      *dval;
71         struct value_fixup_t *next;
72 } value_fixup_t;
73
74
75 /*
76  *      So VALUEs in the dictionary can have forward references.
77  */
78 static value_fixup_t *value_fixup = NULL;
79
80 static const FR_NAME_NUMBER type_table[] = {
81         { "integer",    PW_TYPE_INTEGER },
82         { "string",     PW_TYPE_STRING },
83         { "ipaddr",     PW_TYPE_IPADDR },
84         { "date",       PW_TYPE_DATE },
85         { "abinary",    PW_TYPE_ABINARY },
86         { "octets",     PW_TYPE_OCTETS },
87         { "ifid",       PW_TYPE_IFID },
88         { "ipv6addr",   PW_TYPE_IPV6ADDR },
89         { "ipv6prefix", PW_TYPE_IPV6PREFIX },
90         { "byte",       PW_TYPE_BYTE },
91         { "short",      PW_TYPE_SHORT },
92         { "ether",      PW_TYPE_ETHERNET },
93         { NULL, 0 }
94 };
95
96
97 /*
98  *      Create the hash of the name.
99  *
100  *      We copy the hash function here because it's substantially faster.
101  */
102 #define FNV_MAGIC_INIT (0x811c9dc5)
103 #define FNV_MAGIC_PRIME (0x01000193)
104
105 static uint32_t dict_hashname(const char *name)
106 {
107         uint32_t hash = FNV_MAGIC_INIT;
108         const char *p;
109
110         for (p = name; *p != '\0'; p++) {
111                 int c = *(const unsigned char *) p;
112                 if (isalpha(c)) c = tolower(c);
113
114                 hash *= FNV_MAGIC_PRIME;
115                 hash ^= (uint32_t ) (c & 0xff);
116         }
117
118         return hash;
119 }
120
121
122 /*
123  *      Hash callback functions.
124  */
125 static uint32_t dict_attr_name_hash(const void *data)
126 {
127         return dict_hashname(((const DICT_ATTR *)data)->name);
128 }
129
130 static int dict_attr_name_cmp(const void *one, const void *two)
131 {
132         const DICT_ATTR *a = one;
133         const DICT_ATTR *b = two;
134
135         return strcasecmp(a->name, b->name);
136 }
137
138 static uint32_t dict_attr_value_hash(const void *data)
139 {
140         uint32_t hash;
141         const DICT_ATTR *attr = data;
142
143         hash = fr_hash(&attr->vendor, sizeof(attr->vendor));
144         return fr_hash_update(&attr->attr, sizeof(attr->attr), hash);
145 }
146
147 static int dict_attr_value_cmp(const void *one, const void *two)
148 {
149         const DICT_ATTR *a = one;
150         const DICT_ATTR *b = two;
151
152         if (a->vendor < b->vendor) return -1;
153         if (a->vendor > b->vendor) return +1;
154
155         return a->attr - b->attr;
156 }
157
158 static uint32_t dict_vendor_name_hash(const void *data)
159 {
160         return dict_hashname(((const DICT_VENDOR *)data)->name);
161 }
162
163 static int dict_vendor_name_cmp(const void *one, const void *two)
164 {
165         const DICT_VENDOR *a = one;
166         const DICT_VENDOR *b = two;
167
168         return strcasecmp(a->name, b->name);
169 }
170
171 static uint32_t dict_vendor_value_hash(const void *data)
172 {
173         return fr_hash(&(((const DICT_VENDOR *)data)->vendorpec),
174                          sizeof(((const DICT_VENDOR *)data)->vendorpec));
175 }
176
177 static int dict_vendor_value_cmp(const void *one, const void *two)
178 {
179         const DICT_VENDOR *a = one;
180         const DICT_VENDOR *b = two;
181
182         return a->vendorpec - b->vendorpec;
183 }
184
185 static uint32_t dict_value_name_hash(const void *data)
186 {
187         uint32_t hash;
188         const DICT_VALUE *dval = data;
189
190         hash = dict_hashname(dval->name);
191         return fr_hash_update(&dval->attr, sizeof(dval->attr), hash);
192 }
193
194 static int dict_value_name_cmp(const void *one, const void *two)
195 {
196         int rcode;
197         const DICT_VALUE *a = one;
198         const DICT_VALUE *b = two;
199
200         rcode = a->attr - b->attr;
201         if (rcode != 0) return rcode;
202
203         return strcasecmp(a->name, b->name);
204 }
205
206 static uint32_t dict_value_value_hash(const void *data)
207 {
208         uint32_t hash;
209         const DICT_VALUE *dval = data;
210
211         hash = fr_hash(&dval->attr, sizeof(dval->attr));
212         return fr_hash_update(&dval->value, sizeof(dval->value), hash);
213 }
214
215 static int dict_value_value_cmp(const void *one, const void *two)
216 {
217         int rcode;
218         const DICT_VALUE *a = one;
219         const DICT_VALUE *b = two;
220
221         rcode = a->attr - b->attr;
222         if (rcode != 0) return rcode;
223
224         return a->value - b->value;
225 }
226
227
228 /*
229  *      Free the list of stat buffers
230  */
231 static void dict_stat_free(void)
232 {
233         dict_stat_t *this, *next;
234
235         free(stat_root_dir);
236         stat_root_dir = NULL;
237         free(stat_root_file);
238         stat_root_file = NULL;
239
240         if (!stat_head) {
241                 stat_tail = NULL;
242                 return;
243         }
244
245         for (this = stat_head; this != NULL; this = next) {
246                 next = this->next;
247                 free(this->name);
248                 free(this);
249         }
250
251         stat_head = stat_tail = NULL;
252 }
253
254
255 /*
256  *      Add an entry to the list of stat buffers.
257  */
258 static void dict_stat_add(const char *name, const struct stat *stat_buf)
259 {
260         dict_stat_t *this;
261
262         this = malloc(sizeof(*this));
263         if (!this) return;
264         memset(this, 0, sizeof(*this));
265
266         this->name = strdup(name);
267         this->mtime = stat_buf->st_mtime;
268
269         if (!stat_head) {
270                 stat_head = stat_tail = this;
271         } else {
272                 stat_tail->next = this;
273                 stat_tail = this;
274         }
275 }
276
277
278 /*
279  *      See if any dictionaries have changed.  If not, don't
280  *      do anything.
281  */
282 static int dict_stat_check(const char *root_dir, const char *root_file)
283 {
284         struct stat buf;
285         dict_stat_t *this;
286
287         if (!stat_root_dir) return 0;
288         if (!stat_root_file) return 0;
289
290         if (strcmp(root_dir, stat_root_dir) != 0) return 0;
291         if (strcmp(root_file, stat_root_file) != 0) return 0;
292
293         if (!stat_head) return 0; /* changed, reload */
294
295         for (this = stat_head; this != NULL; this = this->next) {
296                 if (stat(this->name, &buf) < 0) return 0;
297
298                 if (buf.st_mtime != this->mtime) return 0;
299         }
300
301         return 1;
302 }
303
304 typedef struct fr_pool_t {
305         void    *page_end;
306         void    *free_ptr;
307         struct fr_pool_t *page_free;
308         struct fr_pool_t *page_next;
309 } fr_pool_t;
310
311 #define FR_POOL_SIZE (32768)
312 #define FR_ALLOC_ALIGN (8)
313
314 static fr_pool_t *dict_pool = NULL;
315
316 static fr_pool_t *fr_pool_create(void)
317 {
318         fr_pool_t *fp = malloc(FR_POOL_SIZE);
319
320         if (!fp) return NULL;
321
322         fp->page_end = ((uint8_t *) fp) + FR_POOL_SIZE;
323         fp->free_ptr = ((uint8_t *) fp) + sizeof(*fp);
324         fp->page_free = fp;
325         fp->page_next = NULL;
326         return fp;
327 }
328
329 static void fr_pool_delete(fr_pool_t **pfp)
330 {
331         fr_pool_t *fp, *next;
332
333         if (!pfp || !*pfp) return;
334
335         for (fp = *pfp; fp != NULL; fp = next) {
336                 next = fp->page_next;
337                 free(fp);
338         }
339 }
340
341
342 static void *fr_pool_alloc(size_t size)
343 {
344         void *ptr;
345
346         if (size == 0) return NULL;
347
348         if (size > 256) return NULL; /* shouldn't happen */
349
350         if (!dict_pool) {
351                 dict_pool = fr_pool_create();
352                 if (!dict_pool) return NULL;
353         }
354
355         if ((size & (FR_ALLOC_ALIGN - 1)) != 0) {
356                 size += FR_ALLOC_ALIGN - (size & (FR_ALLOC_ALIGN - 1));
357         }
358
359         if ((((uint8_t *) dict_pool->page_free->free_ptr) + size) > (uint8_t *) dict_pool->page_free->page_end) {
360                 dict_pool->page_free->page_next = fr_pool_create();
361                 if (!dict_pool->page_free->page_next) return NULL;
362                 dict_pool->page_free = dict_pool->page_free->page_next;
363         }
364
365         ptr = dict_pool->page_free->free_ptr;
366         dict_pool->page_free->free_ptr = ((uint8_t *) dict_pool->page_free->free_ptr) + size;
367
368         return ptr;
369 }
370
371
372 static void fr_pool_free(UNUSED void *ptr)
373 {
374         /*
375          *      Place-holder for later code.
376          */
377 }
378
379 /*
380  *      Free the dictionary_attributes and dictionary_values lists.
381  */
382 void dict_free(void)
383 {
384         /*
385          *      Free the tables
386          */
387         fr_hash_table_free(vendors_byname);
388         fr_hash_table_free(vendors_byvalue);
389         vendors_byname = NULL;
390         vendors_byvalue = NULL;
391
392         fr_hash_table_free(attributes_byname);
393         fr_hash_table_free(attributes_byvalue);
394         attributes_byname = NULL;
395         attributes_byvalue = NULL;
396
397         fr_hash_table_free(values_byname);
398         fr_hash_table_free(values_byvalue);
399         values_byname = NULL;
400         values_byvalue = NULL;
401
402         memset(dict_base_attrs, 0, sizeof(dict_base_attrs));
403
404         fr_pool_delete(&dict_pool);
405
406         dict_stat_free();
407 }
408
409
410 /*
411  *      Add vendor to the list.
412  */
413 int dict_addvendor(const char *name, int value)
414 {
415         size_t length;
416         DICT_VENDOR *dv;
417
418         if (value >= 32767) {
419                 librad_log("dict_addvendor: Cannot handle vendor ID larger than 65535");
420                 return -1;
421         }
422
423         if ((length = strlen(name)) >= DICT_VENDOR_MAX_NAME_LEN) {
424                 librad_log("dict_addvendor: vendor name too long");
425                 return -1;
426         }
427
428         if ((dv = fr_pool_alloc(sizeof(*dv) + length)) == NULL) {
429                 librad_log("dict_addvendor: out of memory");
430                 return -1;
431         }
432
433         strcpy(dv->name, name);
434         dv->vendorpec  = value;
435         dv->type = dv->length = 1; /* defaults */
436
437         if (!fr_hash_table_insert(vendors_byname, dv)) {
438                 DICT_VENDOR *old_dv;
439
440                 old_dv = fr_hash_table_finddata(vendors_byname, dv);
441                 if (!old_dv) {
442                         librad_log("dict_addvendor: Failed inserting vendor name %s", name);
443                         return -1;
444                 }
445                 if (old_dv->vendorpec != dv->vendorpec) {
446                         librad_log("dict_addvendor: Duplicate vendor name %s", name);
447                         return -1;
448                 }
449
450                 /*
451                  *      Already inserted.  Discard the duplicate entry.
452                  */
453                 fr_pool_free(dv);
454                 return 0;
455         }
456
457         /*
458          *      Insert the SAME pointer (not free'd when this table is
459          *      deleted), into another table.
460          *
461          *      We want this behaviour because we want OLD names for
462          *      the attributes to be read from the configuration
463          *      files, but when we're printing them, (and looking up
464          *      by value) we want to use the NEW name.
465          */
466         if (!fr_hash_table_replace(vendors_byvalue, dv)) {
467                 librad_log("dict_addvendor: Failed inserting vendor %s",
468                            name);
469                 return -1;
470         }
471
472         return 0;
473 }
474
475 /*
476  *      Add an attribute to the dictionary.
477  */
478 int dict_addattr(const char *name, int vendor, int type, int value,
479                  ATTR_FLAGS flags)
480 {
481         static int      max_attr = 0;
482         DICT_ATTR       *attr;
483
484         if (strlen(name) > (sizeof(attr->name) -1)) {
485                 librad_log("dict_addattr: attribute name too long");
486                 return -1;
487         }
488
489         /*
490          *      If the value is '-1', that means use a pre-existing
491          *      one (if it already exists).  If one does NOT already exist,
492          *      then create a new attribute, with a non-conflicting value,
493          *      and use that.
494          */
495         if (value == -1) {
496                 if (dict_attrbyname(name)) {
497                         return 0; /* exists, don't add it again */
498                 }
499
500                 value = ++max_attr;
501
502         } else if (vendor == 0) {
503                 /*
504                  *  Update 'max_attr'
505                  */
506                 if (value > max_attr) {
507                         max_attr = value;
508                 }
509         }
510
511         if (value < 0) {
512                 librad_log("dict_addattr: ATTRIBUTE has invalid number (less than zero)");
513                 return -1;
514         }
515
516         if (value >= 65536) {
517                 librad_log("dict_addattr: ATTRIBUTE has invalid number (larger than 65535).");
518                 return -1;
519         }
520
521         if (vendor) {
522                 DICT_VENDOR *dv;
523                 static DICT_VENDOR *last_vendor = NULL;
524
525                 /*
526                  *      Most ATTRIBUTEs are bunched together by
527                  *      VENDOR.  We can save a lot of lookups on
528                  *      dictionary initialization by caching the last
529                  *      vendor.
530                  */
531                 if (last_vendor && (vendor == last_vendor->vendorpec)) {
532                         dv = last_vendor;
533                 } else {
534                         dv = dict_vendorbyvalue(vendor);
535                         last_vendor = dv;
536                 }
537
538                 /*
539                  *      If the vendor isn't defined, die.
540                  */
541                 if (!dv) {
542                         librad_log("dict_addattr: Unknown vendor");
543                         return -1;
544                 }
545
546                 /*
547                  *      FIXME: Switch over dv->type, and limit things
548                  *      properly.
549                  */
550                 if ((dv->type == 1) && (value >= 256)) {
551                         librad_log("dict_addattr: ATTRIBUTE has invalid number (larger than 255).");
552                         return -1;
553                 } /* else 256..65535 are allowed */
554         }
555
556         /*
557          *      Create a new attribute for the list
558          */
559         if ((attr = fr_pool_alloc(sizeof(*attr))) == NULL) {
560                 librad_log("dict_addattr: out of memory");
561                 return -1;
562         }
563
564         strcpy(attr->name, name);
565         attr->attr = value;
566         attr->attr |= (vendor << 16); /* FIXME: hack */
567         attr->vendor = vendor;
568         attr->type = type;
569         attr->flags = flags;
570         attr->vendor = vendor;
571
572         /*
573          *      Insert the attribute, only if it's not a duplicate.
574          */
575         if (!fr_hash_table_insert(attributes_byname, attr)) {
576                 DICT_ATTR       *a;
577
578                 /*
579                  *      If the attribute has identical number, then
580                  *      ignore the duplicate.
581                  */
582                 a = fr_hash_table_finddata(attributes_byname, attr);
583                 if (a && (strcasecmp(a->name, attr->name) == 0)) {
584                         if (a->attr != attr->attr) {
585                                 librad_log("dict_addattr: Duplicate attribute name %s", name);
586                                 fr_pool_free(attr);
587                                 return -1;
588                         }
589
590                         /*
591                          *      Same name, same vendor, same attr,
592                          *      maybe the flags and/or type is
593                          *      different.  Let the new value
594                          *      over-ride the old one.
595                          */
596                 }
597
598
599                 fr_hash_table_delete(attributes_byvalue, a);
600
601                 if (!fr_hash_table_replace(attributes_byname, attr)) {
602                         librad_log("dict_addattr: Internal error storing attribute %s", name);
603                         fr_pool_free(attr);
604                         return -1;
605                 }
606         }
607
608         /*
609          *      Insert the SAME pointer (not free'd when this entry is
610          *      deleted), into another table.
611          *
612          *      We want this behaviour because we want OLD names for
613          *      the attributes to be read from the configuration
614          *      files, but when we're printing them, (and looking up
615          *      by value) we want to use the NEW name.
616          */
617         if (!fr_hash_table_replace(attributes_byvalue, attr)) {
618                 librad_log("dict_addattr: Failed inserting attribute name %s", name);
619                 return -1;
620         }
621
622         if (!vendor && (value > 0) && (value < 256)) {
623                  dict_base_attrs[value] = attr;
624         }
625
626         return 0;
627 }
628
629
630 /*
631  *      Add a value for an attribute to the dictionary.
632  */
633 int dict_addvalue(const char *namestr, const char *attrstr, int value)
634 {
635         size_t          length;
636         DICT_ATTR       *dattr;
637         DICT_VALUE      *dval;
638
639         static DICT_ATTR *last_attr = NULL;
640
641         if (!*namestr) {
642                 librad_log("dict_addvalue: empty names are not permitted");
643                 return -1;
644         }
645
646         if ((length = strlen(namestr)) >= DICT_VALUE_MAX_NAME_LEN) {
647                 librad_log("dict_addvalue: value name too long");
648                 return -1;
649         }
650
651         if ((dval = fr_pool_alloc(sizeof(*dval) + length)) == NULL) {
652                 librad_log("dict_addvalue: out of memory");
653                 return -1;
654         }
655         memset(dval, 0, sizeof(*dval));
656
657         strcpy(dval->name, namestr);
658         dval->value = value;
659
660         /*
661          *      Most VALUEs are bunched together by ATTRIBUTE.  We can
662          *      save a lot of lookups on dictionary initialization by
663          *      caching the last attribute.
664          */
665         if (last_attr && (strcasecmp(attrstr, last_attr->name) == 0)) {
666                 dattr = last_attr;
667         } else {
668                 dattr = dict_attrbyname(attrstr);
669                 last_attr = dattr;
670         }
671
672         /*
673          *      Remember which attribute is associated with this
674          *      value, if possible.
675          */
676         if (dattr) {
677                 if (dattr->flags.has_value_alias) {
678                         librad_log("dict_addvalue: Cannot add VALUE for ATTRIBUTE \"%s\": It already has a VALUE-ALIAS", attrstr);
679                         return -1;
680                 }
681
682                 dval->attr = dattr->attr;
683
684                 /*
685                  *      Enforce valid values
686                  *
687                  *      Don't worry about fixups...
688                  */
689                 switch (dattr->type) {
690                         case PW_TYPE_BYTE:
691                                 if (value > 255) {
692                                         fr_pool_free(dval);
693                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'byte' cannot have VALUEs larger than 255");
694                                         return -1;
695                                 }
696                                 break;
697                         case PW_TYPE_SHORT:
698                                 if (value > 65535) {
699                                         fr_pool_free(dval);
700                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'short' cannot have VALUEs larger than 65535");
701                                         return -1;
702                                 }
703                                 break;
704
705                                 /*
706                                  *      Allow octets for now, because
707                                  *      of dictionary.cablelabs
708                                  */
709                         case PW_TYPE_OCTETS:
710
711                         case PW_TYPE_INTEGER:
712                                 break;
713
714                         default:
715                                 fr_pool_free(dval);
716                                 librad_log("dict_addvalue: VALUEs cannot be defined for attributes of type '%s'",
717                                            fr_int2str(type_table, dattr->type, "?Unknown?"));
718                                 return -1;
719                 }
720
721                 dattr->flags.has_value = 1;
722         } else {
723                 value_fixup_t *fixup;
724
725                 fixup = (value_fixup_t *) malloc(sizeof(*fixup));
726                 if (!fixup) {
727                         fr_pool_free(dval);
728                         librad_log("dict_addvalue: out of memory");
729                         return -1;
730                 }
731                 memset(fixup, 0, sizeof(*fixup));
732
733                 strlcpy(fixup->attrstr, attrstr, sizeof(fixup->attrstr));
734                 fixup->dval = dval;
735
736                 /*
737                  *      Insert to the head of the list.
738                  */
739                 fixup->next = value_fixup;
740                 value_fixup = fixup;
741
742                 return 0;
743         }
744
745         /*
746          *      Add the value into the dictionary.
747          */
748         if (!fr_hash_table_insert(values_byname, dval)) {
749                 if (dattr) {
750                         DICT_VALUE *old;
751
752                         /*
753                          *      Suppress duplicates with the same
754                          *      name and value.  There are lots in
755                          *      dictionary.ascend.
756                          */
757                         old = dict_valbyname(dattr->attr, namestr);
758                         if (old && (old->value == dval->value)) {
759                                 fr_pool_free(dval);
760                                 return 0;
761                         }
762                 }
763
764                 fr_pool_free(dval);
765                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr);
766                 return -1;
767         }
768
769         /*
770          *      There are multiple VALUE's, keyed by attribute, so we
771          *      take care of that here.
772          */
773         if (!fr_hash_table_replace(values_byvalue, dval)) {
774                 librad_log("dict_addvalue: Failed inserting value %s",
775                            namestr);
776                 return -1;
777         }
778
779         return 0;
780 }
781
782 static int sscanf_i(const char *str, int *pvalue)
783 {
784         int rcode = 0;
785         int base = 10;
786         const char *tab = "0123456789";
787
788         if ((str[0] == '0') &&
789             ((str[1] == 'x') || (str[1] == 'X'))) {
790                 tab = "0123456789abcdef";
791                 base = 16;
792
793                 str += 2;
794         }
795
796         while (*str) {
797                 const char *c;
798
799                 c = memchr(tab, tolower((int) *str), base);
800                 if (!c) return 0;
801
802                 rcode *= base;
803                 rcode += (c - tab);
804                 str++;
805         }
806
807         *pvalue = rcode;
808         return 1;
809 }
810
811
812 /*
813  *      Process the ATTRIBUTE command
814  */
815 static int process_attribute(const char* fn, const int line,
816                              const int block_vendor, char **argv,
817                              int argc)
818 {
819         int             vendor = 0;
820         int             value;
821         int             type;
822         ATTR_FLAGS      flags;
823
824         if ((argc < 3) || (argc > 4)) {
825                 librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line",
826                         fn, line);
827                 return -1;
828         }
829
830         /*
831          *      Validate all entries
832          */
833         if (!sscanf_i(argv[1], &value)) {
834                 librad_log("dict_init: %s[%d]: invalid value", fn, line);
835                 return -1;
836         }
837
838         /*
839          *      find the type of the attribute.
840          */
841         type = fr_str2int(type_table, argv[2], -1);
842         if (type < 0) {
843                 librad_log("dict_init: %s[%d]: invalid type \"%s\"",
844                         fn, line, argv[2]);
845                 return -1;
846         }
847
848         /*
849          *      Only look up the vendor if the string
850          *      is non-empty.
851          */
852         memset(&flags, 0, sizeof(flags));
853         if (argc == 4) {
854                 char *key, *next, *last;
855
856                 key = argv[3];
857                 do {
858                         next = strchr(key, ',');
859                         if (next) *(next++) = '\0';
860
861                         if (strcmp(key, "has_tag") == 0 ||
862                             strcmp(key, "has_tag=1") == 0) {
863                                 /* Boolean flag, means this is a
864                                    tagged attribute */
865                                 flags.has_tag = 1;
866                                 
867                         } else if (strncmp(key, "encrypt=", 8) == 0) {
868                                 /* Encryption method, defaults to 0 (none).
869                                    Currently valid is just type 2,
870                                    Tunnel-Password style, which can only
871                                    be applied to strings. */
872                                 flags.encrypt = strtol(key + 8, &last, 0);
873                                 if (*last) {
874                                         librad_log( "dict_init: %s[%d] invalid option %s",
875                                                     fn, line, key);
876                                         return -1;
877                                 }
878                                 
879                         } else if (strncmp(key, "array", 8) == 0) {
880                                 flags.array = 1;
881                                 
882                                 switch (type) {
883                                         case PW_TYPE_IPADDR:
884                                         case PW_TYPE_BYTE:
885                                         case PW_TYPE_SHORT:
886                                         case PW_TYPE_INTEGER:
887                                         case PW_TYPE_DATE:
888                                                 break;
889
890                                         default:
891                                                 librad_log( "dict_init: %s[%d] Only IP addresses can have the \"array\" flag set.",
892                                                             fn, line);
893                                                 return -1;
894                                 }
895                                 
896                         } else if (block_vendor) {
897                                 librad_log( "dict_init: %s[%d]: unknown option \"%s\"",
898                                             fn, line, key);
899                                 return -1;
900
901                         } else {
902                                 /* Must be a vendor 'flag'... */
903                                 if (strncmp(key, "vendor=", 7) == 0) {
904                                         /* New format */
905                                         key += 7;
906                                 }
907
908                                 vendor = dict_vendorbyname(key);
909                                 if (!vendor) {
910                                         librad_log( "dict_init: %s[%d]: unknown vendor \"%s\"",
911                                                     fn, line, key);
912                                         return -1;
913                                 }
914                                 if (block_vendor && argv[3][0] &&
915                                     (block_vendor != vendor)) {
916                                         librad_log("dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block",
917                                                    fn, line, argv[3]);
918                                         return -1;
919                                 }
920                         }
921
922                         key = next;
923                         if (key && !*key) break;
924                 } while (key);
925         }
926
927         if (block_vendor) vendor = block_vendor;
928
929         /*
930          *      Special checks for tags, they make our life much more
931          *      difficult.
932          */
933         if (flags.has_tag) {
934                 /*
935                  *      Only string, octets, and integer can be tagged.
936                  */
937                 switch (type) {
938                 case PW_TYPE_STRING:
939                 case PW_TYPE_INTEGER:
940                         break;
941
942                 default:
943                         librad_log("dict_init: %s[%d]: Attributes of type %s cannot be tagged.",
944                                    fn, line,
945                                    fr_int2str(type_table, type, "?Unknown?"));
946                         return -1;
947
948                 }
949         }
950
951         /*
952          *      Add it in.
953          */
954         if (dict_addattr(argv[0], vendor, type, value, flags) < 0) {
955                 librad_log("dict_init: %s[%d]: %s",
956                            fn, line, librad_errstr);
957                 return -1;
958         }
959
960         return 0;
961 }
962
963
964 /*
965  *      Process the VALUE command
966  */
967 static int process_value(const char* fn, const int line, char **argv,
968                          int argc)
969 {
970         int     value;
971
972         if (argc != 3) {
973                 librad_log("dict_init: %s[%d]: invalid VALUE line",
974                         fn, line);
975                 return -1;
976         }
977         /*
978          *      For Compatibility, skip "Server-Config"
979          */
980         if (strcasecmp(argv[0], "Server-Config") == 0)
981                 return 0;
982
983         /*
984          *      Validate all entries
985          */
986         if (!sscanf_i(argv[2], &value)) {
987                 librad_log("dict_init: %s[%d]: invalid value",
988                         fn, line);
989                 return -1;
990         }
991
992         if (dict_addvalue(argv[1], argv[0], value) < 0) {
993                 librad_log("dict_init: %s[%d]: %s",
994                            fn, line, librad_errstr);
995                 return -1;
996         }
997
998         return 0;
999 }
1000
1001
1002 /*
1003  *      Process the VALUE-ALIAS command
1004  *
1005  *      This allows VALUE mappings to be shared among multiple
1006  *      attributes.
1007  */
1008 static int process_value_alias(const char* fn, const int line, char **argv,
1009                                int argc)
1010 {
1011         DICT_ATTR *my_da, *da;
1012         DICT_VALUE *dval;
1013
1014         if (argc != 2) {
1015                 librad_log("dict_init: %s[%d]: invalid VALUE-ALIAS line",
1016                         fn, line);
1017                 return -1;
1018         }
1019
1020         my_da = dict_attrbyname(argv[0]);
1021         if (!my_da) {
1022                 librad_log("dict_init: %s[%d]: ATTRIBUTE \"%s\" does not exist",
1023                            fn, line, argv[1]);
1024                 return -1;
1025         }
1026
1027         if (my_da->flags.has_value) {
1028                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" with pre-existing VALUE",
1029                            fn, line, argv[0]);
1030                 return -1;
1031         }
1032
1033         if (my_da->flags.has_value_alias) {
1034                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" with pre-existing VALUE-ALIAS",
1035                            fn, line, argv[0]);
1036                 return -1;
1037         }
1038
1039         da = dict_attrbyname(argv[1]);
1040         if (!da) {
1041                 librad_log("dict_init: %s[%d]: Cannot find ATTRIBUTE \"%s\" for alias",
1042                            fn, line, argv[1]);
1043                 return -1;
1044         }
1045
1046         if (!da->flags.has_value) {
1047                 librad_log("dict_init: %s[%d]: VALUE-ALIAS cannot refer to ATTRIBUTE %s: It has no values",
1048                            fn, line, argv[1]);
1049                 return -1;
1050         }
1051
1052         if (da->flags.has_value_alias) {
1053                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" which itself has a VALUE-ALIAS",
1054                            fn, line, argv[1]);
1055                 return -1;
1056         }
1057
1058         if (my_da->type != da->type) {
1059                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS between attributes of differing type",
1060                            fn, line);
1061                 return -1;
1062         }
1063
1064         if ((dval = fr_pool_alloc(sizeof(*dval))) == NULL) {
1065                 librad_log("dict_addvalue: out of memory");
1066                 return -1;
1067         }
1068         memset(dval, 0, sizeof(*dval));
1069
1070         dval->name[0] = '\0';   /* empty name */
1071         dval->attr = my_da->attr;
1072         dval->value = da->attr;
1073
1074         if (!fr_hash_table_insert(values_byname, dval)) {
1075                 librad_log("dict_init: %s[%d]: Error create alias",
1076                            fn, line);
1077                 fr_pool_free(dval);
1078                 return -1;
1079         }
1080
1081         return 0;
1082 }
1083
1084
1085 /*
1086  *      Process the VENDOR command
1087  */
1088 static int process_vendor(const char* fn, const int line, char **argv,
1089                           int argc)
1090 {
1091         int     value;
1092         const   char *format = NULL;
1093
1094         if ((argc < 2) || (argc > 3)) {
1095                 librad_log( "dict_init: %s[%d] invalid VENDOR entry",
1096                             fn, line);
1097                 return -1;
1098         }
1099
1100         /*
1101          *       Validate all entries
1102          */
1103         if (!isdigit((int) argv[1][0])) {
1104                 librad_log("dict_init: %s[%d]: invalid value",
1105                         fn, line);
1106                 return -1;
1107         }
1108         value = atoi(argv[1]);
1109
1110         /* Create a new VENDOR entry for the list */
1111         if (dict_addvendor(argv[0], value) < 0) {
1112                 librad_log("dict_init: %s[%d]: %s",
1113                            fn, line, librad_errstr);
1114                 return -1;
1115         }
1116
1117         /*
1118          *      Look for a format statement
1119          */
1120         if (argc == 3) {
1121                 format = argv[2];
1122
1123         } else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */
1124                 format = "format=4,0";
1125
1126         } else if (value == VENDORPEC_LUCENT) {
1127                 format = "format=2,1";
1128
1129         } else if (value == VENDORPEC_STARENT) {
1130                 format = "format=2,2";
1131
1132         } /* else no fixups to do */
1133
1134         if (format) {
1135                 int type, length;
1136                 const char *p;
1137                 DICT_VENDOR *dv;
1138
1139                 if (strncasecmp(format, "format=", 7) != 0) {
1140                         librad_log("dict_init: %s[%d]: Invalid format for VENDOR.  Expected \"format=\", got \"%s\"",
1141                                    fn, line, format);
1142                         return -1;
1143                 }
1144
1145                 p = format + 7;
1146                 if ((strlen(p) != 3) ||
1147                     !isdigit((int) p[0]) ||
1148                     (p[1] != ',') ||
1149                     !isdigit((int) p[2])) {
1150                         librad_log("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
1151                                    fn, line, p);
1152                         return -1;
1153                 }
1154
1155                 type = (int) (p[0] - '0');
1156                 length = (int) (p[2] - '0');
1157
1158                 dv = dict_vendorbyvalue(value);
1159                 if (!dv) {
1160                         librad_log("dict_init: %s[%d]: Failed adding format for VENDOR",
1161                                    fn, line);
1162                         return -1;
1163                 }
1164
1165                 if ((type != 1) && (type != 2) && (type != 4)) {
1166                         librad_log("dict_init: %s[%d]: invalid type value %d for VENDOR",
1167                                    fn, line, type);
1168                         return -1;
1169                 }
1170
1171                 if ((length != 0) && (length != 1) && (length != 2)) {
1172                         librad_log("dict_init: %s[%d]: invalid length value %d for VENDOR",
1173                                    fn, line, length);
1174                         return -1;
1175                 }
1176
1177                 dv->type = type;
1178                 dv->length = length;
1179         }
1180
1181         return 0;
1182 }
1183
1184 /*
1185  *      String split routine.  Splits an input string IN PLACE
1186  *      into pieces, based on spaces.
1187  */
1188 static int str2argv(char *str, char **argv, int max_argc)
1189 {
1190         int argc = 0;
1191
1192         while (*str) {
1193                 if (argc >= max_argc) return argc;
1194
1195                 /*
1196                  *      Chop out comments early.
1197                  */
1198                 if (*str == '#') {
1199                         *str = '\0';
1200                         break;
1201                 }
1202
1203                 while ((*str == ' ') ||
1204                        (*str == '\t') ||
1205                        (*str == '\r') ||
1206                        (*str == '\n')) *(str++) = '\0';
1207
1208                 if (!*str) return argc;
1209
1210                 argv[argc] = str;
1211                 argc++;
1212
1213                 while (*str &&
1214                        (*str != ' ') &&
1215                        (*str != '\t') &&
1216                        (*str != '\r') &&
1217                        (*str != '\n')) str++;
1218         }
1219
1220         return argc;
1221 }
1222
1223 #define MAX_ARGV (16)
1224
1225 /*
1226  *      Initialize the dictionary.
1227  */
1228 static int my_dict_init(const char *dir, const char *fn,
1229                         const char *src_file, int src_line)
1230 {
1231         FILE    *fp;
1232         char    dirtmp[256];
1233         char    buf[256];
1234         char    *p;
1235         int     line = 0;
1236         int     vendor;
1237         int     block_vendor;
1238         struct stat statbuf;
1239         char    *argv[MAX_ARGV];
1240         int     argc;
1241
1242         if (strlen(fn) >= sizeof(dirtmp) / 2 ||
1243             strlen(dir) >= sizeof(dirtmp) / 2) {
1244                 librad_log("dict_init: filename name too long");
1245                 return -1;
1246         }
1247
1248         /*
1249          *      First see if fn is relative to dir. If so, create
1250          *      new filename. If not, remember the absolute dir.
1251          */
1252         if ((p = strrchr(fn, FR_DIR_SEP)) != NULL) {
1253                 strcpy(dirtmp, fn);
1254                 dirtmp[p - fn] = 0;
1255                 dir = dirtmp;
1256         } else if (dir && dir[0] && strcmp(dir, ".") != 0) {
1257                 snprintf(dirtmp, sizeof(dirtmp), "%s/%s", dir, fn);
1258                 fn = dirtmp;
1259         }
1260
1261         if ((fp = fopen(fn, "r")) == NULL) {
1262                 if (!src_file) {
1263                         librad_log("dict_init: Couldn't open dictionary \"%s\": %s",
1264                                    fn, strerror(errno));
1265                 } else {
1266                         librad_log("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s",
1267                                    src_file, src_line, fn, strerror(errno));
1268                 }
1269                 return -1;
1270         }
1271
1272         stat(fn, &statbuf); /* fopen() guarantees this will succeed */
1273         if (!S_ISREG(statbuf.st_mode)) {
1274                 fclose(fp);
1275                 librad_log("dict_init: Dictionary \"%s\" is not a regular file",
1276                            fn);
1277                 return -1;
1278         }
1279
1280         /*
1281          *      Globally writable dictionaries means that users can control
1282          *      the server configuration with little difficulty.
1283          */
1284 #ifdef S_IWOTH
1285         if ((statbuf.st_mode & S_IWOTH) != 0) {
1286                 fclose(fp);
1287                 librad_log("dict_init: Dictionary \"%s\" is globally writable.  Refusing to start due to insecure configuration.",
1288                            fn);
1289                 return -1;
1290         }
1291 #endif
1292
1293         dict_stat_add(fn, &statbuf);
1294
1295         /*
1296          *      Seed the random pool with data.
1297          */
1298         fr_rand_seed(&statbuf, sizeof(statbuf));
1299
1300         block_vendor = 0;
1301
1302         while (fgets(buf, sizeof(buf), fp) != NULL) {
1303                 line++;
1304                 if (buf[0] == '#' || buf[0] == 0 ||
1305                     buf[0] == '\n' || buf[0] == '\r')
1306                         continue;
1307
1308                 /*
1309                  *  Comment characters should NOT be appearing anywhere but
1310                  *  as start of a comment;
1311                  */
1312                 p = strchr(buf, '#');
1313                 if (p) *p = '\0';
1314
1315                 argc = str2argv(buf, argv, MAX_ARGV);
1316                 if (argc == 0) continue;
1317
1318                 if (argc == 1) {
1319                         librad_log( "dict_init: %s[%d] invalid entry",
1320                                     fn, line);
1321                         fclose(fp);
1322                         return -1;
1323                 }
1324
1325                 /*
1326                  *      Process VALUE lines.
1327                  */
1328                 if (strcasecmp(argv[0], "VALUE") == 0) {
1329                         if (process_value(fn, line,
1330                                           argv + 1, argc - 1) == -1) {
1331                                 fclose(fp);
1332                                 return -1;
1333                         }
1334                         continue;
1335                 }
1336
1337                 /*
1338                  *      Perhaps this is an attribute.
1339                  */
1340                 if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
1341                         if (process_attribute(fn, line, block_vendor,
1342                                               argv + 1, argc - 1) == -1) {
1343                                 fclose(fp);
1344                                 return -1;
1345                         }
1346                         continue;
1347                 }
1348
1349                 /*
1350                  *      See if we need to import another dictionary.
1351                  */
1352                 if (strcasecmp(argv[0], "$INCLUDE") == 0) {
1353                         if (my_dict_init(dir, argv[1], fn, line) < 0) {
1354                                 fclose(fp);
1355                                 return -1;
1356                         }
1357                         continue;
1358                 } /* $INCLUDE */
1359
1360                 if (strcasecmp(argv[0], "VALUE-ALIAS") == 0) {
1361                         if (process_value_alias(fn, line,
1362                                                 argv + 1, argc - 1) == -1) {
1363                                 fclose(fp);
1364                                 return -1;
1365                         }
1366                         continue;
1367                 }
1368
1369                 /*
1370                  *      Process VENDOR lines.
1371                  */
1372                 if (strcasecmp(argv[0], "VENDOR") == 0) {
1373                         if (process_vendor(fn, line,
1374                                            argv + 1, argc - 1) == -1) {
1375                                 fclose(fp);
1376                                 return -1;
1377                         }
1378                         continue;
1379                 }
1380
1381                 if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) {
1382                         if (argc != 2) {
1383                                 librad_log(
1384                                 "dict_init: %s[%d] invalid BEGIN-VENDOR entry",
1385                                         fn, line);
1386                                 fclose(fp);
1387                                 return -1;
1388                         }
1389
1390                         vendor = dict_vendorbyname(argv[1]);
1391                         if (!vendor) {
1392                                 librad_log(
1393                                         "dict_init: %s[%d]: unknown vendor %s",
1394                                         fn, line, argv[1]);
1395                                 fclose(fp);
1396                                 return -1;
1397                         }
1398                         block_vendor = vendor;
1399                         continue;
1400                 } /* BEGIN-VENDOR */
1401
1402                 if (strcasecmp(argv[0], "END-VENDOR") == 0) {
1403                         if (argc != 2) {
1404                                 librad_log(
1405                                 "dict_init: %s[%d] invalid END-VENDOR entry",
1406                                         fn, line);
1407                                 fclose(fp);
1408                                 return -1;
1409                         }
1410
1411                         vendor = dict_vendorbyname(argv[1]);
1412                         if (!vendor) {
1413                                 librad_log(
1414                                         "dict_init: %s[%d]: unknown vendor %s",
1415                                         fn, line, argv[1]);
1416                                 fclose(fp);
1417                                 return -1;
1418                         }
1419
1420                         if (vendor != block_vendor) {
1421                                 librad_log(
1422                                         "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR",
1423                                         fn, line, argv[1]);
1424                                 fclose(fp);
1425                                 return -1;
1426                         }
1427                         block_vendor = 0;
1428                         continue;
1429                 } /* END-VENDOR */
1430
1431                 /*
1432                  *      Any other string: We don't recognize it.
1433                  */
1434                 librad_log("dict_init: %s[%d] invalid keyword \"%s\"",
1435                            fn, line, argv[0]);
1436                 fclose(fp);
1437                 return -1;
1438         }
1439         fclose(fp);
1440         return 0;
1441 }
1442
1443
1444 /*
1445  *      Empty callback for hash table initialization.
1446  */
1447 static int null_callback(void *ctx, void *data)
1448 {
1449         ctx = ctx;              /* -Wunused */
1450         data = data;            /* -Wunused */
1451
1452         return 0;
1453 }
1454
1455
1456 /*
1457  *      Initialize the directory, then fix the attr member of
1458  *      all attributes.
1459  */
1460 int dict_init(const char *dir, const char *fn)
1461 {
1462         /*
1463          *      Check if we need to change anything.  If not, don't do
1464          *      anything.
1465          */
1466         if (dict_stat_check(dir, fn)) {
1467                 return 0;
1468         }
1469
1470         /*
1471          *      Free the dictionaries, and the stat cache.
1472          */
1473         dict_free();
1474         stat_root_dir = strdup(dir);
1475         stat_root_file = strdup(fn);
1476
1477         /*
1478          *      Create the table of vendor by name.   There MAY NOT
1479          *      be multiple vendors of the same name.
1480          *
1481          *      Each vendor is malloc'd, so the free function is free.
1482          */
1483         vendors_byname = fr_hash_table_create(dict_vendor_name_hash,
1484                                                 dict_vendor_name_cmp,
1485                                                 fr_pool_free);
1486         if (!vendors_byname) {
1487                 return -1;
1488         }
1489
1490         /*
1491          *      Create the table of vendors by value.  There MAY
1492          *      be vendors of the same value.  If there are, we
1493          *      pick the latest one.
1494          */
1495         vendors_byvalue = fr_hash_table_create(dict_vendor_value_hash,
1496                                                  dict_vendor_value_cmp,
1497                                                  fr_pool_free);
1498         if (!vendors_byvalue) {
1499                 return -1;
1500         }
1501
1502         /*
1503          *      Create the table of attributes by name.   There MAY NOT
1504          *      be multiple attributes of the same name.
1505          *
1506          *      Each attribute is malloc'd, so the free function is free.
1507          */
1508         attributes_byname = fr_hash_table_create(dict_attr_name_hash,
1509                                                    dict_attr_name_cmp,
1510                                                    fr_pool_free);
1511         if (!attributes_byname) {
1512                 return -1;
1513         }
1514
1515         /*
1516          *      Create the table of attributes by value.  There MAY
1517          *      be attributes of the same value.  If there are, we
1518          *      pick the latest one.
1519          */
1520         attributes_byvalue = fr_hash_table_create(dict_attr_value_hash,
1521                                                     dict_attr_value_cmp,
1522                                                     fr_pool_free);
1523         if (!attributes_byvalue) {
1524                 return -1;
1525         }
1526
1527         values_byname = fr_hash_table_create(dict_value_name_hash,
1528                                                dict_value_name_cmp,
1529                                                fr_pool_free);
1530         if (!values_byname) {
1531                 return -1;
1532         }
1533
1534         values_byvalue = fr_hash_table_create(dict_value_value_hash,
1535                                                 dict_value_value_cmp,
1536                                                 fr_pool_free);
1537         if (!values_byvalue) {
1538                 return -1;
1539         }
1540
1541         value_fixup = NULL;     /* just to be safe. */
1542
1543         if (my_dict_init(dir, fn, NULL, 0) < 0)
1544                 return -1;
1545
1546         if (value_fixup) {
1547                 DICT_ATTR *a;
1548                 value_fixup_t *this, *next;
1549
1550                 for (this = value_fixup; this != NULL; this = next) {
1551                         next = this->next;
1552
1553                         a = dict_attrbyname(this->attrstr);
1554                         if (!a) {
1555                                 librad_log(
1556                                         "dict_init: No ATTRIBUTE \"%s\" defined for VALUE \"%s\"",
1557                                         this->attrstr, this->dval->name);
1558                                 return -1; /* leak, but they should die... */
1559                         }
1560
1561                         this->dval->attr = a->attr;
1562
1563                         /*
1564                          *      Add the value into the dictionary.
1565                          */
1566                         if (!fr_hash_table_replace(values_byname,
1567                                                      this->dval)) {
1568                                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", this->dval->name, a->name);
1569                                 return -1;
1570                         }
1571
1572                         /*
1573                          *      Allow them to use the old name, but
1574                          *      prefer the new name when printing
1575                          *      values.
1576                          */
1577                         if (!fr_hash_table_finddata(values_byvalue, this->dval)) {
1578                                 fr_hash_table_replace(values_byvalue,
1579                                                         this->dval);
1580                         }
1581                         free(this);
1582
1583                         /*
1584                          *      Just so we don't lose track of things.
1585                          */
1586                         value_fixup = next;
1587                 }
1588         }
1589
1590         /*
1591          *      Walk over all of the hash tables to ensure they're
1592          *      initialized.  We do this because the threads may perform
1593          *      lookups, and we don't want multi-threaded re-ordering
1594          *      of the table entries.  That would be bad.
1595          */
1596         fr_hash_table_walk(vendors_byname, null_callback, NULL);
1597         fr_hash_table_walk(vendors_byvalue, null_callback, NULL);
1598
1599         fr_hash_table_walk(attributes_byname, null_callback, NULL);
1600         fr_hash_table_walk(attributes_byvalue, null_callback, NULL);
1601
1602         fr_hash_table_walk(values_byvalue, null_callback, NULL);
1603         fr_hash_table_walk(values_byname, null_callback, NULL);
1604
1605         return 0;
1606 }
1607
1608 /*
1609  *      Get an attribute by its numerical value.
1610  */
1611 DICT_ATTR *dict_attrbyvalue(int attr)
1612 {
1613         DICT_ATTR dattr;
1614
1615         if ((attr > 0) && (attr < 256)) return dict_base_attrs[attr];
1616
1617         dattr.attr = attr;
1618         dattr.vendor = VENDOR(attr) & 0x7fff;
1619
1620         return fr_hash_table_finddata(attributes_byvalue, &dattr);
1621 }
1622
1623 /*
1624  *      Get an attribute by its name.
1625  */
1626 DICT_ATTR *dict_attrbyname(const char *name)
1627 {
1628         DICT_ATTR dattr;
1629
1630         if (!name) return NULL;
1631
1632         strlcpy(dattr.name, name, sizeof(dattr.name));
1633
1634         return fr_hash_table_finddata(attributes_byname, &dattr);
1635 }
1636
1637 /*
1638  *      Associate a value with an attribute and return it.
1639  */
1640 DICT_VALUE *dict_valbyattr(int attr, int value)
1641 {
1642         DICT_VALUE dval, *dv;
1643
1644         /*
1645          *      First, look up aliases.
1646          */
1647         dval.attr = attr;
1648         dval.name[0] = '\0';
1649
1650         /*
1651          *      Look up the attribute alias target, and use
1652          *      the correct attribute number if found.
1653          */
1654         dv = fr_hash_table_finddata(values_byname, &dval);
1655         if (dv) dval.attr = dv->value;
1656
1657         dval.value = value;
1658
1659         return fr_hash_table_finddata(values_byvalue, &dval);
1660 }
1661
1662 /*
1663  *      Get a value by its name, keyed off of an attribute.
1664  */
1665 DICT_VALUE *dict_valbyname(int attr, const char *name)
1666 {
1667         DICT_VALUE *my_dv, *dv;
1668         uint32_t buffer[(sizeof(*my_dv) + DICT_VALUE_MAX_NAME_LEN + 3)/4];
1669
1670         if (!name) return NULL;
1671
1672         my_dv = (DICT_VALUE *) buffer;
1673         my_dv->attr = attr;
1674         my_dv->name[0] = '\0';
1675
1676         /*
1677          *      Look up the attribute alias target, and use
1678          *      the correct attribute number if found.
1679          */
1680         dv = fr_hash_table_finddata(values_byname, my_dv);
1681         if (dv) my_dv->attr = dv->value;
1682
1683         strlcpy(my_dv->name, name, DICT_VALUE_MAX_NAME_LEN);
1684
1685         return fr_hash_table_finddata(values_byname, my_dv);
1686 }
1687
1688 /*
1689  *      Get the vendor PEC based on the vendor name
1690  *
1691  *      This is efficient only for small numbers of vendors.
1692  */
1693 int dict_vendorbyname(const char *name)
1694 {
1695         DICT_VENDOR *dv;
1696         uint32_t buffer[(sizeof(*dv) + DICT_VENDOR_MAX_NAME_LEN + 3)/4];
1697
1698         if (!name) return 0;
1699
1700         dv = (DICT_VENDOR *) buffer;
1701         strlcpy(dv->name, name, DICT_VENDOR_MAX_NAME_LEN);
1702
1703         dv = fr_hash_table_finddata(vendors_byname, dv);
1704         if (!dv) return 0;
1705
1706         return dv->vendorpec;
1707 }
1708
1709 /*
1710  *      Return the vendor struct based on the PEC.
1711  */
1712 DICT_VENDOR *dict_vendorbyvalue(int vendorpec)
1713 {
1714         DICT_VENDOR dv;
1715
1716         dv.vendorpec = vendorpec;
1717
1718         return fr_hash_table_finddata(vendors_byvalue, &dv);
1719 }