Free g_array_free-related memory leaks
[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  * Disable diagnostics in the code generated by Flex.
97  */
98 DIAG_OFF_FLEX
99
100 /*
101  * See
102  *
103  *      http://freeradius.org/radiusd/man/dictionary.html
104  *
105  * for the format of RADIUS dictionary files.
106  *
107  * XXX - features not currently supported:
108  *
109  *      integer64, ipv4prefix, combo-prefix, bool, size, decimal,
110  *      timeval, struct, extended, long-extended, vsa, evs, vendor,
111  *      cidr, uint{8,16,32,64}, int{8,16,32,64} as attribute types
112  *      (some of these aren't documented);
113  *
114  *      octets[N], where N is an integer, as an attribute type
115  *      (not documented in the man page);
116  *
117  *      internal, array, concat, and virtual as attribute flags (not
118  *      documented in the man page);
119  *
120  * We should, perhaps, adopt FreeRADIUS's dictionary-parsing code in
121  * src/lib/dict.c and use that, rather than writing our own parser.
122  * See bug 13176.
123  */
124 #define YY_USER_INIT BEGIN WS_OUT;
125
126 #define ECHO
127 #define MAX_INCLUDE_DEPTH 10
128
129 typedef struct {
130         YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
131         int include_stack_ptr;
132
133         radius_dictionary_t* dict;
134         GHashTable* value_strings; /* GArray(value_string) by attribute name */
135
136         gchar* attr_name;
137         gchar* attr_id;
138         radius_attr_dissector_t* attr_type;
139         gchar* attr_vendor;
140         gchar* vendor_name;
141         guint32 vendor_id;
142         guint vendor_type_octets;
143         guint vendor_length_octets;
144         gboolean vendor_has_flags;
145         gchar* value_repr;
146         guint encrypted;
147         gboolean has_tag;
148         gchar* current_vendor;
149         guint current_vendor_evs_type;
150         gchar* current_attr;
151
152         gchar* directory;
153         gchar* fullpaths[MAX_INCLUDE_DEPTH];
154         int linenums[MAX_INCLUDE_DEPTH];
155
156         GString* error;
157 } Radius_scanner_state_t;
158
159 static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags);
160 static gboolean add_attribute(Radius_scanner_state_t* state, const gchar*,const  gchar*, radius_attr_dissector_t,const  gchar*, guint, gboolean, const gchar*);
161 static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const  gchar* code, radius_attr_dissector_t type, const gchar* attr);
162 static void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const  gchar* repr, guint32 value);
163
164 /*
165  * Sleazy hack to suppress compiler warnings in yy_fatal_error().
166  */
167 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
168
169 /*
170  * Macros for the allocators, to discard the extra argument.
171  */
172 #define Radius_alloc(size, yyscanner)           (void *)malloc(size)
173 #define Radius_realloc(ptr, size, yyscanner)    (void *)realloc((char *)(ptr), (size))
174 #define Radius_free(ptr, yyscanner)             free((char *)ptr)
175
176 %}
177
178 /* Note: FreeRadius allows VENDOR, ATTRIBUTE and VALUE names to contain any non-blank character.
179  *       Using a negated "blank character class" pattern below for those names fails for some reason
180  *       so for now the patterns for each name type include those characters found for the corresponding
181  *       name types in the FreeRadius dictionaries.
182  */
183
184 %START WS_OUT VENDOR VENDOR_W_NAME ATTR ATTR_W_NAME ATTR_W_ID ATTR_W_TYPE VALUE VALUE_W_ATTR VALUE_W_NAME INCLUDE JUNK BEGIN_VENDOR BEGIN_VENDOR_FORMAT END_VENDOR VENDOR_W_ID VENDOR_W_FORMAT VENDOR_W_TYPE_OCTETS VENDOR_W_LENGTH_OCTETS VENDOR_W_CONTINUATION BEGIN_TLV END_TLV
185 %%
186 [:blank:]   ;
187 #[^\n]*         ;
188
189 <JUNK>.*\qn             ;
190
191 <WS_OUT>VENDOR { BEGIN VENDOR; }
192 <WS_OUT>ATTRIBUTE { BEGIN ATTR; }
193 <WS_OUT>VALUE { BEGIN VALUE; }
194 <WS_OUT>\$INCLUDE { BEGIN INCLUDE; }
195 <WS_OUT>BEGIN-VENDOR { BEGIN BEGIN_VENDOR; }
196 <WS_OUT>END-VENDOR { BEGIN END_VENDOR; }
197 <WS_OUT>BEGIN-TLV { BEGIN BEGIN_TLV; }
198 <WS_OUT>END-TLV { BEGIN END_TLV; }
199
200 <BEGIN_VENDOR>[0-9a-z_-]+ {
201     if (yyextra->current_vendor) {
202         g_free(yyextra->current_vendor);
203     }
204     yyextra->current_vendor = g_strdup(yytext);
205     BEGIN BEGIN_VENDOR_FORMAT;
206 }
207 <BEGIN_VENDOR_FORMAT>format=Extended-Vendor-Specific-[123456] {
208     if (strcmp(yytext, "format=Extended-Vendor-Specific-1") == 0) {
209         yyextra->current_vendor_evs_type = 241;
210     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-2") == 0) {
211         yyextra->current_vendor_evs_type = 242;
212     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-3") == 0) {
213         yyextra->current_vendor_evs_type = 243;
214     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-4") == 0) {
215         yyextra->current_vendor_evs_type = 244;
216     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-5") == 0) {
217         yyextra->current_vendor_evs_type = 245;
218     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-6") == 0) {
219         yyextra->current_vendor_evs_type = 246;
220     }
221     BEGIN WS_OUT;
222 }
223 <BEGIN_VENDOR_FORMAT>\n {BEGIN WS_OUT;}
224
225 <END_VENDOR>[^\n]* {
226     if (yyextra->current_vendor) {
227         g_free(yyextra->current_vendor);
228         yyextra->current_vendor = NULL;
229     }
230     yyextra->current_vendor_evs_type = 0;
231     BEGIN WS_OUT;
232 }
233
234 <BEGIN_TLV>[0-9a-z_-]+ {
235     if (yyextra->current_attr) {
236         g_free(yyextra->current_attr);
237     }
238     yyextra->current_attr = g_strdup(yytext);
239     BEGIN WS_OUT;
240 }
241 <END_TLV>[^\n]* {
242     if (yyextra->current_attr) {
243         g_free(yyextra->current_attr);
244         yyextra->current_attr = NULL;
245     }
246     BEGIN WS_OUT;
247 }
248
249 <VENDOR>[0-9a-z_-]+   {
250     yyextra->vendor_name = g_strdup(yytext);
251     yyextra->vendor_type_octets = 1;
252     yyextra->vendor_length_octets = 1;
253     yyextra->vendor_has_flags = FALSE;
254     BEGIN VENDOR_W_NAME;
255 }
256 <VENDOR_W_NAME>[0-9]+   {
257     yyextra->vendor_id = (guint32) strtoul(yytext,NULL,10);
258     BEGIN VENDOR_W_ID;
259 }
260 <VENDOR_W_NAME>0x[0-9a-f]+   {
261     yyextra->vendor_id = (guint32) strtoul(yytext,NULL,16);
262     BEGIN VENDOR_W_ID;
263 }
264 <VENDOR_W_ID>format= {
265     BEGIN VENDOR_W_FORMAT;
266 }
267 <VENDOR_W_FORMAT>[124] {
268     yyextra->vendor_type_octets = (guint) strtoul(yytext,NULL,10);
269     BEGIN VENDOR_W_TYPE_OCTETS;
270 }
271 <VENDOR_W_TYPE_OCTETS>,[012] {
272     yyextra->vendor_length_octets = (guint) strtoul(yytext+1,NULL,10);
273     BEGIN VENDOR_W_LENGTH_OCTETS;
274 }
275 <VENDOR_W_LENGTH_OCTETS>,c {
276     yyextra->vendor_has_flags = TRUE;
277     BEGIN VENDOR_W_CONTINUATION;
278 }
279 <VENDOR_W_FORMAT>\n |
280 <VENDOR_W_TYPE_OCTETS>\n |
281 <VENDOR_W_LENGTH_OCTETS>\n |
282 <VENDOR_W_CONTINUATION>\n |
283 <VENDOR_W_ID>\n {
284     add_vendor(yyextra, yyextra->vendor_name, yyextra->vendor_id, yyextra->vendor_type_octets, yyextra->vendor_length_octets, yyextra->vendor_has_flags);
285     g_free(yyextra->vendor_name);
286     BEGIN WS_OUT;
287 }
288
289 <ATTR>[0-9a-z_/.-]+                     { yyextra->attr_name = g_strdup(yytext); yyextra->encrypted = 0; yyextra->has_tag = FALSE; BEGIN ATTR_W_NAME; }
290 <ATTR_W_NAME>[0-9.]+                    { yyextra->attr_id = g_strdup(yytext);  BEGIN ATTR_W_ID;}
291 <ATTR_W_NAME>0x[0-9a-f]+                { yyextra->attr_id = g_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
292 <ATTR_W_ID>integer                      { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
293 <ATTR_W_ID>string                       { yyextra->attr_type = radius_string;  BEGIN ATTR_W_TYPE; }
294 <ATTR_W_ID>octets                       { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
295 <ATTR_W_ID>ipaddr                       { yyextra->attr_type = radius_ipaddr;  BEGIN ATTR_W_TYPE; }
296 <ATTR_W_ID>ipv6addr                     { yyextra->attr_type = radius_ipv6addr;  BEGIN ATTR_W_TYPE; }
297 <ATTR_W_ID>ipv6prefix                   { yyextra->attr_type = radius_ipv6prefix;  BEGIN ATTR_W_TYPE; }
298 <ATTR_W_ID>ipxnet                       { yyextra->attr_type = radius_ipxnet;  BEGIN ATTR_W_TYPE; }
299 <ATTR_W_ID>date                         { yyextra->attr_type = radius_date;  BEGIN ATTR_W_TYPE; }
300 <ATTR_W_ID>abinary                      { yyextra->attr_type = radius_abinary;  BEGIN ATTR_W_TYPE; }
301 <ATTR_W_ID>ether                        { yyextra->attr_type = radius_ether;  BEGIN ATTR_W_TYPE; }
302 <ATTR_W_ID>ifid                         { yyextra->attr_type = radius_ifid;  BEGIN ATTR_W_TYPE; }
303 <ATTR_W_ID>byte                         { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
304 <ATTR_W_ID>short                        { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
305 <ATTR_W_ID>signed                       { yyextra->attr_type = radius_signed;  BEGIN ATTR_W_TYPE; }
306 <ATTR_W_ID>combo-ip                     { yyextra->attr_type = radius_combo_ip;  BEGIN ATTR_W_TYPE; }
307 <ATTR_W_ID>tlv                          { yyextra->attr_type = radius_tlv;  BEGIN ATTR_W_TYPE; }
308 <ATTR_W_ID>[0-9a-z_-]+                  { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
309 <ATTR_W_TYPE>has_tag[,]?                { yyextra->has_tag = TRUE; }
310 <ATTR_W_TYPE>encrypt=[123][,]?          { yyextra->encrypted = (guint) strtoul(yytext+8,NULL,10); }
311 <ATTR_W_TYPE>[0-9a-z_-]+=([^\n]*)       ;
312 <ATTR_W_TYPE>[0-9a-z_-]+                {
313     /*
314      * Support for "ATTRIBUTE name oid type vendor", where the token
315      * following the type matches neither has_tag nor encrypt={1,2,3},
316      * but is a sequence of digits, lower-case letters, underscores,
317      * and hyphens.
318      *
319      * We mark this as a vendor-specific attribute (VSA), with the token
320      * following the type being the vendor name; this notation is deprecated
321      * in favor of BEGIN-VENDOR/END-VENDOR blocks.
322      */
323     gboolean attribute_ok;
324
325     yyextra->attr_vendor = g_strdup(yytext);
326     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);
327     g_free(yyextra->attr_id);
328     g_free(yyextra->attr_vendor);
329     g_free(yyextra->attr_name);
330     yyextra->attr_id = NULL;
331     yyextra->attr_vendor = NULL;
332     yyextra->attr_name = NULL;
333     if (attribute_ok)
334         BEGIN WS_OUT;
335     else
336         BEGIN JUNK;
337 }
338 <ATTR_W_TYPE>\n                                         {
339     add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->current_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr);
340     g_free(yyextra->attr_id);
341     g_free(yyextra->attr_name);
342     yyextra->linenums[yyextra->include_stack_ptr]++;
343     yyextra->has_tag = FALSE;
344     yyextra->encrypted=FALSE;
345     BEGIN WS_OUT;
346 }
347
348 <VALUE>[0-9a-z_/-]+                             { yyextra->attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
349 <VALUE_W_ATTR>[^[:blank:]]+                     { yyextra->value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
350 <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;}
351 <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;}
352
353 <INCLUDE>[^[:blank:]\n]+   {
354         if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
355                 g_string_append_printf(yyextra->error, "$INCLUDE files nested too deeply\n");
356                 yyterminate();
357         }
358
359         yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER;
360
361         yyextra->fullpaths[yyextra->include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
362             yyextra->directory,yytext);
363
364         yyin = ws_fopen( yyextra->fullpaths[yyextra->include_stack_ptr], "r" );
365
366         if (!yyin) {
367                 if (errno) {
368                         g_string_append_printf(yyextra->error,
369                                         "Could not open file: '%s', error: %s\n",
370                                         yyextra->fullpaths[yyextra->include_stack_ptr],
371                                         g_strerror(errno) );
372                 } else {
373                         g_string_append_printf(yyextra->error,
374                                         "Could not open file: '%s', no errno\n",
375                                         yyextra->fullpaths[yyextra->include_stack_ptr]);
376                 }
377                 g_free(yyextra->fullpaths[yyextra->include_stack_ptr]);
378                 yyextra->fullpaths[yyextra->include_stack_ptr] = NULL;
379                 yyextra->include_stack_ptr--;
380         } else {
381                 yyextra->linenums[yyextra->include_stack_ptr] = 1;
382                 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner);
383         }
384
385
386         BEGIN WS_OUT;
387 }
388
389 <<EOF>> {
390
391         fclose(yyin);
392         yyin = NULL;
393
394         if ( --yyextra->include_stack_ptr < 0 ) {
395                 yyterminate();
396         } else {
397                 g_free(yyextra->fullpaths[yyextra->include_stack_ptr+1]);
398                 yyextra->fullpaths[yyextra->include_stack_ptr+1] = NULL;
399
400                 Radius__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
401                 Radius__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner);
402         }
403
404         BEGIN WS_OUT;
405 }
406
407 \n      { yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; }
408
409
410 %%
411
412 /*
413  * Turn diagnostics back on, so we check the code that we've written.
414  */
415 DIAG_ON_FLEX
416
417 static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags) {
418         radius_vendor_info_t* v;
419
420         v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_id, GUINT_TO_POINTER(id));
421
422         if (!v) {
423                 /*
424                  * New vendor.
425                  * Allocate a new entry and insert it into the by-ID and
426                  * by-name hash tables.
427                  */
428                 v = g_new(radius_vendor_info_t,1);
429                 v->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
430                 v->code = id;
431                 v->ett = -1;
432                 v->name = g_strdup(name);
433                 v->type_octets = type_octets;
434                 v->length_octets = length_octets;
435                 v->has_flags = has_flags;
436
437                 g_hash_table_insert(state->dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
438                 g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v);
439         } else {
440                 /*
441                  * This vendor is already in the table.
442                  *
443                  * Assume that the dictionary knows the 'ground truth' about
444                  * the type/length/has_flags information and thus allow the
445                  * dictionary to overwrite these values even for vendors that
446                  * have already been loaded.
447                  *
448                  * XXX - this could be due to the vendor being in multiple
449                  * dictionary files, rather than having been specially
450                  * entered by the RADIUS dissector, as a side effect of
451                  * specially entering an attribute; should we report vendors
452                  * that appear in different dictionaries with different
453                  * properties?
454                  */
455                 v->type_octets = type_octets;
456                 v->length_octets = length_octets;
457                 v->has_flags = has_flags;
458
459                 /*
460                  * Did the name change?
461                  */
462                 if (g_strcmp0(v->name, name) != 0) {
463                         /*
464                          * Yes.  Remove the entry from the by-name hash table
465                          * and re-insert it with the new name.
466                          */
467                         g_hash_table_remove(state->dict->vendors_by_name, (gpointer) v->name);
468                         g_free((gpointer) v->name);
469                         v->name = g_strdup(name);
470                         g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v);
471                 }
472         }
473 }
474
475 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) {
476         radius_attr_info_t* a;
477         GHashTable* by_id;
478         radius_attr_type_t code;
479         guint8 code0 = 0, code1 = 0;
480         gchar *dot, *buf = NULL;
481
482         if (attr){
483                 return add_tlv(state, name, codestr, type, attr);
484         }
485
486         buf = g_strdup(codestr);
487         dot = strchr(codestr, '.');
488         if (dot)
489                 *dot = '\0';
490         code0 = (guint8) strtoul(buf, NULL, 10);
491         if (dot)
492                 code1 = (guint8) strtoul(dot + 1, NULL, 10);
493         g_free(buf);
494
495         memset(&code, 0, sizeof(code));
496         if (vendor) {
497                 if (state->current_vendor_evs_type) {
498                         code.u8_code[0] = (guint8) state->current_vendor_evs_type;
499                         code.u8_code[1] = code0;
500                 } else {
501                         code.u8_code[0] = code0;
502                         code.u8_code[1] = 0;
503                 }
504
505                 radius_vendor_info_t* v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_name,vendor);
506                 if (! v) {
507                         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] );
508                         return FALSE;
509                 } else {
510                         by_id = v->attrs_by_id;
511                 }
512         } else {
513                 code.u8_code[0] = code0;
514                 code.u8_code[1] = code1;
515
516                 by_id = state->dict->attrs_by_id;
517         }
518
519         a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code.value));
520
521         if (!a) {
522                 /*
523                  * New attribute.
524                  * Allocate a new entry and insert it into the by-ID and
525                  * by-name hash tables.
526                  */
527                 a = g_new(radius_attr_info_t,1);
528                 a->code = code;
529                 a->name = g_strdup(name);
530                 a->dissector = NULL;
531                 a->encrypt = encrypted_flag;
532                 a->tagged =  tagged;
533                 a->type = type;
534                 a->vs = NULL;
535                 a->hf = -1;
536                 a->hf_alt = -1;
537                 a->hf_tag = -1;
538                 a->hf_len = -1;
539                 a->ett = -1;
540                 a->tlvs_by_id = NULL;
541                 g_hash_table_insert(by_id, GUINT_TO_POINTER(code.value),a);
542                 g_hash_table_insert(state->dict->attrs_by_name,(gpointer) (a->name),a);
543         } else {
544                 /*
545                  * This attribute is already in the table.
546                  *
547                  * Overwrite the encrypted flag, tagged property, and type;
548                  * the other properties don't get set until after we've
549                  * finished reading the dictionaries.
550                  *
551                  * XXX - this could be due to the attribute being in
552                  * multiple dictionary files, rather than having been
553                  * specially entered by the RADIUS dissector to give it
554                  * a special dissection routine; should we report attributes
555                  * that appear in different dictionaries with different
556                  * properties?
557                  */
558                 a->encrypt = encrypted_flag;
559                 a->tagged =  tagged;
560                 a->type = type;
561
562                 /*
563                  * Did the name change?
564                  */
565                 if (g_strcmp0(a->name, name) != 0) {
566                         /*
567                          * Yes.  Steal the entry from the by-name hash table
568                          * and re-insert it with the new name.  (Don't
569                          * remove it - that calls the free routine, which
570                          * frees up the entry.)
571                          */
572                         g_hash_table_steal(state->dict->attrs_by_name, (gpointer) (a->name));
573                         g_free((gpointer) a->name);
574                         a->name = g_strdup(name);
575                         g_hash_table_insert(state->dict->attrs_by_name, (gpointer) (a->name),a);
576                 }
577         }
578         return TRUE;
579 }
580
581 static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const  gchar* codestr, radius_attr_dissector_t type, const gchar* attr) {
582         radius_attr_info_t* a;
583         radius_attr_info_t* s;
584         radius_attr_type_t code;
585
586         a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->attrs_by_name, attr);
587
588         if (! a) {
589                 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]);
590                 return FALSE;
591         }
592
593         if (type == radius_tlv) {
594                 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]);
595                 return FALSE;
596         }
597
598
599         if (! a->tlvs_by_id) {
600                 a->tlvs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
601         }
602
603         memset(&code, 0, sizeof(code));
604         code.u8_code[0] = (guint8) strtoul(codestr, NULL, 10);
605
606         s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code.value));
607
608         if (!s) {
609                 /*
610                  * This TLV doesn't yet exist in this attribute's TLVs-by-ID
611                  * hash table.  Add it.
612                  */
613                 s = g_new(radius_attr_info_t,1);
614                 s->name = g_strdup(name);
615                 s->dissector = NULL;
616                 s->code = code;
617                 s->type = type;
618                 s->encrypt = FALSE;
619                 s->tagged = FALSE;
620                 s->dissector = NULL;
621                 s->vs = NULL;
622                 s->hf = -1;
623                 s->hf_alt = -1;
624                 s->hf_tag = -1;
625                 s->hf_len = -1;
626                 s->ett = -1;
627                 s->tlvs_by_id = NULL;
628
629                 g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code.value),s);
630                 g_hash_table_insert(state->dict->tlvs_by_name,(gpointer) (s->name),s);
631         }
632
633         /*
634          * If it *does* exist, leave it alone; there shouldn't be duplicate
635          * entries by name in the dictionaries (even if there might be
636          * multiple entries for a given attribute in the dictionaries, each
637          * one adding some TLV values), and we don't directly add entries
638          * for TLVs in the RADIUS dissector.
639          *
640          * XXX - report the duplicate entries?
641          */
642         return TRUE;
643 }
644
645 void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const gchar* repr, guint32 value) {
646         value_string v;
647         GArray* a = (GArray*)g_hash_table_lookup(state->value_strings,attrib_name);
648
649         if (! a) {
650                 /* Ensure that the array is zero terminated. */
651                 a = g_array_new(TRUE, TRUE, sizeof(value_string));
652                 g_hash_table_insert(state->value_strings, g_strdup(attrib_name), a);
653         }
654
655         v.value = value;
656         v.strptr = g_strdup(repr);
657
658         g_array_append_val(a,v);
659 }
660
661 static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p) {
662         radius_attr_info_t* s = (radius_attr_info_t*)v;
663         Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
664         gpointer key;
665
666         union {
667                 GArray* a;
668                 gpointer p;
669         } vs;
670
671         if (g_hash_table_lookup_extended(state->value_strings, s->name, &key, &vs.p)) {
672                 g_hash_table_steal(state->value_strings, key);
673                 s->vs = (value_string*)(void *)g_array_free(vs.a, FALSE);
674                 g_free(key);
675         }
676 }
677
678 static void setup_attrs(gpointer k _U_, gpointer v, gpointer p) {
679         radius_attr_info_t* a = (radius_attr_info_t*)v;
680         Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
681         gpointer key;
682
683         union {
684                 GArray* a;
685                 gpointer p;
686         } vs;
687
688         if (g_hash_table_lookup_extended(state->value_strings, a->name, &key, &vs.p) ) {
689                 g_hash_table_steal(state->value_strings, key);
690                 a->vs = (value_string*)(void *)g_array_free(vs.a, FALSE);
691                 g_free(key);
692         }
693
694         if (a->tlvs_by_id) {
695                 g_hash_table_foreach(a->tlvs_by_id, setup_tlvs, p);
696         }
697 }
698
699 static void setup_vendors(gpointer k _U_, gpointer v, gpointer p) {
700         radius_vendor_info_t* vnd = (radius_vendor_info_t*)v;
701
702         g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p);
703 }
704
705 static void destroy_value_strings(gpointer v) {
706         value_string* vs = (value_string*)(void *)(((GArray*)v)->data);
707
708         for (;vs->strptr;vs++) {
709                 g_free((void*)vs->strptr);
710         }
711
712         g_array_free((GArray*)v,TRUE);
713 }
714
715 gboolean radius_load_dictionary (radius_dictionary_t* d, gchar* dir, const gchar* filename, gchar** err_str) {
716         FILE *in;
717         yyscan_t scanner;
718         Radius_scanner_state_t state;
719         int i;
720
721         state.include_stack_ptr = 0;
722
723         state.dict = d;
724         state.value_strings = NULL;
725
726         state.attr_name = NULL;
727         state.attr_id = NULL;
728         state.attr_type = NULL;
729         state.attr_vendor = NULL;
730         state.vendor_name = NULL;
731         state.vendor_id = 0;
732         state.vendor_type_octets = 1;
733         state.vendor_length_octets = 1;
734         state.vendor_has_flags = FALSE;
735         state.value_repr = NULL;
736         state.encrypted = 0;
737         state.has_tag = FALSE;
738         state.current_vendor = NULL;
739         state.current_vendor_evs_type = 0;
740         state.current_attr = NULL;
741
742         state.directory = dir;
743
744         state.fullpaths[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
745             state.directory,filename);
746         state.linenums[0] = 1;
747         for (i = 1; i < MAX_INCLUDE_DEPTH; i++) {
748                 state.fullpaths[i] = NULL;
749                 state.linenums[i] = 1;
750         }
751
752         state.error = g_string_new("");
753
754         in = ws_fopen(state.fullpaths[0],"r");
755
756         if (!in) {
757                 g_string_append_printf(state.error, "Could not open file: '%s', error: %s\n", state.fullpaths[0], g_strerror(errno));
758                 g_free(state.fullpaths[0]);
759                 *err_str = g_string_free(state.error,FALSE);
760                 return FALSE;
761         }
762
763         state.value_strings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_value_strings);
764
765         if (Radius_lex_init(&scanner) != 0) {
766                 g_string_append_printf(state.error, "Can't initialize scanner: %s",
767                     strerror(errno));
768                 fclose(in);
769                 g_free(state.fullpaths[0]);
770                 *err_str = g_string_free(state.error,FALSE);
771                 return FALSE;
772         }
773
774         Radius_set_in(in, scanner);
775
776         /* Associate the state with the scanner */
777         Radius_set_extra(&state, scanner);
778
779         Radius_lex(scanner);
780
781         Radius_lex_destroy(scanner);
782         /*
783          * XXX - can the lexical analyzer terminate without closing
784          * all open input files?
785          */
786
787         for (i = 0; i < MAX_INCLUDE_DEPTH; i++) {
788                 g_free(state.fullpaths[i]);
789         }
790
791         g_hash_table_foreach(state.dict->attrs_by_id,setup_attrs,&state);
792         g_hash_table_foreach(state.dict->vendors_by_id,setup_vendors,&state);
793         g_hash_table_destroy(state.value_strings);
794
795         if (state.error->len > 0) {
796                 *err_str = g_string_free(state.error,FALSE);
797                 return FALSE;
798         } else {
799                 *err_str = NULL;
800                 g_string_free(state.error,TRUE);
801                 return TRUE;
802         }
803 }
804
805 /*
806  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
807  *
808  * Local variables:
809  * c-basic-offset: 8
810  * tab-width: 8
811  * indent-tabs-mode: t
812  * End:
813  *
814  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
815  * :indentSize=8:tabSize=8:noTabs=false:
816  */