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