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