Expand a comment.
[metze/wireshark/wip.git] / epan / radius_dict.l
1 %top {
2 /* Include this before everything else, for various large-file definitions */
3 #include "config.h"
4 }
5
6 /*
7  * We want a reentrant scanner.
8  */
9 %option reentrant
10
11 /*
12  * We don't use input, so don't generate code for it.
13  */
14 %option noinput
15
16 /*
17  * We don't use unput, so don't generate code for it.
18  */
19 %option nounput
20
21 /*
22  * We don't read interactively from the terminal.
23  */
24 %option never-interactive
25
26 /*
27  * We want to stop processing when we get to the end of the input.
28  */
29 %option noyywrap
30
31 /*
32  * The language we're scanning is case-insensitive.
33  */
34 %option caseless
35
36 /*
37  * The type for the state we keep for a scanner.
38  */
39 %option extra-type="Radius_scanner_state_t*"
40
41 /*
42  * We have to override the memory allocators so that we don't get
43  * "unused argument" warnings from the yyscanner argument (which
44  * we don't use, as we have a global memory allocator).
45  *
46  * We provide, as macros, our own versions of the routines generated by Flex,
47  * which just call malloc()/realloc()/free() (as the Flex versions do),
48  * discarding the extra argument.
49  */
50 %option noyyalloc
51 %option noyyrealloc
52 %option noyyfree
53
54 /*
55  * Prefix scanner routines with "Radius_" rather than "yy", so this scanner
56  * can coexist with other scanners.
57  */
58 %option prefix="Radius_"
59
60 %option outfile="radius_dict.c"
61
62 %{
63         /* radius_dict.l
64         *
65         * RADIUS dictionary parser
66         *
67         * Wireshark - Network traffic analyzer
68         * By Gerald Combs <gerald@wireshark.org>
69         * Copyright 1998 Gerald Combs
70         *
71         * This program is free software; you can redistribute it and/or
72         * modify it under the terms of the GNU General Public License
73         * as published by the Free Software Foundation; either version 2
74         * of the License, or (at your option) any later version.
75         *
76         * This program is distributed in the hope that it will be useful,
77         * but WITHOUT ANY WARRANTY; without even the implied warranty of
78         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
79         * GNU General Public License for more details.
80         *
81         * You should have received a copy of the GNU General Public License
82         * along with this program; if not, write to the Free Software
83         * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
84         */
85
86 #include <glib.h>
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <errno.h>
91 #include <epan/packet.h>
92 #include <epan/dissectors/packet-radius.h>
93 #include <wsutil/file_util.h>
94
95 /*
96  * See
97  *
98  *      http://freeradius.org/radiusd/man/dictionary.html
99  *
100  * for the format of RADIUS dictionary files.
101  *
102  * XXX - features not currently supported:
103  *
104  *      dotted number values for the oid field;
105  *
106  *      integer64, ipv4prefix, combo-prefix, bool, size, decimal,
107  *      timeval, struct, extended, long-extended, vsa, evs, vendor,
108  *      cidr, uint{8,16,32,64}, int{8,16,32,64} as attribute types
109  *      (some of these aren't documented);
110  *
111  *      octets[N], where N is an integer, as an attribute type
112  *      (not documented in the man page);
113  *
114  *      internal, array, concat, and virtual as attribute flags;
115  *
116  *      format= for BEGIN-VENDOR.
117  *
118  * We should, perhaps, adopt FreeRADIUS's dictionary-parsing code in
119  * src/lib/dict.c and use that, rather than writing our own parser.
120  * See bug 13176.
121  */
122 #define YY_USER_INIT BEGIN WS_OUT;
123
124 #ifdef _WIN32
125 /* disable Windows VC compiler warning "signed/unsigned mismatch" associated  */
126 /* with YY_INPUT code generated by flex versions such as 2.5.35.              */
127 #pragma warning (disable:4018)
128 #endif
129
130 #define ECHO
131 #define MAX_INCLUDE_DEPTH 10
132
133 typedef struct {
134         YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
135         int include_stack_ptr;
136
137         radius_dictionary_t* dict;
138         GHashTable* value_strings; /* GArray(value_string) by attribute name */
139
140         gchar* attr_name;
141         gchar* attr_id;
142         radius_attr_dissector_t* attr_type;
143         gchar* attr_vendor;
144         gchar* vendor_name;
145         guint32 vendor_id;
146         guint vendor_type_octets;
147         guint vendor_length_octets;
148         gboolean vendor_has_flags;
149         gchar* value_repr;
150         guint encrypted;
151         gboolean has_tag;
152         gchar* current_vendor;
153         gchar* current_attr;
154
155         gchar* directory;
156         gchar* fullpaths[MAX_INCLUDE_DEPTH];
157         int linenums[MAX_INCLUDE_DEPTH];
158
159         GString* error;
160 } Radius_scanner_state_t;
161
162 static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags);
163 static gboolean add_attribute(Radius_scanner_state_t* state, const gchar*,const  gchar*, radius_attr_dissector_t,const  gchar*, guint, gboolean, const gchar*);
164 static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const  gchar* code, radius_attr_dissector_t type, const gchar* attr);
165 static void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const  gchar* repr, guint32 value);
166
167 /*
168  * Sleazy hack to suppress compiler warnings in yy_fatal_error().
169  */
170 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
171
172 /*
173  * Macros for the allocators, to discard the extra argument.
174  */
175 #define Radius_alloc(size, yyscanner)           (void *)malloc(size)
176 #define Radius_realloc(ptr, size, yyscanner)    (void *)realloc((char *)(ptr), (size))
177 #define Radius_free(ptr, yyscanner)             free((char *)ptr)
178
179 %}
180
181 /* Note: FreeRadius allows VENDOR, ATTRIBUTE and VALUE names to contain any non-blank character.
182  *       Using a negated "blank character class" pattern below for those names fails for some reason
183  *       so for now the patterns for each name type include those characters found for the corresponding
184  *       name types in the FreeRadius dictionaries.
185  */
186
187 %START WS_OUT VENDOR VENDOR_W_NAME ATTR ATTR_W_NAME ATTR_W_ID ATTR_W_TYPE ATTR_W_VENDOR VALUE VALUE_W_ATTR VALUE_W_NAME INCLUDE JUNK BEGIN_VENDOR END_VENDOR VENDOR_W_ID VENDOR_W_FORMAT VENDOR_W_TYPE_OCTETS VENDOR_W_LENGTH_OCTETS VENDOR_W_CONTINUATION BEGIN_TLV END_TLV
188 %%
189 [:blank:]   ;
190 #[^\n]*         ;
191
192 <JUNK>.*\qn             ;
193
194 <WS_OUT>VENDOR { BEGIN VENDOR; }
195 <WS_OUT>ATTRIBUTE { BEGIN ATTR; }
196 <WS_OUT>VALUE { BEGIN VALUE; }
197 <WS_OUT>\$INCLUDE { BEGIN INCLUDE; }
198 <WS_OUT>BEGIN-VENDOR { BEGIN BEGIN_VENDOR; }
199 <WS_OUT>END-VENDOR { BEGIN END_VENDOR; }
200 <WS_OUT>BEGIN-TLV { BEGIN BEGIN_TLV; }
201 <WS_OUT>END-TLV { BEGIN END_TLV; }
202
203 <BEGIN_VENDOR>[0-9a-z_-]+ {
204     if (yyextra->current_vendor) {
205         g_free(yyextra->current_vendor);
206     }
207     yyextra->current_vendor = g_strdup(yytext);
208     BEGIN WS_OUT;
209 }
210 <END_VENDOR>[^\n]* {
211     if (yyextra->current_vendor) {
212         g_free(yyextra->current_vendor);
213         yyextra->current_vendor = NULL;
214     }
215     BEGIN WS_OUT;
216 }
217
218 <BEGIN_TLV>[0-9a-z_-]+ {
219     if (yyextra->current_attr) {
220         g_free(yyextra->current_attr);
221     }
222     yyextra->current_attr = g_strdup(yytext);
223     BEGIN WS_OUT;
224 }
225 <END_TLV>[^\n]* {
226     if (yyextra->current_attr) {
227         g_free(yyextra->current_attr);
228         yyextra->current_attr = NULL;
229     }
230     BEGIN WS_OUT;
231 }
232
233 <VENDOR>[0-9a-z_-]+   {
234     yyextra->vendor_name = g_strdup(yytext);
235     yyextra->vendor_type_octets = 1;
236     yyextra->vendor_length_octets = 1;
237     yyextra->vendor_has_flags = FALSE;
238     BEGIN VENDOR_W_NAME;
239 }
240 <VENDOR_W_NAME>[0-9]+   {
241     yyextra->vendor_id = (guint32) strtoul(yytext,NULL,10);
242     BEGIN VENDOR_W_ID;
243 }
244 <VENDOR_W_NAME>0x[0-9a-f]+   {
245     yyextra->vendor_id = (guint32) strtoul(yytext,NULL,16);
246     BEGIN VENDOR_W_ID;
247 }
248 <VENDOR_W_ID>format= {
249     BEGIN VENDOR_W_FORMAT;
250 }
251 <VENDOR_W_FORMAT>[124] {
252     yyextra->vendor_type_octets = (guint) strtoul(yytext,NULL,10);
253     BEGIN VENDOR_W_TYPE_OCTETS;
254 }
255 <VENDOR_W_TYPE_OCTETS>,[012] {
256     yyextra->vendor_length_octets = (guint) strtoul(yytext+1,NULL,10);
257     BEGIN VENDOR_W_LENGTH_OCTETS;
258 }
259 <VENDOR_W_LENGTH_OCTETS>,c {
260     yyextra->vendor_has_flags = TRUE;
261     BEGIN VENDOR_W_CONTINUATION;
262 }
263 <VENDOR_W_FORMAT>\n |
264 <VENDOR_W_TYPE_OCTETS>\n |
265 <VENDOR_W_LENGTH_OCTETS>\n |
266 <VENDOR_W_CONTINUATION>\n |
267 <VENDOR_W_ID>\n {
268     add_vendor(yyextra, yyextra->vendor_name, yyextra->vendor_id, yyextra->vendor_type_octets, yyextra->vendor_length_octets, yyextra->vendor_has_flags);
269     g_free(yyextra->vendor_name);
270     BEGIN WS_OUT;
271 }
272
273 <ATTR>[0-9a-z_/.-]+                     { yyextra->attr_name = g_strdup(yytext); yyextra->encrypted = 0; yyextra->has_tag = FALSE; BEGIN ATTR_W_NAME; }
274 <ATTR_W_NAME>[0-9]+                     { yyextra->attr_id = g_strdup(yytext);  BEGIN ATTR_W_ID;}
275 <ATTR_W_NAME>0x[0-9a-f]+                { yyextra->attr_id = g_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
276 <ATTR_W_ID>integer                      { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
277 <ATTR_W_ID>string                       { yyextra->attr_type = radius_string;  BEGIN ATTR_W_TYPE; }
278 <ATTR_W_ID>octets                       { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
279 <ATTR_W_ID>ipaddr                       { yyextra->attr_type = radius_ipaddr;  BEGIN ATTR_W_TYPE; }
280 <ATTR_W_ID>ipv6addr                     { yyextra->attr_type = radius_ipv6addr;  BEGIN ATTR_W_TYPE; }
281 <ATTR_W_ID>ipv6prefix                   { yyextra->attr_type = radius_ipv6prefix;  BEGIN ATTR_W_TYPE; }
282 <ATTR_W_ID>ipxnet                       { yyextra->attr_type = radius_ipxnet;  BEGIN ATTR_W_TYPE; }
283 <ATTR_W_ID>date                         { yyextra->attr_type = radius_date;  BEGIN ATTR_W_TYPE; }
284 <ATTR_W_ID>abinary                      { yyextra->attr_type = radius_abinary;  BEGIN ATTR_W_TYPE; }
285 <ATTR_W_ID>ether                        { yyextra->attr_type = radius_ether;  BEGIN ATTR_W_TYPE; }
286 <ATTR_W_ID>ifid                         { yyextra->attr_type = radius_ifid;  BEGIN ATTR_W_TYPE; }
287 <ATTR_W_ID>byte                         { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
288 <ATTR_W_ID>short                        { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
289 <ATTR_W_ID>signed                       { yyextra->attr_type = radius_signed;  BEGIN ATTR_W_TYPE; }
290 <ATTR_W_ID>combo-ip                     { yyextra->attr_type = radius_combo_ip;  BEGIN ATTR_W_TYPE; }
291 <ATTR_W_ID>tlv                          { yyextra->attr_type = radius_tlv;  BEGIN ATTR_W_TYPE; }
292 <ATTR_W_ID>[0-9a-z_-]+                  { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
293 <ATTR_W_TYPE>has_tag[,]?                { yyextra->has_tag = TRUE; }
294 <ATTR_W_TYPE>encrypt=[123][,]?          { yyextra->encrypted = (guint) strtoul(yytext+8,NULL,10); }
295 <ATTR_W_TYPE>[0-9a-z_-]+=([^\n]*)       ;
296 <ATTR_W_TYPE>[0-9a-z_-]+                {
297     gboolean attribute_ok;
298
299     yyextra->attr_vendor = g_strdup(yytext);
300     attribute_ok = add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->attr_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr);
301     g_free(yyextra->attr_id);
302     g_free(yyextra->attr_vendor);
303     g_free(yyextra->attr_name);
304     yyextra->attr_id = NULL;
305     yyextra->attr_vendor = NULL;
306     yyextra->attr_name = NULL;
307     if (attribute_ok)
308         BEGIN WS_OUT;
309     else
310         BEGIN JUNK;
311 }
312 <ATTR_W_TYPE>\n                                         {
313     add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->current_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr);
314     g_free(yyextra->attr_id);
315     g_free(yyextra->attr_name);
316     yyextra->linenums[yyextra->include_stack_ptr]++;
317     yyextra->has_tag = FALSE;
318     yyextra->encrypted=FALSE;
319     BEGIN WS_OUT;
320 }
321 <ATTR_W_VENDOR>\n                                       {
322     add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->attr_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr);
323     g_free(yyextra->attr_id);
324     g_free(yyextra->attr_vendor);
325     g_free(yyextra->attr_name);
326     yyextra->linenums[yyextra->include_stack_ptr]++;
327     BEGIN WS_OUT;
328 };
329
330 <VALUE>[0-9a-z_/-]+                             { yyextra->attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
331 <VALUE_W_ATTR>[^[:blank:]]+                     { yyextra->value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
332 <VALUE_W_NAME>[0-9]+                            { add_value(yyextra, yyextra->attr_name,yyextra->value_repr, (guint32) strtoul(yytext,NULL,10));  g_free(yyextra->attr_name); g_free(yyextra->value_repr); BEGIN WS_OUT;}
333 <VALUE_W_NAME>0x[0-9a-f]+                       { add_value(yyextra, yyextra->attr_name,yyextra->value_repr, (guint32) strtoul(yytext,NULL,16));  g_free(yyextra->attr_name); g_free(yyextra->value_repr); BEGIN WS_OUT;}
334
335 <INCLUDE>[^[:blank:]\n]+   {
336         if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
337                 g_string_append_printf(yyextra->error, "$INCLUDE files nested too deeply\n");
338                 yyterminate();
339         }
340
341         yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER;
342
343         yyextra->fullpaths[yyextra->include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
344             yyextra->directory,yytext);
345
346         yyin = ws_fopen( yyextra->fullpaths[yyextra->include_stack_ptr], "r" );
347
348         if (!yyin) {
349                 if (errno) {
350                         g_string_append_printf(yyextra->error,
351                                         "Could not open file: '%s', error: %s\n",
352                                         yyextra->fullpaths[yyextra->include_stack_ptr],
353                                         g_strerror(errno) );
354                 } else {
355                         g_string_append_printf(yyextra->error,
356                                         "Could not open file: '%s', no errno\n",
357                                         yyextra->fullpaths[yyextra->include_stack_ptr]);
358                 }
359                 g_free(yyextra->fullpaths[yyextra->include_stack_ptr]);
360                 yyextra->fullpaths[yyextra->include_stack_ptr] = NULL;
361                 yyextra->include_stack_ptr--;
362         } else {
363                 yyextra->linenums[yyextra->include_stack_ptr] = 1;
364                 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner);
365         }
366
367
368         BEGIN WS_OUT;
369 }
370
371 <<EOF>> {
372
373         fclose(yyin);
374         yyin = NULL;
375
376         if ( --yyextra->include_stack_ptr < 0 ) {
377                 yyterminate();
378         } else {
379                 g_free(yyextra->fullpaths[yyextra->include_stack_ptr+1]);
380                 yyextra->fullpaths[yyextra->include_stack_ptr+1] = NULL;
381
382                 Radius__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
383                 Radius__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner);
384         }
385
386         BEGIN WS_OUT;
387 }
388
389 \n      { yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; }
390
391
392 %%
393
394 static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags) {
395         radius_vendor_info_t* v;
396
397         v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_id, GUINT_TO_POINTER(id));
398
399         if (!v) {
400                 /*
401                  * New vendor.
402                  * Allocate a new entry and insert it into the by-ID and
403                  * by-name hash tables.
404                  */
405                 v = g_new(radius_vendor_info_t,1);
406                 v->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
407                 v->code = id;
408                 v->ett = -1;
409                 v->name = g_strdup(name);
410                 v->type_octets = type_octets;
411                 v->length_octets = length_octets;
412                 v->has_flags = has_flags;
413
414                 g_hash_table_insert(state->dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
415                 g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v);
416         } else {
417                 /*
418                  * This vendor is already in the table.
419                  *
420                  * Assume that the dictionary knows the 'ground truth' about
421                  * the type/length/has_flags information and thus allow the
422                  * dictionary to overwrite these values even for vendors that
423                  * have already been loaded.
424                  *
425                  * XXX - this could be due to the vendor being in multiple
426                  * dictionary files, rather than having been specially
427                  * entered by the RADIUS dissector, as a side effect of
428                  * specially entering an attribute; should we report vendors
429                  * that appear in different dictionaries with different
430                  * properties?
431                  */
432                 v->type_octets = type_octets;
433                 v->length_octets = length_octets;
434                 v->has_flags = has_flags;
435
436                 /*
437                  * Did the name change?
438                  */
439                 if (g_strcmp0(v->name, name) != 0) {
440                         /*
441                          * Yes.  Remove the entry from the by-name hash table
442                          * and re-insert it with the new name.
443                          */
444                         g_hash_table_remove(state->dict->vendors_by_name, (gpointer) v->name);
445                         g_free((gpointer) v->name);
446                         v->name = g_strdup(name);
447                         g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v);
448                 }
449         }
450 }
451
452 static gboolean add_attribute(Radius_scanner_state_t* state, const gchar* name, const  gchar* codestr, radius_attr_dissector_t type, const  gchar* vendor, guint encrypted_flag, gboolean tagged, const gchar* attr) {
453         radius_attr_info_t* a;
454         GHashTable* by_id;
455         guint32 code;
456
457         if (attr){
458                 return add_tlv(state, name, codestr, type, attr);
459         }
460
461
462         if (vendor) {
463                 radius_vendor_info_t* v;
464                 v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_name,vendor);
465
466                 if (! v) {
467                         g_string_append_printf(state->error, "Vendor: '%s', does not exist in %s:%i \n", vendor, state->fullpaths[state->include_stack_ptr], state->linenums[state->include_stack_ptr] );
468                         return FALSE;
469                 } else {
470                         by_id = v->attrs_by_id;
471                 }
472         } else {
473                 by_id = state->dict->attrs_by_id;
474         }
475
476         code= (guint32) strtoul(codestr, NULL, 10);
477
478         a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code));
479
480         if (!a) {
481                 /*
482                  * New attribute.
483                  * Allocate a new entry and insert it into the by-ID and
484                  * by-name hash tables.
485                  */
486                 a = g_new(radius_attr_info_t,1);
487                 a->code = code;
488                 a->name = g_strdup(name);
489                 a->dissector = NULL;
490                 a->encrypt = encrypted_flag;
491                 a->tagged =  tagged;
492                 a->type = type;
493                 a->vs = NULL;
494                 a->hf = -1;
495                 a->hf_alt = -1;
496                 a->hf_tag = -1;
497                 a->hf_len = -1;
498                 a->ett = -1;
499                 a->tlvs_by_id = NULL;
500                 g_hash_table_insert(by_id, GUINT_TO_POINTER(code),a);
501                 g_hash_table_insert(state->dict->attrs_by_name,(gpointer) (a->name),a);
502         } else {
503                 /*
504                  * This attribute is already in the table.
505                  *
506                  * Overwrite the encrypted flag, tagged property, and type;
507                  * the other properties don't get set until after we've
508                  * finished reading the dictionaries.
509                  *
510                  * XXX - this could be due to the attribute being in
511                  * multiple dictionary files, rather than having been
512                  * specially entered by the RADIUS dissector to give it
513                  * a special dissection routine; should we report attributes
514                  * that appear in different dictionaries with different
515                  * properties?
516                  */
517                 a->encrypt = encrypted_flag;
518                 a->tagged =  tagged;
519                 a->type = type;
520
521                 /*
522                  * Did the name change?
523                  */
524                 if (g_strcmp0(a->name, name) != 0) {
525                         /*
526                          * Yes.  Steal the entry from the by-name hash table
527                          * and re-insert it with the new name.  (Don't
528                          * remove it - that calls the free routine, which
529                          * frees up the entry.)
530                          */
531                         g_hash_table_steal(state->dict->attrs_by_name, (gpointer) (a->name));
532                         g_free((gpointer) a->name);
533                         a->name = g_strdup(name);
534                         g_hash_table_insert(state->dict->attrs_by_name, (gpointer) (a->name),a);
535                 }
536         }
537         return TRUE;
538 }
539
540 static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const  gchar* codestr, radius_attr_dissector_t type, const gchar* attr) {
541         radius_attr_info_t* a;
542         radius_attr_info_t* s;
543         guint32 code;
544
545         a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->attrs_by_name, attr);
546
547         if (! a) {
548                 g_string_append_printf(state->error, "Attr: '%s', does not exist in %s:%i \n", attr, state->fullpaths[state->include_stack_ptr], state->linenums[state->include_stack_ptr]);
549                 return FALSE;
550         }
551
552         if (type == radius_tlv) {
553                 g_string_append_printf(state->error, "sub-TLV: '%s', sub-TLV's type is specified as tlv in %s:%i \n", name, state->fullpaths[state->include_stack_ptr], state->linenums[state->include_stack_ptr]);
554                 return FALSE;
555         }
556
557
558         if (! a->tlvs_by_id) {
559                 a->tlvs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
560         }
561
562         code = (guint32) strtoul(codestr, NULL, 10);
563
564         s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code));
565
566         if (!s) {
567                 /*
568                  * This TLV doesn't yet exist in this attribute's TLVs-by-ID
569                  * hash table.  Add it.
570                  */
571                 s = g_new(radius_attr_info_t,1);
572                 s->name = g_strdup(name);
573                 s->dissector = NULL;
574                 s->code = code;
575                 s->type = type;
576                 s->encrypt = FALSE;
577                 s->tagged = FALSE;
578                 s->dissector = NULL;
579                 s->vs = NULL;
580                 s->hf = -1;
581                 s->hf_alt = -1;
582                 s->hf_tag = -1;
583                 s->hf_len = -1;
584                 s->ett = -1;
585                 s->tlvs_by_id = NULL;
586
587                 g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code),s);
588                 g_hash_table_insert(state->dict->tlvs_by_name,(gpointer) (s->name),s);
589         }
590
591         /*
592          * If it *does* exist, leave it alone; there shouldn't be duplicate
593          * entries by name in the dictionaries (even if there might be
594          * multiple entries for a given attribute in the dictionaries, each
595          * one adding some TLV values), and we don't directly add entries
596          * for TLVs in the RADIUS dissector.
597          *
598          * XXX - report the duplicate entries?
599          */
600         return TRUE;
601 }
602
603 void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const gchar* repr, guint32 value) {
604         value_string v;
605         GArray* a = (GArray*)g_hash_table_lookup(state->value_strings,attrib_name);
606
607         if (! a) {
608                 /* Ensure that the array is zero terminated. */
609                 a = g_array_new(TRUE, TRUE, sizeof(value_string));
610                 g_hash_table_insert(state->value_strings, g_strdup(attrib_name), a);
611         }
612
613         v.value = value;
614         v.strptr = g_strdup(repr);
615
616         g_array_append_val(a,v);
617 }
618
619 static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p) {
620         radius_attr_info_t* s = (radius_attr_info_t*)v;
621         Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
622         gpointer key;
623
624         union {
625                 GArray* a;
626                 gpointer p;
627         } vs;
628
629         if (g_hash_table_lookup_extended(state->value_strings, s->name, &key, &vs.p)) {
630                 s->vs = (value_string*)(void *)vs.a->data;
631                 g_hash_table_steal(state->value_strings, key);
632                 g_array_free(vs.a, FALSE);
633                 g_free(key);
634         }
635 }
636
637 static void setup_attrs(gpointer k _U_, gpointer v, gpointer p) {
638         radius_attr_info_t* a = (radius_attr_info_t*)v;
639         Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
640         gpointer key;
641
642         union {
643                 GArray* a;
644                 gpointer p;
645         } vs;
646
647         if (g_hash_table_lookup_extended(state->value_strings, a->name, &key, &vs.p) ) {
648                 a->vs = (value_string*)(void *)vs.a->data;
649                 g_hash_table_steal(state->value_strings, key);
650                 g_array_free(vs.a, FALSE);
651                 g_free(key);
652         }
653
654         if (a->tlvs_by_id) {
655                 g_hash_table_foreach(a->tlvs_by_id, setup_tlvs, p);
656         }
657 }
658
659 static void setup_vendors(gpointer k _U_, gpointer v, gpointer p) {
660         radius_vendor_info_t* vnd = (radius_vendor_info_t*)v;
661
662         g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p);
663 }
664
665 static void destroy_value_strings(gpointer v) {
666         value_string* vs = (value_string*)(void *)(((GArray*)v)->data);
667
668         for (;vs->strptr;vs++) {
669                 g_free((void*)vs->strptr);
670         }
671
672         g_array_free((GArray*)v,TRUE);
673 }
674
675 gboolean radius_load_dictionary (radius_dictionary_t* d, gchar* dir, const gchar* filename, gchar** err_str) {
676         FILE *in;
677         yyscan_t scanner;
678         Radius_scanner_state_t state;
679         int i;
680
681         state.include_stack_ptr = 0;
682
683         state.dict = d;
684         state.value_strings = NULL;
685
686         state.attr_name = NULL;
687         state.attr_id = NULL;
688         state.attr_type = NULL;
689         state.attr_vendor = NULL;
690         state.vendor_name = NULL;
691         state.vendor_id = 0;
692         state.vendor_type_octets = 1;
693         state.vendor_length_octets = 1;
694         state.vendor_has_flags = FALSE;
695         state.value_repr = NULL;
696         state.encrypted = 0;
697         state.has_tag = FALSE;
698         state.current_vendor = NULL;
699         state.current_attr = NULL;
700
701         state.directory = dir;
702
703         state.fullpaths[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
704             state.directory,filename);
705         state.linenums[0] = 1;
706         for (i = 1; i < MAX_INCLUDE_DEPTH; i++) {
707                 state.fullpaths[i] = NULL;
708                 state.linenums[i] = 1;
709         }
710
711         state.error = g_string_new("");
712
713         in = ws_fopen(state.fullpaths[0],"r");
714
715         if (!in) {
716                 g_string_append_printf(state.error, "Could not open file: '%s', error: %s\n", state.fullpaths[0], g_strerror(errno));
717                 g_free(state.fullpaths[0]);
718                 *err_str = g_string_free(state.error,FALSE);
719                 return FALSE;
720         }
721
722         state.value_strings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_value_strings);
723
724         if (Radius_lex_init(&scanner) != 0) {
725                 g_string_append_printf(state.error, "Can't initialize scanner: %s",
726                     strerror(errno));
727                 fclose(in);
728                 g_free(state.fullpaths[0]);
729                 *err_str = g_string_free(state.error,FALSE);
730                 return FALSE;
731         }
732
733         Radius_set_in(in, scanner);
734
735         /* Associate the state with the scanner */
736         Radius_set_extra(&state, scanner);
737
738         Radius_lex(scanner);
739
740         Radius_lex_destroy(scanner);
741         /*
742          * XXX - can the lexical analyzer terminate without closing
743          * all open input files?
744          */
745
746         for (i = 0; i < MAX_INCLUDE_DEPTH; i++) {
747                 if (state.fullpaths[i])
748                         g_free(state.fullpaths[i]);
749         }
750
751         g_hash_table_foreach(state.dict->attrs_by_id,setup_attrs,&state);
752         g_hash_table_foreach(state.dict->vendors_by_id,setup_vendors,&state);
753         g_hash_table_destroy(state.value_strings);
754
755         if (state.error->len > 0) {
756                 *err_str = g_string_free(state.error,FALSE);
757                 return FALSE;
758         } else {
759                 *err_str = NULL;
760                 g_string_free(state.error,TRUE);
761                 return TRUE;
762         }
763 }
764
765 /*
766  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
767  *
768  * Local variables:
769  * c-basic-offset: 8
770  * tab-width: 8
771  * indent-tabs-mode: t
772  * End:
773  *
774  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
775  * :indentSize=8:tabSize=8:noTabs=false:
776  */