09852a0fa3f677cc6a8af80ee61f8bed69bac68d
[freeradius.git] / src / lib / dict.c
1 /*
2  * dict.c       Routines to read the dictionary file.
3  *
4  * Version:     $Id$
5  *
6  */
7
8 static const char rcsid[] = "$Id$";
9
10 #include        "autoconf.h"
11
12 #include        <stdio.h>
13 #include        <stdlib.h>
14 #include        <sys/types.h>
15 #include        <pwd.h>
16 #include        <ctype.h>
17 #include        <string.h>
18
19 #if HAVE_MALLOC_H
20 #  include      <malloc.h>
21 #endif
22
23 #include        "libradius.h"
24 #include    "missing.h"
25
26 static DICT_ATTR        *dictionary_attributes;
27 static DICT_VALUE       *dictionary_values;
28 static DICT_VENDOR      *dictionary_vendors;
29
30 static int              vendorno = 1;
31 static char *dtypes[] = {
32         "string",
33         "integer",
34         "ipaddr",
35         "date",
36         "abinary",
37         "octets",
38         NULL,
39 };
40
41 #ifdef WITH_DICT_NOCASE
42 #define DICT_STRCMP strcasecmp
43 #else
44 #define DICT_STRCMP strcmp
45 #endif
46
47 /*
48  *      Free the dictionary_attributes and dictionary_values lists.
49  */
50 static void dict_free(void)
51 {
52         DICT_ATTR       *dattr, *anext;
53         DICT_VALUE      *dval, *vnext;
54         DICT_VENDOR     *dvend, *enext;
55
56         for (dattr = dictionary_attributes; dattr; dattr = anext) {
57                 anext = dattr->next;
58                 free(dattr);
59         }
60         for (dval = dictionary_values; dval; dval = vnext) {
61                 vnext = dval->next;
62                 free(dval);
63         }
64         for (dvend = dictionary_vendors; dvend; dvend = enext) {
65                 enext = dvend->next;
66                 free(dvend);
67         }
68         dictionary_attributes = NULL;
69         dictionary_values = NULL;
70         dictionary_vendors = NULL;
71         vendorno = 1;
72 }
73
74 /*
75  *      Add vendor to the list.
76  */
77 int dict_addvendor(char *name, int value)
78 {
79         DICT_VENDOR *vval;
80
81         if (strlen(name) > (sizeof(vval->vendorname) -1)) {
82                 librad_log("dict_addvendor: vendor name too long");
83                 return -1;
84         }
85
86         if ((vval =(DICT_VENDOR *)malloc(sizeof(DICT_VENDOR))) == NULL) {
87                 librad_log("dict_addvendor: out of memory");
88                 return -1;
89         }
90         strcpy(vval->vendorname, name);
91         vval->vendorpec  = value;
92         vval->vendorcode = vendorno++;
93
94         /* Insert at front. */
95         vval->next = dictionary_vendors;
96         dictionary_vendors = vval;
97
98         return 0;
99 }
100
101 int dict_addattr(char *name, int vendor, int type, int value)
102 {
103         DICT_ATTR       *attr;
104
105         if (strlen(name) > (sizeof(attr->name) -1)) {
106                 librad_log("dict_addattr: attribute name too long");
107                 return -1;
108         }
109
110         /*
111          *      Create a new attribute for the list
112          */
113         if ((attr = (DICT_ATTR *)malloc(sizeof(DICT_ATTR))) == NULL) {
114                 librad_log("dict_addattr: out of memory");
115                 return -1;
116         }
117         strcpy(attr->name, name);
118         attr->attr = value;
119         attr->type = type;
120         if (vendor) {
121                 attr->attr |= (vendor << 16);
122         }
123
124         /*
125          *      Add to the front of the list, so that
126          *      values at the end of the file override
127          *      those in the begin.
128          */
129         attr->next = dictionary_attributes;
130         dictionary_attributes = attr;
131         
132         return 0;
133 }
134
135 int dict_addvalue(char *namestr, char *attrstr, int value)
136 {
137         DICT_VALUE      *dval;
138
139         if (strlen(namestr) > (sizeof(dval->name) -1)) {
140                 librad_log("dict_addvalue: value name too long");
141                 return -1;
142         }
143
144         if (strlen(attrstr) > (sizeof(dval->attrname) -1)) {
145                 librad_log("dict_addvalue: attribute name too long");
146                 return -1;
147         }
148
149         if ((dval = (DICT_VALUE *)malloc(sizeof(DICT_VALUE))) == NULL) {
150                 librad_log("dict_addvalue: out of memory");
151                 return -1;
152         }
153         
154         strcpy(dval->name, namestr);
155         strcpy(dval->attrname, attrstr);
156         dval->attr = 0;         /* ??? */
157         dval->value = value;
158         
159         /* Insert at front. */
160         dval->next = dictionary_values;
161         dictionary_values = dval;
162                         
163         return 0;
164 }
165
166 /*
167  *      Initialize the dictionary.
168  */
169 static int my_dict_init(char *dir, char *fn)
170 {
171         FILE    *fp;
172         char    dirtmp[256];
173         char    buf[256];
174         char    namestr[256];
175         char    valstr[256];
176         char    attrstr[256];
177         char    typestr[256];
178         char    vendorstr[256];
179         char    *p;
180         char    *keyword;
181         char    *data;
182         int     line = 0;
183         int     value;
184         int     type;
185         int     vendor;
186         int     is_attrib;
187 #ifdef ATTRIB_NMC
188         int     vendor_usr_seen = 0;
189         int     is_nmc;
190 #endif
191
192         /*
193          *      First see if fn is relative to dir. If so, create
194          *      new filename. If not, remember the absolute dir.
195          */
196         if ((p = strrchr(fn, '/')) != NULL) {
197                 strcpy(dirtmp, fn);
198                 dirtmp[p - fn] = 0;
199                 dir = dirtmp;
200         } else if (dir && dir[0] && strcmp(dir, ".") != 0) {
201                 sprintf(dirtmp, "%s/%s", dir, fn);
202                 fn = dirtmp;
203         }
204
205         if ((fp = fopen(fn, "r")) == NULL) {
206                 librad_log("dict_init: Couldn't open dictionary: %s", fn);
207                 return -1;
208         }
209
210         while (fgets(buf, sizeof(buf), fp) != NULL) {
211
212                 line++;
213                 if (buf[0] == '#' || buf[0] == 0 || buf[0] == '\n')
214                         continue;
215
216                 keyword = strtok(buf, " \t\n");
217                 data    = strtok(NULL, "\n");
218                 if (data == NULL || data[0] == 0) {
219                         librad_log("dict_init: %s[%d]: invalid entry",
220                                 fn, line);
221                         return -1;
222                 }
223
224                 /*
225                  *      See if we need to import another dictionary.
226                  */
227                 if (strcasecmp(keyword, "$INCLUDE") == 0) {
228                         if (my_dict_init(dir, data) < 0)
229                                 return -1;
230                         continue;
231                 }
232
233                 /*
234                  *      Perhaps this is an attribute.
235                  */
236                 is_attrib = 0;
237                 if (strcmp(keyword, "ATTRIBUTE") == 0)
238                         is_attrib = 1;
239 #ifdef ATTRIB_NMC
240                 is_nmc = 0;
241                 if (strcmp(keyword, "ATTRIB_NMC") == 0)
242                         is_attrib = is_nmc = 1;
243 #endif
244                 if (is_attrib) {
245
246                         vendor = 0;
247                         vendorstr[0] = 0;
248                         if(sscanf(data, "%s%s%s%s", namestr,
249                                         valstr, typestr, vendorstr) < 3) {
250                                 librad_log(
251                                         "dict_init: %s[%d]: invalid attribute",
252                                         fn, line);
253                                 return -1;
254                         }
255
256 #ifdef ATTRIB_NMC
257                         /*
258                          *      Convert ATTRIB_NMC into our format.
259                          *      We might need to add USR to the list of
260                          *      vendors first.
261                          */
262                         if (is_nmc && vendorstr[0] == 0) {
263                                 if (!vendor_usr_seen) {
264                                         if (dict_addvendor("USR", VENDORPEC_USR) < 0)
265                                                 librad_log("dict_init: %s[%d]: %s", fn, line, librad_errstr);
266                                                 return -1;
267                                         vendor_usr_seen = 1;
268                                 }
269                                 strcpy(vendorstr, "USR");
270                         }
271 #endif
272                         /*
273                          *      Validate all entries
274                          */
275                         if (!isdigit(*valstr)) {
276                                 librad_log("dict_init: %s[%d]: invalid value",
277                                         fn, line);
278                                 return -1;
279                         }
280                         if (valstr[0] != '0')
281                                 value = atoi(valstr);
282                         else
283                                 sscanf(valstr, "%i", &value);
284
285                         /*
286                          *      find the type.
287                          */
288                         for (type = 0; dtypes[type]; type++) {
289                                 if (strcmp(typestr, dtypes[type]) == 0)
290                                         break;
291                         }
292                         if (dtypes[type] == NULL) {
293                                 librad_log("dict_init: %s[%d]: invalid type",
294                                         fn, line);
295                                 return -1;
296                         }
297
298                         vendor = dict_vendorname(vendorstr);
299                         if (vendorstr[0] && !vendor) {
300                                 librad_log(
301                                         "dict_init: %s[%d]: unknown vendor %s",
302                                         fn, line, vendorstr);
303                                 return -1;
304                         }
305
306                         if (dict_addattr(namestr, vendor, type, value) < 0) {
307                                 librad_log("dict_init: %s[%d]: %s",
308                                            fn, line, librad_errstr);
309                                 return -1;
310                         }
311
312                 }
313
314                 /*
315                  *      Process VALUE lines.
316                  */
317                 if (strcmp(keyword, "VALUE") == 0) {
318
319                         if (sscanf(data, "%s%s%s", attrstr,
320                                                 namestr, valstr) != 3) {
321                                 librad_log("dict_init: %s[%d]: invalid value",
322                                         fn, line);
323                                 return -1;
324                         }
325                         /*
326                          *      For Compatibility, skip "Server-Config"
327                          */
328                         if (strcmp(attrstr, "Server-Config") == 0)
329                                 continue;
330
331                         /*
332                          *      Validate all entries
333                          */
334                         if (!isdigit(*valstr)) {
335                                 librad_log("dict_init: %s[%d]: invalid value",
336                                         fn, line);
337                                 return -1;
338                         }
339                         if (valstr[0] != '0')
340                                 value = atoi(valstr);
341                         else
342                                 sscanf(valstr, "%i", &value);
343
344                         if (dict_addvalue(namestr, attrstr, value) < 0) {
345                                 librad_log("dict_init: %s[%d]: %s", 
346                                            fn, line, librad_errstr);
347                                 return -1;
348                         }
349                 }
350
351                 /*
352                  *      Process VENDOR lines.
353                  */
354                 if (strcmp(keyword, "VENDOR") == 0) {
355
356                         if (sscanf(data, "%s%s", attrstr, valstr) != 2) {
357                                 librad_log(
358                                 "dict_init: %s[%d] invalid vendor entry",
359                                         fn, line);
360                                 return -1;
361                         }
362
363                         /*
364                          *       Validate all entries
365                          */
366                         if (!isdigit(*valstr)) {
367                                 librad_log("dict_init: %s[%d]: invalid value",
368                                         fn, line);
369                                 return -1;
370                         }
371                         value = atoi(valstr);
372
373                         /* Create a new VENDOR entry for the list */
374                         if (dict_addvendor(attrstr, value) < 0) {
375                                 librad_log("dict_init: %s[%d]: %s",
376                                            fn, line, librad_errstr);
377                                 return -1;
378                         }
379 #ifdef ATTRIB_NMC
380                         if (value == VENDORPEC_USR)
381                                 vendor_usr_seen = 1;
382 #endif
383                 }
384         }
385         fclose(fp);
386         return 0;
387 }
388
389 /*
390  *      Initialize the directory, then fix the attr member of
391  *      all attributes.
392  */
393 int dict_init(char *dir, char *fn)
394 {
395         DICT_ATTR       *attr;
396         DICT_VALUE      *dval;
397
398         dict_free();
399
400         if (my_dict_init(dir, fn) < 0)
401                 return -1;
402
403         for (dval = dictionary_values; dval; dval = dval->next) {
404                 if (dval->attr != 0)
405                         continue;
406                 if ((attr = dict_attrbyname(dval->attrname)) == NULL) {
407                 librad_log("dict_init: VALUE %s for unknown ATTRIBUTE %s",
408                         dval->name, dval->attrname);
409                         return -1;
410                 }
411                 dval->attr = attr->attr;
412         }
413
414         return 0;
415 }
416
417 /*
418  *      Get an attribute by its numerical value.
419  */
420 DICT_ATTR * dict_attrbyvalue(int val)
421 {
422         DICT_ATTR       *a;
423
424         for (a = dictionary_attributes; a; a = a->next) {
425                 if (a->attr == val)
426                         return a;
427         }
428
429         return NULL;
430 }
431
432 /*
433  *      Get an attribute by its name.
434  */
435 DICT_ATTR * dict_attrbyname(char *name)
436 {
437         DICT_ATTR       *a;
438
439         for (a = dictionary_attributes; a; a = a->next) {
440                 if (DICT_STRCMP(a->name, name) == 0)
441                         return a;
442         }
443
444         return NULL;
445 }
446
447 /*
448  *      Associate a value with an attribute and return it.
449  */
450 DICT_VALUE * dict_valbyattr(int attr, int val)
451 {
452         DICT_VALUE      *v;
453
454         for (v = dictionary_values; v; v = v->next) {
455                 if (v->attr == attr && v->value == val)
456                         return v;
457         }
458
459         return NULL;
460 }
461
462 /*
463  *      Get a value by its name.
464  */
465 DICT_VALUE * dict_valbyname(char *name)
466 {
467         DICT_VALUE      *v;
468
469         for (v = dictionary_values; v; v = v->next) {
470                 if (DICT_STRCMP(v->name, name) == 0)
471                         return v;
472         }
473
474         return NULL;
475 }
476
477 /*
478  *      Get the PEC (Private Enterprise Code) of the vendor
479  *      based on its internal number.
480  */
481 int dict_vendorpec(int code)
482 {
483         DICT_VENDOR     *v;
484
485         for (v = dictionary_vendors; v; v = v->next)
486                 if (v->vendorcode == code)
487                         break;
488
489         return v ? v->vendorpec : 0;
490 }
491
492 /*
493  *      Get the internal code of the vendor based on its PEC.
494  */
495 int dict_vendorcode(int pec)
496 {
497         DICT_VENDOR     *v;
498
499         for (v = dictionary_vendors; v; v = v->next)
500                 if (v->vendorpec == pec)
501                         break;
502         return v ? v->vendorcode : 0;
503 }
504
505 /*
506  *      Get the vendor code based on the vendor name
507  */
508 int dict_vendorname(char *name)
509 {
510         DICT_VENDOR *v;
511
512         /*
513          *      Find the vendor, if any.
514          */
515         for (v = dictionary_vendors; v; v = v->next) {
516                 if (strcmp(name, v->vendorname) == 0) {
517                         return v->vendorcode;
518                 }
519         }
520
521         return 0;
522 }