regen pidl all: rm epan/dissectors/pidl/*-stamp; pushd epan/dissectors/pidl/ && make...
[wireshark-sm.git] / epan / radius_dict.l
blob94afa4000ac7cc0285f16de83a48b9da562d6098
1 %top {
2 /* Include this before everything else, for various large-file definitions */
3 #include "config.h"
4 #include <wireshark.h>
7 /*
8  * We want a reentrant scanner.
9  */
10 %option reentrant
13  * We don't use input, so don't generate code for it.
14  */
15 %option noinput
18  * We don't use unput, so don't generate code for it.
19  */
20 %option nounput
23  * We don't read interactively from the terminal.
24  */
25 %option never-interactive
28  * We want to stop processing when we get to the end of the input.
29  */
30 %option noyywrap
33  * The language we're scanning is case-insensitive.
34  */
35 %option caseless
38  * The type for the state we keep for a scanner.
39  */
40 %option extra-type="Radius_scanner_state_t*"
43  * We have to override the memory allocators so that we don't get
44  * "unused argument" warnings from the yyscanner argument (which
45  * we don't use, as we have a global memory allocator).
46  *
47  * We provide, as macros, our own versions of the routines generated by Flex,
48  * which just call malloc()/realloc()/free() (as the Flex versions do),
49  * discarding the extra argument.
50  */
51 %option noyyalloc
52 %option noyyrealloc
53 %option noyyfree
56  * Prefix scanner routines with "Radius_" rather than "yy", so this scanner
57  * can coexist with other scanners.
58  */
59 %option prefix="Radius_"
62         /* radius_dict.l
63         *
64         * RADIUS dictionary parser
65         *
66         * Wireshark - Network traffic analyzer
67         * By Gerald Combs <gerald@wireshark.org>
68         * Copyright 1998 Gerald Combs
69         *
70         * This program is free software; you can redistribute it and/or
71         * modify it under the terms of the GNU General Public License
72         * as published by the Free Software Foundation; either version 2
73         * of the License, or (at your option) any later version.
74         *
75         * This program is distributed in the hope that it will be useful,
76         * but WITHOUT ANY WARRANTY; without even the implied warranty of
77         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
78         * GNU General Public License for more details.
79         *
80         * You should have received a copy of the GNU General Public License
81         * along with this program; if not, write to the Free Software
82         * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
83         */
85 #include <glib.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <errno.h>
90 #include <epan/packet.h>
91 #include <epan/dissectors/packet-radius.h>
92 #include <wsutil/file_util.h>
95  * Disable diagnostics in the code generated by Flex.
96  */
97 DIAG_OFF_FLEX()
100  * See
102  *      http://freeradius.org/radiusd/man/dictionary.html
104  * for the format of RADIUS dictionary files.
106  * XXX - features not currently supported:
108  *      integer64, ipv4prefix, combo-prefix, bool, size, decimal,
109  *      timeval, struct, extended, long-extended, vsa, evs, vendor,
110  *      cidr, uint{8,16,32,64}, int{8,16,32,64} as attribute types
111  *      (some of these aren't documented);
113  *      octets[N], where N is an integer, as an attribute type
114  *      (not documented in the man page) - we support this as the octets
115  *      type but do not enforce the length (e.g., with an expert info).
116  *      FreeRADIUS uses the length for encoding; as we're just decoding,
117  *      we take whatever is indicated in the AVP;
119  *      internal, array, and virtual as attribute flags (not
120  *      documented in the man page);
122  * We alter the dictionaries for TLVs by unwrapping them. It would be
123  * better to support the format as-is. We look up the TLVs by name;
124  * this probably doesn't work with the current master (pre-4.0) version
125  * of FreeRADIUS's dictionary files, because the vendor prefix was removed
126  * from the attributes, so they're not as likely to be unique.
128  * We should, perhaps, adopt FreeRADIUS's dictionary-parsing code in
129  * src/lib/dict.c and use that, rather than writing our own parser.
130  * See bug 13176.
131  */
132 #define YY_USER_INIT BEGIN WS_OUT;
134 #define ECHO
135 #define MAX_INCLUDE_DEPTH 10
137 typedef struct {
138         YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
139         int include_stack_ptr;
141         radius_dictionary_t* dict;
142         GHashTable* value_strings; /* GArray(value_string) by attribute name */
144         char* attr_name;
145         char* attr_id;
146         radius_attr_dissector_t* attr_type;
147         char* attr_vendor;
148         char* vendor_name;
149         uint32_t vendor_id;
150         unsigned vendor_type_octets;
151         unsigned vendor_length_octets;
152         bool vendor_has_flags;
153         char* value_repr;
154         unsigned encrypted;
155         bool has_tag;
156         bool concat;
157         char* current_vendor;
158         unsigned current_vendor_evs_type;
160         int tlv_stack_ptr;
161         char* tlv_stack[MAX_INCLUDE_DEPTH];
163         char* directory;
164         char* fullpaths[MAX_INCLUDE_DEPTH];
165         int linenums[MAX_INCLUDE_DEPTH];
167         GString* error;
168 } Radius_scanner_state_t;
170 static void add_vendor(Radius_scanner_state_t* state, const char* name, uint32_t id, unsigned type_octets, unsigned length_octets, bool has_flags);
171 static bool add_attribute(Radius_scanner_state_t* state, const char*,const  char*, radius_attr_dissector_t,const  char*, unsigned, bool, bool, const char*);
172 static bool add_tlv(Radius_scanner_state_t* state, const char* name, const  char* code, radius_attr_dissector_t type, const char* attr);
173 static void add_value(Radius_scanner_state_t* state, const char* attrib_name, const  char* repr, uint32_t value);
176  * Sleazy hack to suppress compiler warnings in yy_fatal_error().
177  */
178 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
181  * Macros for the allocators, to discard the extra argument.
182  */
183 #define Radius_alloc(size, yyscanner)           (void *)malloc(size)
184 #define Radius_realloc(ptr, size, yyscanner)    (void *)realloc((char *)(ptr), (size))
185 #define Radius_free(ptr, yyscanner)             free((char *)ptr)
189 /* Note: FreeRadius allows VENDOR, ATTRIBUTE and VALUE names to contain any non-blank character.
190  *       Using a negated "blank character class" pattern below for those names fails for some reason
191  *       so for now the patterns for each name type include those characters found for the corresponding
192  *       name types in the FreeRadius dictionaries.
193  */
195 %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
197 [:blank:]   ;
198 #[^\n]*         ;
200 <JUNK>.*\qn             ;
202 <WS_OUT>VENDOR { BEGIN VENDOR; }
203 <WS_OUT>ATTRIBUTE { BEGIN ATTR; }
204 <WS_OUT>VALUE { BEGIN VALUE; }
205 <WS_OUT>\$INCLUDE { BEGIN INCLUDE; }
206 <WS_OUT>BEGIN-VENDOR { BEGIN BEGIN_VENDOR; }
207 <WS_OUT>END-VENDOR { BEGIN END_VENDOR; }
208 <WS_OUT>BEGIN-TLV { BEGIN BEGIN_TLV; }
209 <WS_OUT>END-TLV { BEGIN END_TLV; }
211 <BEGIN_VENDOR>[0-9a-z_-]+ {
212     if (yyextra->current_vendor) {
213         g_free(yyextra->current_vendor);
214     }
215     yyextra->current_vendor = g_strdup(yytext);
216     BEGIN BEGIN_VENDOR_FORMAT;
218 <BEGIN_VENDOR_FORMAT>format=Extended-Vendor-Specific-[123456] {
219     if (strcmp(yytext, "format=Extended-Vendor-Specific-1") == 0) {
220         yyextra->current_vendor_evs_type = 241;
221     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-2") == 0) {
222         yyextra->current_vendor_evs_type = 242;
223     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-3") == 0) {
224         yyextra->current_vendor_evs_type = 243;
225     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-4") == 0) {
226         yyextra->current_vendor_evs_type = 244;
227     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-5") == 0) {
228         yyextra->current_vendor_evs_type = 245;
229     } else if(strcmp(yytext, "format=Extended-Vendor-Specific-6") == 0) {
230         yyextra->current_vendor_evs_type = 246;
231     }
232     BEGIN WS_OUT;
234 <BEGIN_VENDOR_FORMAT>\n {BEGIN WS_OUT;}
236 <END_VENDOR>[^\n]* {
237         if (yyextra->current_vendor) {
238                 g_free(yyextra->current_vendor);
239                 yyextra->current_vendor = NULL;
240         }
241         yyextra->current_vendor_evs_type = 0;
242         BEGIN WS_OUT;
245 <BEGIN_TLV>[0-9a-z_-]+ {
246         yyextra->tlv_stack_ptr++;
247         if ( yyextra->tlv_stack_ptr >= MAX_INCLUDE_DEPTH ) {
248                 g_string_append_printf(yyextra->error, "TLV %s nested too deeply in %s:%i\n",
249                                 yytext, yyextra->fullpaths[yyextra->include_stack_ptr],
250                                 yyextra->linenums[yyextra->include_stack_ptr]);
251                 yyterminate();
252         }
253         yyextra->tlv_stack[yyextra->tlv_stack_ptr] = g_strdup(yytext);
254         /* XXX - Do we really need the BEGIN-TLV? We could do this after
255          * encountering a tlv type. */
256         BEGIN WS_OUT;
258 <END_TLV>[^\n]* {
259         if (yyextra->tlv_stack[yyextra->tlv_stack_ptr]) {
260                 g_free(yyextra->tlv_stack[yyextra->tlv_stack_ptr]);
261                 yyextra->tlv_stack[yyextra->tlv_stack_ptr] = NULL;
262         }
263         if ( --yyextra->tlv_stack_ptr < 0 ) {
264                 g_string_append_printf(yyextra->error, "END-TLV would go below stack level 0 in %s:%i\n",
265                                 yyextra->fullpaths[yyextra->include_stack_ptr],
266                                 yyextra->linenums[yyextra->include_stack_ptr]);
267                 yyterminate();
268         }
269         /* XXX - We could parse yytext and see if it matches */
270         BEGIN WS_OUT;
273 <VENDOR>[0-9a-z_-]+   {
274     yyextra->vendor_name = g_strdup(yytext);
275     yyextra->vendor_type_octets = 1;
276     yyextra->vendor_length_octets = 1;
277     yyextra->vendor_has_flags = false;
278     BEGIN VENDOR_W_NAME;
280 <VENDOR_W_NAME>[0-9]+   {
281     yyextra->vendor_id = (uint32_t) strtoul(yytext,NULL,10);
282     BEGIN VENDOR_W_ID;
284 <VENDOR_W_NAME>0x[0-9a-f]+   {
285     yyextra->vendor_id = (uint32_t) strtoul(yytext,NULL,16);
286     BEGIN VENDOR_W_ID;
288 <VENDOR_W_ID>format= {
289     BEGIN VENDOR_W_FORMAT;
291 <VENDOR_W_FORMAT>[124] {
292     yyextra->vendor_type_octets = (unsigned) strtoul(yytext,NULL,10);
293     BEGIN VENDOR_W_TYPE_OCTETS;
295 <VENDOR_W_TYPE_OCTETS>,[012] {
296     yyextra->vendor_length_octets = (unsigned) strtoul(yytext+1,NULL,10);
297     BEGIN VENDOR_W_LENGTH_OCTETS;
299 <VENDOR_W_LENGTH_OCTETS>,c {
300     yyextra->vendor_has_flags = true;
301     BEGIN VENDOR_W_CONTINUATION;
303 <VENDOR_W_FORMAT>\n |
304 <VENDOR_W_TYPE_OCTETS>\n |
305 <VENDOR_W_LENGTH_OCTETS>\n |
306 <VENDOR_W_CONTINUATION>\n |
307 <VENDOR_W_ID>\n {
308     add_vendor(yyextra, yyextra->vendor_name, yyextra->vendor_id, yyextra->vendor_type_octets, yyextra->vendor_length_octets, yyextra->vendor_has_flags);
309     g_free(yyextra->vendor_name);
310     BEGIN WS_OUT;
313 <ATTR>[0-9a-z_/.-]+                     { yyextra->attr_name = g_strdup(yytext); yyextra->encrypted = 0; yyextra->has_tag = false; yyextra->concat = false; BEGIN ATTR_W_NAME; }
314 <ATTR_W_NAME>[0-9.]+                    { yyextra->attr_id = g_strdup(yytext);  BEGIN ATTR_W_ID;}
315 <ATTR_W_NAME>0x[0-9a-f]+                { yyextra->attr_id = ws_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
316 <ATTR_W_ID>integer                      { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
317 <ATTR_W_ID>string                       { yyextra->attr_type = radius_string;  BEGIN ATTR_W_TYPE; }
318 <ATTR_W_ID>octets(\[[1-9][0-9]*\])?     { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
319 <ATTR_W_ID>ipaddr                       { yyextra->attr_type = radius_ipaddr;  BEGIN ATTR_W_TYPE; }
320 <ATTR_W_ID>ipv6addr                     { yyextra->attr_type = radius_ipv6addr;  BEGIN ATTR_W_TYPE; }
321 <ATTR_W_ID>ipv6prefix                   { yyextra->attr_type = radius_ipv6prefix;  BEGIN ATTR_W_TYPE; }
322 <ATTR_W_ID>ipxnet                       { yyextra->attr_type = radius_ipxnet;  BEGIN ATTR_W_TYPE; }
323 <ATTR_W_ID>date                         { yyextra->attr_type = radius_date;  BEGIN ATTR_W_TYPE; }
324 <ATTR_W_ID>abinary                      { yyextra->attr_type = radius_abinary;  BEGIN ATTR_W_TYPE; }
325 <ATTR_W_ID>ether                        { yyextra->attr_type = radius_ether;  BEGIN ATTR_W_TYPE; }
326 <ATTR_W_ID>ifid                         { yyextra->attr_type = radius_ifid;  BEGIN ATTR_W_TYPE; }
327 <ATTR_W_ID>byte                         { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
328 <ATTR_W_ID>short                        { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
329 <ATTR_W_ID>signed                       { yyextra->attr_type = radius_signed;  BEGIN ATTR_W_TYPE; }
330 <ATTR_W_ID>combo-ip                     { yyextra->attr_type = radius_combo_ip;  BEGIN ATTR_W_TYPE; }
331 <ATTR_W_ID>tlv                          { yyextra->attr_type = radius_tlv;  BEGIN ATTR_W_TYPE; }
332 <ATTR_W_ID>vsa                          { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
333 <ATTR_W_ID>[0-9a-z_-]+                  { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
334 <ATTR_W_TYPE>has_tag[,]?                { yyextra->has_tag = true; }
335 <ATTR_W_TYPE>encrypt=[123][,]?          { yyextra->encrypted = (unsigned) strtoul(yytext+8,NULL,10); }
336 <ATTR_W_TYPE>concat                     { yyextra->concat = true; }
337 <ATTR_W_TYPE>[0-9a-z_-]+=([^\n]*)       ;
338 <ATTR_W_TYPE>[0-9a-z_-]+                {
339     /*
340      * Support for "ATTRIBUTE name oid type vendor", where the token
341      * following the type matches neither has_tag, concat, nor encrypt={1,2,3},
342      * but is a sequence of digits, lower-case letters, underscores,
343      * and hyphens.
344      *
345      * We mark this as a vendor-specific attribute (VSA), with the token
346      * following the type being the vendor name; this notation is deprecated
347      * in favor of BEGIN-VENDOR/END-VENDOR blocks.
348      */
349     bool attribute_ok;
351     yyextra->attr_vendor = g_strdup(yytext);
352     attribute_ok = add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->attr_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->concat, yyextra->tlv_stack[yyextra->tlv_stack_ptr]);
353     g_free(yyextra->attr_id);
354     g_free(yyextra->attr_vendor);
355     g_free(yyextra->attr_name);
356     yyextra->attr_id = NULL;
357     yyextra->attr_vendor = NULL;
358     yyextra->attr_name = NULL;
359     if (attribute_ok)
360         BEGIN WS_OUT;
361     else
362         BEGIN JUNK;
364 <ATTR_W_TYPE>\n                                         {
365     add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->current_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->concat, yyextra->tlv_stack[yyextra->tlv_stack_ptr]);
366     g_free(yyextra->attr_id);
367     g_free(yyextra->attr_name);
368     yyextra->linenums[yyextra->include_stack_ptr]++;
369     yyextra->has_tag = false;
370     yyextra->encrypted=false;
371     yyextra->concat = false;
372     BEGIN WS_OUT;
375 <VALUE>[0-9a-z_/-]+                             { yyextra->attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
376 <VALUE_W_ATTR>[^[:blank:]]+                     { yyextra->value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
377 <VALUE_W_NAME>[0-9]+                            { add_value(yyextra, yyextra->attr_name,yyextra->value_repr, (uint32_t) strtoul(yytext,NULL,10));  g_free(yyextra->attr_name); g_free(yyextra->value_repr); BEGIN WS_OUT;}
378 <VALUE_W_NAME>0x[0-9a-f]+                       { add_value(yyextra, yyextra->attr_name,yyextra->value_repr, (uint32_t) strtoul(yytext,NULL,16));  g_free(yyextra->attr_name); g_free(yyextra->value_repr); BEGIN WS_OUT;}
380 <INCLUDE>[^[:blank:]\n]+   {
381         if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
382                 g_string_append_printf(yyextra->error, "$INCLUDE files nested too deeply\n");
383                 yyterminate();
384         }
386         yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER;
388         if (g_path_is_absolute(yytext)) {
389                 yyextra->fullpaths[yyextra->include_stack_ptr] = ws_strdup(yytext);
390         } else {
391                 yyextra->fullpaths[yyextra->include_stack_ptr] = g_build_filename(yyextra->directory, yytext, NULL);
392         }
394         FILE *old_yyin = yyin;
395         yyin = ws_fopen( yyextra->fullpaths[yyextra->include_stack_ptr], "r" );
397         if (!yyin) {
398                 if (errno) {
399                         g_string_append_printf(yyextra->error,
400                                         "Could not open file: '%s', error: %s\n",
401                                         yyextra->fullpaths[yyextra->include_stack_ptr],
402                                         g_strerror(errno) );
403                 } else {
404                         g_string_append_printf(yyextra->error,
405                                         "Could not open file: '%s', no errno\n",
406                                         yyextra->fullpaths[yyextra->include_stack_ptr]);
407                 }
408                 g_free(yyextra->fullpaths[yyextra->include_stack_ptr]);
409                 yyextra->fullpaths[yyextra->include_stack_ptr] = NULL;
410                 yyextra->include_stack_ptr--;
411                 yyin = old_yyin;
412         } else {
413                 if (g_path_is_absolute(yytext)) {
414                         g_free(yyextra->directory);
415                         /*
416                          * Switch the directory for any relative $INCLUDEs in
417                          * the new file.
418                          * XXX - Follow symlinks (#6466)? FreeRADIUS doesn't.
419                          * Use g_file_test() + g_file_read_link() to do so.
420                          * In that case, make sure to follow symlinks when
421                          * saving the fullpath too, for restoring the directory
422                          * at EOF.
423                          */
424                         yyextra->directory = g_path_get_dirname(yytext);
425                 }
426                 yyextra->linenums[yyextra->include_stack_ptr] = 1;
427                 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner);
428         }
431         BEGIN WS_OUT;
434 <<EOF>> {
436         fclose(yyin);
437         yyin = NULL;
439         g_free(yyextra->directory);
441         if ( --yyextra->include_stack_ptr < 0 ) {
442                 yyterminate();
443         } else {
444                 g_free(yyextra->fullpaths[yyextra->include_stack_ptr+1]);
445                 yyextra->fullpaths[yyextra->include_stack_ptr+1] = NULL;
447                 yyextra->directory = g_path_get_dirname(yyextra->fullpaths[yyextra->include_stack_ptr]);
449                 Radius__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
450                 Radius__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner);
451         }
453         BEGIN WS_OUT;
456 \n      { yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; }
462  * Turn diagnostics back on, so we check the code that we've written.
463  */
464 DIAG_ON_FLEX()
466 static void add_vendor(Radius_scanner_state_t* state, const char* name, uint32_t id, unsigned type_octets, unsigned length_octets, bool has_flags) {
467         radius_vendor_info_t* v;
469         v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_id, GUINT_TO_POINTER(id));
471         if (!v) {
472                 /*
473                  * New vendor.
474                  * Allocate a new entry and insert it into the by-ID and
475                  * by-name hash tables.
476                  */
477                 v = g_new(radius_vendor_info_t,1);
478                 v->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
479                 v->code = id;
480                 v->ett = -1;
481                 v->name = g_strdup(name);
482                 v->type_octets = type_octets;
483                 v->length_octets = length_octets;
484                 v->has_flags = has_flags;
486                 g_hash_table_insert(state->dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
487                 g_hash_table_insert(state->dict->vendors_by_name, (void *) v->name, v);
488         } else {
489                 /*
490                  * This vendor is already in the table.
491                  *
492                  * Assume that the dictionary knows the 'ground truth' about
493                  * the type/length/has_flags information and thus allow the
494                  * dictionary to overwrite these values even for vendors that
495                  * have already been loaded.
496                  *
497                  * XXX - this could be due to the vendor being in multiple
498                  * dictionary files, rather than having been specially
499                  * entered by the RADIUS dissector, as a side effect of
500                  * specially entering an attribute; should we report vendors
501                  * that appear in different dictionaries with different
502                  * properties?
503                  */
504                 v->type_octets = type_octets;
505                 v->length_octets = length_octets;
506                 v->has_flags = has_flags;
508                 /*
509                  * Did the name change?
510                  */
511                 if (g_strcmp0(v->name, name) != 0) {
512                         /*
513                          * Yes.  Remove the entry from the by-name hash table
514                          * and re-insert it with the new name.
515                          */
516                         g_hash_table_remove(state->dict->vendors_by_name, (void *) v->name);
517                         g_free((void *) v->name);
518                         v->name = g_strdup(name);
519                         g_hash_table_insert(state->dict->vendors_by_name, (void *) v->name, v);
520                 }
521         }
524 static bool add_attribute(Radius_scanner_state_t* state, const char* name, const  char* codestr, radius_attr_dissector_t type, const  char* vendor, unsigned encrypted_flag, bool tagged, bool concat, const char* attr) {
525         radius_attr_info_t* a;
526         GHashTable* by_id;
527         radius_attr_type_t code;
528         uint8_t code0 = 0, code1 = 0;
529         char *dot, *buf = NULL;
531         if (attr){
532                 return add_tlv(state, name, codestr, type, attr);
533         }
535         buf = g_strdup(codestr);
536         dot = strchr(codestr, '.');
537         if (dot)
538                 *dot = '\0';
539         code0 = (uint8_t) strtoul(buf, NULL, 10);
540         if (dot)
541                 code1 = (uint8_t) strtoul(dot + 1, NULL, 10);
542         g_free(buf);
544         memset(&code, 0, sizeof(code));
545         if (vendor) {
546                 if (state->current_vendor_evs_type) {
547                         code.u8_code[0] = (uint8_t) state->current_vendor_evs_type;
548                         code.u8_code[1] = code0;
549                 } else {
550                         code.u8_code[0] = code0;
551                         code.u8_code[1] = 0;
552                 }
554                 radius_vendor_info_t* v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_name,vendor);
555                 if (! v) {
556                         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] );
557                         return false;
558                 } else {
559                         by_id = v->attrs_by_id;
560                 }
561         } else {
562                 code.u8_code[0] = code0;
563                 code.u8_code[1] = code1;
565                 by_id = state->dict->attrs_by_id;
566         }
568         /*
569          * XXX - FreeRADIUS dict.c enforces that concat can only be used with
570          * type radius_octets, not with any other flags, and not with VSAs.
571          */
573         a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code.value));
575         if (!a) {
576                 /*
577                  * New attribute.
578                  * Allocate a new entry and insert it into the by-ID and
579                  * by-name hash tables.
580                  */
581                 a = g_new(radius_attr_info_t,1);
582                 a->code = code;
583                 a->name = g_strdup(name);
584                 a->dissector = NULL;
585                 a->encrypt = encrypted_flag;
586                 a->tagged =  tagged;
587                 a->concat =  concat;
588                 a->type = type;
589                 a->vs = NULL;
590                 a->hf = -1;
591                 a->hf_alt = -1;
592                 a->hf_enc = -1;
593                 a->hf_tag = -1;
594                 a->hf_len = -1;
595                 a->ett = -1;
596                 a->tlvs_by_id = NULL;
597                 g_hash_table_insert(by_id, GUINT_TO_POINTER(code.value),a);
598                 g_hash_table_insert(state->dict->attrs_by_name,(void *) (a->name),a);
599         } else {
600                 /*
601                  * This attribute is already in the table.
602                  *
603                  * Overwrite the encrypted flag, tagged property, concat
604                  * property, and type; the other properties don't get set
605                  * until after we've finished reading the dictionaries.
606                  *
607                  * XXX - this could be due to the attribute being in
608                  * multiple dictionary files, rather than having been
609                  * specially entered by the RADIUS dissector to give it
610                  * a special dissection routine; should we report attributes
611                  * that appear in different dictionaries with different
612                  * properties?
613                  */
614                 a->encrypt = encrypted_flag;
615                 a->tagged =  tagged;
616                 a->concat =  concat;
617                 a->type = type;
619                 /*
620                  * Did the name change?
621                  */
622                 if (g_strcmp0(a->name, name) != 0) {
623                         /*
624                          * Yes.  Steal the entry from the by-name hash table
625                          * and re-insert it with the new name.  (Don't
626                          * remove it - that calls the free routine, which
627                          * frees up the entry.)
628                          */
629                         g_hash_table_steal(state->dict->attrs_by_name, (void *) (a->name));
630                         g_free((void *) a->name);
631                         a->name = g_strdup(name);
632                         g_hash_table_insert(state->dict->attrs_by_name, (void *) (a->name),a);
633                 }
634         }
635         return true;
638 static bool add_tlv(Radius_scanner_state_t* state, const char* name, const  char* codestr, radius_attr_dissector_t type, const char* attr) {
639         radius_attr_info_t* a;
640         radius_attr_info_t* s;
641         radius_attr_type_t code;
643         a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->attrs_by_name, attr);
645         if (!a) {
646                 a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->tlvs_by_name, attr);
647         }
649         if (!a) {
650                 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]);
651                 return false;
652         }
654         if (!a->tlvs_by_id) {
655                 a->tlvs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
656         }
658         memset(&code, 0, sizeof(code));
659         code.u8_code[0] = (uint8_t) strtoul(codestr, NULL, 10);
661         s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code.value));
663         if (!s) {
664                 /*
665                  * This TLV doesn't yet exist in this attribute's TLVs-by-ID
666                  * hash table.  Add it.
667                  */
668                 s = g_new(radius_attr_info_t,1);
669                 s->name = g_strdup(name);
670                 s->dissector = NULL;
671                 s->code = code;
672                 s->type = type;
673                 s->encrypt = false;
674                 s->tagged = false;
675                 s->concat = false;
676                 s->dissector = NULL;
677                 s->vs = NULL;
678                 s->hf = -1;
679                 s->hf_alt = -1;
680                 s->hf_tag = -1;
681                 s->hf_len = -1;
682                 s->ett = -1;
683                 s->tlvs_by_id = NULL;
685                 g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code.value),s);
686                 g_hash_table_insert(state->dict->tlvs_by_name,(void *) (s->name),s);
687         }
689         /*
690          * If it *does* exist, leave it alone; there shouldn't be duplicate
691          * entries by name in the dictionaries (even if there might be
692          * multiple entries for a given attribute in the dictionaries, each
693          * one adding some TLV values), and we don't directly add entries
694          * for TLVs in the RADIUS dissector.
695          *
696          * XXX - report the duplicate entries?
697          */
698         return true;
701 void add_value(Radius_scanner_state_t* state, const char* attrib_name, const char* repr, uint32_t value) {
702         value_string v;
703         GArray* a = (GArray*)g_hash_table_lookup(state->value_strings,attrib_name);
705         if (! a) {
706                 /* Ensure that the array is zero terminated. */
707                 a = g_array_new(true, true, sizeof(value_string));
708                 g_hash_table_insert(state->value_strings, g_strdup(attrib_name), a);
709         }
711         v.value = value;
712         v.strptr = g_strdup(repr);
714         g_array_append_val(a,v);
717 static void setup_tlvs(void *k _U_, void *v, void *p) {
718         radius_attr_info_t* s = (radius_attr_info_t*)v;
719         Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
720         void *key;
722         union {
723                 GArray* a;
724                 void *p;
725         } vs;
727         if (g_hash_table_lookup_extended(state->value_strings, s->name, &key, &vs.p)) {
728                 g_hash_table_steal(state->value_strings, key);
729                 s->vs = (value_string*)(void *)g_array_free(vs.a, false);
730                 g_free(key);
731         }
733         if (s->tlvs_by_id) {
734                 g_hash_table_foreach(s->tlvs_by_id, setup_tlvs, p);
735         }
738 static void setup_attrs(void *k _U_, void *v, void *p) {
739         radius_attr_info_t* a = (radius_attr_info_t*)v;
740         Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
741         void *key;
743         union {
744                 GArray* a;
745                 void *p;
746         } vs;
748         if (g_hash_table_lookup_extended(state->value_strings, a->name, &key, &vs.p) ) {
749                 g_hash_table_steal(state->value_strings, key);
750                 a->vs = (value_string*)(void *)g_array_free(vs.a, false);
751                 g_free(key);
752         }
754         if (a->tlvs_by_id) {
755                 g_hash_table_foreach(a->tlvs_by_id, setup_tlvs, p);
756         }
759 static void setup_vendors(void *k _U_, void *v, void *p) {
760         radius_vendor_info_t* vnd = (radius_vendor_info_t*)v;
762         g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p);
765 static void destroy_value_strings(void *v) {
766         value_string* vs = (value_string*)(void *)(((GArray*)v)->data);
768         for (;vs->strptr;vs++) {
769                 g_free((void*)vs->strptr);
770         }
772         g_array_free((GArray*)v,true);
775 bool radius_load_dictionary (radius_dictionary_t* d, char* dir, const char* filename, char** err_str) {
776         FILE *in;
777         yyscan_t scanner;
778         Radius_scanner_state_t state;
779         int i;
781         state.include_stack_ptr = 0;
783         state.dict = d;
784         state.value_strings = NULL;
786         state.attr_name = NULL;
787         state.attr_id = NULL;
788         state.attr_type = NULL;
789         state.attr_vendor = NULL;
790         state.vendor_name = NULL;
791         state.vendor_id = 0;
792         state.vendor_type_octets = 1;
793         state.vendor_length_octets = 1;
794         state.vendor_has_flags = false;
795         state.value_repr = NULL;
796         state.encrypted = 0;
797         state.has_tag = false;
798         state.concat = false;
799         state.current_vendor = NULL;
800         state.current_vendor_evs_type = 0;
802         state.directory = g_strdup(dir);
804         state.fullpaths[0] = ws_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
805             state.directory,filename);
806         state.linenums[0] = 1;
807         state.tlv_stack[0] = NULL;
808         for (i = 1; i < MAX_INCLUDE_DEPTH; i++) {
809                 state.fullpaths[i] = NULL;
810                 state.linenums[i] = 1;
811                 state.tlv_stack[i] = NULL;
812         }
814         state.tlv_stack_ptr = 0;
816         state.error = g_string_new("");
818         in = ws_fopen(state.fullpaths[0],"r");
820         if (!in) {
821                 g_string_append_printf(state.error, "Could not open file: '%s', error: %s\n", state.fullpaths[0], g_strerror(errno));
822                 g_free(state.fullpaths[0]);
823                 *err_str = g_string_free(state.error,FALSE);
824                 return false;
825         }
827         state.value_strings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_value_strings);
829         if (Radius_lex_init(&scanner) != 0) {
830                 g_string_append_printf(state.error, "Can't initialize scanner: %s",
831                     strerror(errno));
832                 fclose(in);
833                 g_free(state.fullpaths[0]);
834                 *err_str = g_string_free(state.error,FALSE);
835                 return false;
836         }
838         Radius_set_in(in, scanner);
840         /* Associate the state with the scanner */
841         Radius_set_extra(&state, scanner);
843         Radius_lex(scanner);
845         Radius_lex_destroy(scanner);
846         /*
847          * XXX - can the lexical analyzer terminate without closing
848          * all open input files?
849          */
851         for (i = 0; i < MAX_INCLUDE_DEPTH; i++) {
852                 g_free(state.fullpaths[i]);
853                 g_free(state.tlv_stack[i]);
854         }
856         g_hash_table_foreach(state.dict->attrs_by_id,setup_attrs,&state);
857         g_hash_table_foreach(state.dict->vendors_by_id,setup_vendors,&state);
858         g_hash_table_destroy(state.value_strings);
860         if (state.error->len > 0) {
861                 *err_str = g_string_free(state.error,FALSE);
862                 return false;
863         } else {
864                 *err_str = NULL;
865                 g_string_free(state.error,TRUE);
866                 return true;
867         }
871  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
873  * Local variables:
874  * c-basic-offset: 8
875  * tab-width: 8
876  * indent-tabs-mode: t
877  * End:
879  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
880  * :indentSize=8:tabSize=8:noTabs=false:
881  */