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