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