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