2 * dict.c Routines to read the dictionary file.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20 * Copyright 2000 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
35 #include "libradius.h"
39 * There are very few vendors, and they're looked up only when we
40 * read the dictionaries. So it's OK to have a singly linked
43 static DICT_VENDOR *dictionary_vendors = NULL;
45 static rbtree_t *attributes_byname = NULL;
46 static rbtree_t *attributes_byvalue = NULL;
48 static rbtree_t *values_byvalue = NULL;
49 static rbtree_t *values_byname = NULL;
52 * So VALUEs in the dictionary can have forward references.
54 static rbtree_t *values_fixup = NULL;
56 static const LRAD_NAME_NUMBER type_table[] = {
57 { "string", PW_TYPE_STRING },
58 { "integer", PW_TYPE_INTEGER },
59 { "ipaddr", PW_TYPE_IPADDR },
60 { "date", PW_TYPE_DATE },
61 { "abinary", PW_TYPE_ABINARY },
62 { "octets", PW_TYPE_OCTETS },
63 { "ifid", PW_TYPE_IFID },
64 { "ipv6addr", PW_TYPE_IPV6ADDR },
65 { "ipv6prefix", PW_TYPE_IPV6PREFIX },
70 * Quick pointers to the base 0..255 attributes.
72 * These attributes are referenced a LOT, especially during
73 * decoding of the on-the-wire packets. It's useful to keep a
74 * cache of their dictionary entries, so looking them up is
75 * O(1), instead of O(log(N)). (N==number of dictionary entries...)
77 static DICT_ATTR *base_attributes[256];
80 * Free the dictionary_attributes and dictionary_values lists.
82 static void dict_free(void)
84 DICT_VENDOR *dvend, *enext;
86 memset(base_attributes, 0, sizeof(base_attributes));
88 for (dvend = dictionary_vendors; dvend; dvend = enext) {
93 dictionary_vendors = NULL;
96 * Free the tree of attributes by name and value.
98 rbtree_free(attributes_byname);
99 rbtree_free(attributes_byvalue);
100 attributes_byname = NULL;
101 attributes_byvalue = NULL;
103 rbtree_free(values_byname);
104 rbtree_free(values_byvalue);
105 values_byname = NULL;
106 values_byvalue = NULL;
110 * Add vendor to the list.
112 int dict_addvendor(const char *name, int value)
116 if (value >= (1 << 16)) {
117 librad_log("dict_addvendor: Cannot handle vendor ID larger than 65535");
121 if (strlen(name) > (sizeof(vval->name) -1)) {
122 librad_log("dict_addvendor: vendor name too long");
126 if ((vval =(DICT_VENDOR *)malloc(sizeof(DICT_VENDOR))) == NULL) {
127 librad_log("dict_addvendor: out of memory");
130 strcpy(vval->name, name);
131 vval->vendorpec = value;
133 /* Insert at front. */
134 vval->next = dictionary_vendors;
135 dictionary_vendors = vval;
141 * Add an attribute to the dictionary.
143 int dict_addattr(const char *name, int vendor, int type, int value,
146 static int max_attr = 0;
149 if (strlen(name) > (sizeof(attr->name) -1)) {
150 librad_log("dict_addattr: attribute name too long");
155 * If the value is '-1', that means use a pre-existing
156 * one (if it already exists). If one does NOT already exist,
157 * then create a new attribute, with a non-conflicting value,
161 attr = dict_attrbyname(name);
163 return 0; /* exists, don't add it again */
168 } else if (vendor == 0) {
172 if (value > max_attr) {
179 * Create a new attribute for the list
181 if ((attr = (DICT_ATTR *)malloc(sizeof(DICT_ATTR))) == NULL) {
182 librad_log("dict_addattr: out of memory");
185 strcpy(attr->name, name);
191 attr->attr |= (vendor << 16);
192 } else if ((attr->attr >= 0) && (attr->attr < 256)) {
194 * If it's an on-the-wire base attribute,
195 * then keep a quick reference to it, for speed.
197 base_attributes[attr->attr] = attr;
201 * Insert the attribute, only if it's not a duplicate.
203 if (rbtree_insert(attributes_byname, attr) == 0) {
207 * If the attribute has identical number, then
208 * ignore the duplicate.
210 a = rbtree_finddata(attributes_byname, attr);
211 if (a->attr == attr->attr) {
216 librad_log("dict_addattr: Duplicate attribute %s", name);
221 * Insert the SAME pointer (not free'd when this tree is
222 * deleted), into another tree.
224 * If the newly inserted entry is a duplicate of an existing
225 * entry, then the old entry is tossed, and the new one
226 * replaces it. This behaviour is configured in the
227 * rbtree_create() function.
229 * We want this behaviour because we want OLD names for
230 * the attributes to be read from the configuration
231 * files, but when we're printing them, (and looking up
232 * by value) we want to use the NEW name.
234 rbtree_insert(attributes_byvalue, attr);
240 * Add a value for an attribute to the dictionary.
242 int dict_addvalue(const char *namestr, char *attrstr, int value)
247 if (strlen(namestr) > (sizeof(dval->name) -1)) {
248 librad_log("dict_addvalue: value name too long");
252 if ((dval = (DICT_VALUE *)malloc(sizeof(DICT_VALUE))) == NULL) {
253 librad_log("dict_addvalue: out of memory");
257 strcpy(dval->name, namestr);
261 * Remember which attribute is associated with this
262 * value, if possible.
264 dattr = dict_attrbyname(attrstr);
266 dval->attr = dattr->attr;
268 dval->attr = (int) strdup(attrstr);
269 rbtree_insert(values_fixup, dval);
274 * Add the value into the dictionary.
276 if (rbtree_insert(values_byname, dval) == 0) {
277 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr);
280 rbtree_insert(values_byvalue, dval);
286 * Process the ATTRIBUTE command
288 static int process_attribute(const char* fn, const int line,
289 const int block_vendor, const char* data)
303 if(sscanf(data, "%s%s%s%s", namestr, valstr, typestr, optstr) < 3) {
304 librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line",
310 * Validate all entries
312 if (!isdigit((int) *valstr)) {
313 librad_log("dict_init: %s[%d]: invalid value", fn, line);
316 if (valstr[0] != '0')
317 value = atoi(valstr);
319 sscanf(valstr, "%i", &value);
322 * find the type of the attribute.
324 type = lrad_str2int(type_table, typestr, -1);
326 librad_log("dict_init: %s[%d]: invalid type \"%s\"",
334 if (optstr[0] == '#') optstr[0] = '\0';
337 * Only look up the vendor if the string
341 memset(&flags, 0, sizeof(flags));
342 s = strtok(optstr, ",");
344 if (strcmp(s, "has_tag") == 0 ||
345 strcmp(s, "has_tag=1") == 0) {
346 /* Boolean flag, means this is a
350 else if (strncmp(s, "len+=", 5) == 0 ||
351 strncmp(s, "len-=", 5) == 0) {
352 /* Length difference, to accomodate
353 braindead NASes & their vendors */
354 flags.len_disp = strtol(s + 5, &c, 0);
356 librad_log("dict_init: %s[%d] invalid option %s",
361 flags.len_disp = -flags.len_disp;
364 else if (strncmp(s, "encrypt=", 8) == 0) {
365 /* Encryption method, defaults to 0 (none).
366 Currently valid is just type 2,
367 Tunnel-Password style, which can only
368 be applied to strings. */
369 flags.encrypt = strtol(s + 8, &c, 0);
371 librad_log( "dict_init: %s[%d] invalid option %s",
377 /* Must be a vendor 'flag'... */
378 if (strncmp(s, "vendor=", 5) == 0) {
383 vendor = dict_vendorbyname(s);
385 librad_log( "dict_init: %s[%d]: unknown vendor %s",
389 if (block_vendor && optstr[0] &&
390 (block_vendor != vendor)) {
391 librad_log("dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block",
396 s = strtok(NULL, ",");
399 if (block_vendor) vendor = block_vendor;
401 if (dict_addattr(namestr, vendor, type, value, flags) < 0) {
402 librad_log("dict_init: %s[%d]: %s",
403 fn, line, librad_errstr);
412 * Process the VALUE command
414 static int process_value(const char* fn, const int line, const char* data)
421 if (sscanf(data, "%s%s%s", attrstr, namestr, valstr) != 3) {
422 librad_log("dict_init: %s[%d]: invalid VALUE line",
427 * For Compatibility, skip "Server-Config"
429 if (strcasecmp(attrstr, "Server-Config") == 0)
433 * Validate all entries
435 if (!isdigit((int) *valstr)) {
436 librad_log("dict_init: %s[%d]: invalid value",
440 if (valstr[0] != '0')
441 value = atoi(valstr);
443 sscanf(valstr, "%i", &value);
445 if (dict_addvalue(namestr, attrstr, value) < 0) {
446 librad_log("dict_init: %s[%d]: %s",
447 fn, line, librad_errstr);
456 * Process the VENDOR command
458 static int process_vendor(const char* fn, const int line, const char* data)
464 if (sscanf(data, "%s%s", attrstr, valstr) != 2) {
466 "dict_init: %s[%d] invalid VENDOR entry",
472 * Validate all entries
474 if (!isdigit((int) *valstr)) {
475 librad_log("dict_init: %s[%d]: invalid value",
479 value = atoi(valstr);
481 /* Create a new VENDOR entry for the list */
482 if (dict_addvendor(attrstr, value) < 0) {
483 librad_log("dict_init: %s[%d]: %s",
484 fn, line, librad_errstr);
493 * Initialize the dictionary.
495 static int my_dict_init(const char *dir, const char *fn,
496 const char *src_file, int src_line)
509 if (strlen(fn) >= sizeof(dirtmp) / 2 ||
510 strlen(dir) >= sizeof(dirtmp) / 2) {
511 librad_log("dict_init: filename name too long");
516 * First see if fn is relative to dir. If so, create
517 * new filename. If not, remember the absolute dir.
519 if ((p = strrchr(fn, '/')) != NULL) {
523 } else if (dir && dir[0] && strcmp(dir, ".") != 0) {
524 snprintf(dirtmp, sizeof(dirtmp), "%s/%s", dir, fn);
528 if ((fp = fopen(fn, "r")) == NULL) {
530 librad_log("dict_init: Couldn't open dictionary \"%s\": %s",
531 fn, strerror(errno));
533 librad_log("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s",
534 src_file, src_line, fn, strerror(errno));
541 while (fgets(buf, sizeof(buf), fp) != NULL) {
543 if (buf[0] == '#' || buf[0] == 0 ||
544 buf[0] == '\n' || buf[0] == '\r')
548 * Comment characters should NOT be appearing anywhere but
549 * as start of a comment;
551 p = strchr(buf, '#');
554 keyword = strtok(buf, " \t\r\n");
555 if (keyword == NULL) {
559 data = strtok(NULL, "\r\n");
560 if (data == NULL || data[0] == 0) {
561 librad_log("dict_init: %s[%d]: invalid entry for keyword %s",
568 * See if we need to import another dictionary.
570 if (strcasecmp(keyword, "$INCLUDE") == 0) {
571 p = strtok(data, " \t");
572 if (my_dict_init(dir, data, fn, line) < 0) {
580 * Perhaps this is an attribute.
582 if (strcasecmp(keyword, "ATTRIBUTE") == 0) {
583 if (process_attribute(fn, line, block_vendor, data) == -1) {
591 * Process VALUE lines.
593 if (strcasecmp(keyword, "VALUE") == 0) {
594 if (process_value(fn, line, data) == -1) {
602 * Process VENDOR lines.
604 if (strcasecmp(keyword, "VENDOR") == 0) {
605 if (process_vendor(fn, line, data) == -1) {
612 if (strcasecmp(keyword, "BEGIN-VENDOR") == 0) {
614 if (sscanf(data, "%s", optstr) != 1) {
616 "dict_init: %s[%d] invalid BEGIN-VENDOR entry",
622 vendor = dict_vendorbyname(optstr);
625 "dict_init: %s[%d]: unknown vendor %s",
630 block_vendor = vendor;
634 if (strcasecmp(keyword, "END-VENDOR") == 0) {
636 if (sscanf(data, "%s", optstr) != 1) {
638 "dict_init: %s[%d] invalid END-VENDOR entry",
644 vendor = dict_vendorbyname(optstr);
647 "dict_init: %s[%d]: unknown vendor %s",
653 if (vendor != block_vendor) {
655 "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR",
665 * Any other string: We don't recognize it.
668 "dict_init: %s[%d] invalid keyword \"%s\"",
678 * Callbacks for red-black trees.
680 static int attrname_cmp(const void *a, const void *b)
682 return strcasecmp(((const DICT_ATTR *)a)->name,
683 ((const DICT_ATTR *)b)->name);
687 * Return: < 0 if a < b,
690 static int attrvalue_cmp(const void *a, const void *b)
692 return (((const DICT_ATTR *)a)->attr -
693 ((const DICT_ATTR *)b)->attr);
697 * Compare values by name, keying off of the attribute number,
698 * and then the value name.
700 static int valuename_cmp(const void *a, const void *b)
703 rcode = (((const DICT_VALUE *)a)->attr -
704 ((const DICT_VALUE *)b)->attr);
705 if (rcode != 0) return rcode;
707 return strcasecmp(((const DICT_VALUE *)a)->name,
708 ((const DICT_VALUE *)b)->name);
712 * Compare values by value, keying off of the attribute number,
713 * and then the value number.
715 static int valuevalue_cmp(const void *a, const void *b)
718 rcode = (((const DICT_VALUE *)a)->attr -
719 ((const DICT_VALUE *)b)->attr);
720 if (rcode != 0) return rcode;
722 return (((const DICT_VALUE *)a)->value -
723 ((const DICT_VALUE *)b)->value);
727 * Compare values by name, keying off of the value number,
728 * and then the value number.
730 static int valuefixup_cmp(const void *a, const void *b)
733 rcode = strcasecmp((const char *) ((const DICT_VALUE *)a)->attr,
734 (const char *) ((const DICT_VALUE *)b)->attr);
735 if (rcode != 0) return rcode;
737 return (((const DICT_VALUE *)a)->value -
738 ((const DICT_VALUE *)b)->value);
741 static int values_fixup_func(void *data)
745 DICT_VALUE *dval = data;
747 a = dict_attrbyname((const char *) dval->attr);
749 librad_log("dict_addvalue: No attribute named %s for value %s", (const char *) dval->attr, dval->name);
753 free ((const char *) dval->attr);
754 dval->attr = a->attr;
757 * Add the value into the dictionary.
760 if (rbtree_insert(values_byname, dval) == 0) {
761 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", dval->name, a->name);
766 * Allow them to use the old name, but prefer the new name
767 * when printing values.
769 v = rbtree_find(values_byvalue, dval);
771 rbtree_insert(values_byvalue, dval);
778 * Initialize the directory, then fix the attr member of
781 int dict_init(const char *dir, const char *fn)
786 * Create the tree of attributes by name. There MAY NOT
787 * be multiple attributes of the same name.
789 * Each attribute is malloc'd, so the free function is free.
791 attributes_byname = rbtree_create(attrname_cmp, free, 0);
792 if (!attributes_byname) {
797 * Create the tree of attributes by value. There MAY
798 * be attributes of the same value. If there are, we
799 * pick the latest one.
801 attributes_byvalue = rbtree_create(attrvalue_cmp, NULL, 1);
802 if (!attributes_byvalue) {
806 values_byname = rbtree_create(valuename_cmp, NULL, 1);
807 if (!values_byname) {
811 values_byvalue = rbtree_create(valuevalue_cmp, NULL, 1);
812 if (!values_byvalue) {
817 * ONLY used in this function!
819 values_fixup = rbtree_create(valuefixup_cmp, NULL, 1);
824 if (my_dict_init(dir, fn, NULL, 0) < 0)
828 * Fix up the dictionary, based on values with an attribute
831 if (rbtree_walk(values_fixup, values_fixup_func, InOrder) != 0) {
835 rbtree_free(values_fixup);
842 * Get an attribute by its numerical value.
844 DICT_ATTR *dict_attrbyvalue(int val)
847 * If it's an on-the-wire base attribute, return
848 * the cached value for it.
850 if ((val >= 0) && (val < 256)) {
851 return base_attributes[val];
857 return rbtree_finddata(attributes_byvalue, &myattr);
860 return NULL; /* never reached, but useful */
864 * Get an attribute by its name.
866 DICT_ATTR *dict_attrbyname(const char *name)
870 strNcpy(myattr.name, name, sizeof(myattr.name));
872 return rbtree_finddata(attributes_byname, &myattr);
876 * Associate a value with an attribute and return it.
878 DICT_VALUE *dict_valbyattr(int attr, int val)
885 return rbtree_finddata(values_byvalue, &myval);
889 * Get a value by its name.
890 * If you pass an actual attr, it will try to match it.
891 * If you just want it to return on the first match,
892 * send it 0 as the attr. I hope this works the way it
895 DICT_VALUE *dict_valbyname(int attr, const char *name)
900 strNcpy(myval.name, name, sizeof(myval.name));
902 return rbtree_finddata(values_byname, &myval);
906 * Get the vendor PEC based on the vendor name
908 int dict_vendorbyname(const char *name)
913 * Find the vendor, if any.
915 for (v = dictionary_vendors; v; v = v->next) {
916 if (strcasecmp(name, v->name) == 0) {
925 * Return the vendor struct based on the PEC.
927 DICT_VENDOR *dict_vendorbyvalue(int vendor)
932 * Find the vendor, if any.
934 for (v = dictionary_vendors; v; v = v->next) {
935 if (vendor == v->vendorpec) {