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