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