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