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