2 /* Include this before everything else, for various large-file definitions */
8 * We want a reentrant scanner.
13 * We don't use input, so don't generate code for it.
18 * We don't use unput, so don't generate code for it.
23 * We don't read interactively from the terminal.
25 %option never-interactive
28 * We want to stop processing when we get to the end of the input.
33 * The language we're scanning is case-insensitive.
38 * The type for the state we keep for a scanner.
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).
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.
56 * Prefix scanner routines with "Radius_" rather than "yy", so this scanner
57 * can coexist with other scanners.
59 %option prefix="Radius_"
61 %option outfile="radius_dict.c"
66 * RADIUS dictionary parser
68 * Wireshark - Network traffic analyzer
69 * By Gerald Combs <gerald@wireshark.org>
70 * Copyright 1998 Gerald Combs
72 * This program is free software; you can redistribute it and/or
73 * modify it under the terms of the GNU General Public License
74 * as published by the Free Software Foundation; either version 2
75 * of the License, or (at your option) any later version.
77 * This program is distributed in the hope that it will be useful,
78 * but WITHOUT ANY WARRANTY; without even the implied warranty of
79 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
80 * GNU General Public License for more details.
82 * You should have received a copy of the GNU General Public License
83 * along with this program; if not, write to the Free Software
84 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
92 #include <epan/packet.h>
93 #include <epan/dissectors/packet-radius.h>
94 #include <wsutil/file_util.h>
97 * Disable diagnostics in the code generated by Flex.
104 * http://freeradius.org/radiusd/man/dictionary.html
106 * for the format of RADIUS dictionary files.
108 * XXX - features not currently supported:
110 * integer64, ipv4prefix, combo-prefix, bool, size, decimal,
111 * timeval, struct, extended, long-extended, vsa, evs, vendor,
112 * cidr, uint{8,16,32,64}, int{8,16,32,64} as attribute types
113 * (some of these aren't documented);
115 * octets[N], where N is an integer, as an attribute type
116 * (not documented in the man page) - we support this as the octets
117 * type but do not enforce the length (e.g., with an expert info).
118 * FreeRADIUS uses the length for encoding; as we're just decoding,
119 * we take whatever is indicated in the AVP;
121 * internal, array, and virtual as attribute flags (not
122 * documented in the man page);
124 * We alter the dictionaries for TLVs by unwrapping them. It would be
125 * better to support the format as-is. We look up the TLVs by name;
126 * this probably doesn't work with the current master (pre-4.0) version
127 * of FreeRADIUS's dictionary files, because the vendor prefix was removed
128 * from the attributes, so they're not as likely to be unique.
130 * We should, perhaps, adopt FreeRADIUS's dictionary-parsing code in
131 * src/lib/dict.c and use that, rather than writing our own parser.
134 #define YY_USER_INIT BEGIN WS_OUT;
137 #define MAX_INCLUDE_DEPTH 10
140 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
141 int include_stack_ptr;
143 radius_dictionary_t* dict;
144 GHashTable* value_strings; /* GArray(value_string) by attribute name */
148 radius_attr_dissector_t* attr_type;
152 unsigned vendor_type_octets;
153 unsigned vendor_length_octets;
154 bool vendor_has_flags;
159 char* current_vendor;
160 unsigned current_vendor_evs_type;
163 char* tlv_stack[MAX_INCLUDE_DEPTH];
166 char* fullpaths[MAX_INCLUDE_DEPTH];
167 int linenums[MAX_INCLUDE_DEPTH];
170 } Radius_scanner_state_t;
172 static void add_vendor(Radius_scanner_state_t* state, const char* name, uint32_t id, unsigned type_octets, unsigned length_octets, bool has_flags);
173 static bool add_attribute(Radius_scanner_state_t* state, const char*,const char*, radius_attr_dissector_t,const char*, unsigned, bool, bool, const char*);
174 static bool add_tlv(Radius_scanner_state_t* state, const char* name, const char* code, radius_attr_dissector_t type, const char* attr);
175 static void add_value(Radius_scanner_state_t* state, const char* attrib_name, const char* repr, uint32_t value);
178 * Sleazy hack to suppress compiler warnings in yy_fatal_error().
180 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
183 * Macros for the allocators, to discard the extra argument.
185 #define Radius_alloc(size, yyscanner) (void *)malloc(size)
186 #define Radius_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
187 #define Radius_free(ptr, yyscanner) free((char *)ptr)
191 /* Note: FreeRadius allows VENDOR, ATTRIBUTE and VALUE names to contain any non-blank character.
192 * Using a negated "blank character class" pattern below for those names fails for some reason
193 * so for now the patterns for each name type include those characters found for the corresponding
194 * name types in the FreeRadius dictionaries.
197 %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
204 <WS_OUT>VENDOR { BEGIN VENDOR; }
205 <WS_OUT>ATTRIBUTE { BEGIN ATTR; }
206 <WS_OUT>VALUE { BEGIN VALUE; }
207 <WS_OUT>\$INCLUDE { BEGIN INCLUDE; }
208 <WS_OUT>BEGIN-VENDOR { BEGIN BEGIN_VENDOR; }
209 <WS_OUT>END-VENDOR { BEGIN END_VENDOR; }
210 <WS_OUT>BEGIN-TLV { BEGIN BEGIN_TLV; }
211 <WS_OUT>END-TLV { BEGIN END_TLV; }
213 <BEGIN_VENDOR>[0-9a-z_-]+ {
214 if (yyextra->current_vendor) {
215 g_free(yyextra->current_vendor);
217 yyextra->current_vendor = g_strdup(yytext);
218 BEGIN BEGIN_VENDOR_FORMAT;
220 <BEGIN_VENDOR_FORMAT>format=Extended-Vendor-Specific-[123456] {
221 if (strcmp(yytext, "format=Extended-Vendor-Specific-1") == 0) {
222 yyextra->current_vendor_evs_type = 241;
223 } else if(strcmp(yytext, "format=Extended-Vendor-Specific-2") == 0) {
224 yyextra->current_vendor_evs_type = 242;
225 } else if(strcmp(yytext, "format=Extended-Vendor-Specific-3") == 0) {
226 yyextra->current_vendor_evs_type = 243;
227 } else if(strcmp(yytext, "format=Extended-Vendor-Specific-4") == 0) {
228 yyextra->current_vendor_evs_type = 244;
229 } else if(strcmp(yytext, "format=Extended-Vendor-Specific-5") == 0) {
230 yyextra->current_vendor_evs_type = 245;
231 } else if(strcmp(yytext, "format=Extended-Vendor-Specific-6") == 0) {
232 yyextra->current_vendor_evs_type = 246;
236 <BEGIN_VENDOR_FORMAT>\n {BEGIN WS_OUT;}
239 if (yyextra->current_vendor) {
240 g_free(yyextra->current_vendor);
241 yyextra->current_vendor = NULL;
243 yyextra->current_vendor_evs_type = 0;
247 <BEGIN_TLV>[0-9a-z_-]+ {
248 yyextra->tlv_stack_ptr++;
249 if ( yyextra->tlv_stack_ptr >= MAX_INCLUDE_DEPTH ) {
250 g_string_append_printf(yyextra->error, "TLV %s nested too deeply in %s:%i\n",
251 yytext, yyextra->fullpaths[yyextra->include_stack_ptr],
252 yyextra->linenums[yyextra->include_stack_ptr]);
255 yyextra->tlv_stack[yyextra->tlv_stack_ptr] = g_strdup(yytext);
256 /* XXX - Do we really need the BEGIN-TLV? We could do this after
257 * encountering a tlv type. */
261 if (yyextra->tlv_stack[yyextra->tlv_stack_ptr]) {
262 g_free(yyextra->tlv_stack[yyextra->tlv_stack_ptr]);
263 yyextra->tlv_stack[yyextra->tlv_stack_ptr] = NULL;
265 if ( --yyextra->tlv_stack_ptr < 0 ) {
266 g_string_append_printf(yyextra->error, "END-TLV would go below stack level 0 in %s:%i\n",
267 yyextra->fullpaths[yyextra->include_stack_ptr],
268 yyextra->linenums[yyextra->include_stack_ptr]);
271 /* XXX - We could parse yytext and see if it matches */
275 <VENDOR>[0-9a-z_-]+ {
276 yyextra->vendor_name = g_strdup(yytext);
277 yyextra->vendor_type_octets = 1;
278 yyextra->vendor_length_octets = 1;
279 yyextra->vendor_has_flags = false;
282 <VENDOR_W_NAME>[0-9]+ {
283 yyextra->vendor_id = (uint32_t) strtoul(yytext,NULL,10);
286 <VENDOR_W_NAME>0x[0-9a-f]+ {
287 yyextra->vendor_id = (uint32_t) strtoul(yytext,NULL,16);
290 <VENDOR_W_ID>format= {
291 BEGIN VENDOR_W_FORMAT;
293 <VENDOR_W_FORMAT>[124] {
294 yyextra->vendor_type_octets = (unsigned) strtoul(yytext,NULL,10);
295 BEGIN VENDOR_W_TYPE_OCTETS;
297 <VENDOR_W_TYPE_OCTETS>,[012] {
298 yyextra->vendor_length_octets = (unsigned) strtoul(yytext+1,NULL,10);
299 BEGIN VENDOR_W_LENGTH_OCTETS;
301 <VENDOR_W_LENGTH_OCTETS>,c {
302 yyextra->vendor_has_flags = true;
303 BEGIN VENDOR_W_CONTINUATION;
305 <VENDOR_W_FORMAT>\n |
306 <VENDOR_W_TYPE_OCTETS>\n |
307 <VENDOR_W_LENGTH_OCTETS>\n |
308 <VENDOR_W_CONTINUATION>\n |
310 add_vendor(yyextra, yyextra->vendor_name, yyextra->vendor_id, yyextra->vendor_type_octets, yyextra->vendor_length_octets, yyextra->vendor_has_flags);
311 g_free(yyextra->vendor_name);
315 <ATTR>[0-9a-z_/.-]+ { yyextra->attr_name = g_strdup(yytext); yyextra->encrypted = 0; yyextra->has_tag = false; yyextra->concat = false; BEGIN ATTR_W_NAME; }
316 <ATTR_W_NAME>[0-9.]+ { yyextra->attr_id = g_strdup(yytext); BEGIN ATTR_W_ID;}
317 <ATTR_W_NAME>0x[0-9a-f]+ { yyextra->attr_id = ws_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
318 <ATTR_W_ID>integer { yyextra->attr_type = radius_integer; BEGIN ATTR_W_TYPE; }
319 <ATTR_W_ID>string { yyextra->attr_type = radius_string; BEGIN ATTR_W_TYPE; }
320 <ATTR_W_ID>octets(\[[1-9][0-9]*\])? { yyextra->attr_type = radius_octets; BEGIN ATTR_W_TYPE; }
321 <ATTR_W_ID>ipaddr { yyextra->attr_type = radius_ipaddr; BEGIN ATTR_W_TYPE; }
322 <ATTR_W_ID>ipv6addr { yyextra->attr_type = radius_ipv6addr; BEGIN ATTR_W_TYPE; }
323 <ATTR_W_ID>ipv6prefix { yyextra->attr_type = radius_ipv6prefix; BEGIN ATTR_W_TYPE; }
324 <ATTR_W_ID>ipxnet { yyextra->attr_type = radius_ipxnet; BEGIN ATTR_W_TYPE; }
325 <ATTR_W_ID>date { yyextra->attr_type = radius_date; BEGIN ATTR_W_TYPE; }
326 <ATTR_W_ID>abinary { yyextra->attr_type = radius_abinary; BEGIN ATTR_W_TYPE; }
327 <ATTR_W_ID>ether { yyextra->attr_type = radius_ether; BEGIN ATTR_W_TYPE; }
328 <ATTR_W_ID>ifid { yyextra->attr_type = radius_ifid; BEGIN ATTR_W_TYPE; }
329 <ATTR_W_ID>byte { yyextra->attr_type = radius_integer; BEGIN ATTR_W_TYPE; }
330 <ATTR_W_ID>short { yyextra->attr_type = radius_integer; BEGIN ATTR_W_TYPE; }
331 <ATTR_W_ID>signed { yyextra->attr_type = radius_signed; BEGIN ATTR_W_TYPE; }
332 <ATTR_W_ID>combo-ip { yyextra->attr_type = radius_combo_ip; BEGIN ATTR_W_TYPE; }
333 <ATTR_W_ID>tlv { yyextra->attr_type = radius_tlv; BEGIN ATTR_W_TYPE; }
334 <ATTR_W_ID>vsa { yyextra->attr_type = radius_octets; BEGIN ATTR_W_TYPE; }
335 <ATTR_W_ID>[0-9a-z_-]+ { yyextra->attr_type = radius_octets; BEGIN ATTR_W_TYPE; }
336 <ATTR_W_TYPE>has_tag[,]? { yyextra->has_tag = true; }
337 <ATTR_W_TYPE>encrypt=[123][,]? { yyextra->encrypted = (unsigned) strtoul(yytext+8,NULL,10); }
338 <ATTR_W_TYPE>concat { yyextra->concat = true; }
339 <ATTR_W_TYPE>[0-9a-z_-]+=([^\n]*) ;
340 <ATTR_W_TYPE>[0-9a-z_-]+ {
342 * Support for "ATTRIBUTE name oid type vendor", where the token
343 * following the type matches neither has_tag, concat, nor encrypt={1,2,3},
344 * but is a sequence of digits, lower-case letters, underscores,
347 * We mark this as a vendor-specific attribute (VSA), with the token
348 * following the type being the vendor name; this notation is deprecated
349 * in favor of BEGIN-VENDOR/END-VENDOR blocks.
353 yyextra->attr_vendor = g_strdup(yytext);
354 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]);
355 g_free(yyextra->attr_id);
356 g_free(yyextra->attr_vendor);
357 g_free(yyextra->attr_name);
358 yyextra->attr_id = NULL;
359 yyextra->attr_vendor = NULL;
360 yyextra->attr_name = NULL;
367 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]);
368 g_free(yyextra->attr_id);
369 g_free(yyextra->attr_name);
370 yyextra->linenums[yyextra->include_stack_ptr]++;
371 yyextra->has_tag = false;
372 yyextra->encrypted=false;
373 yyextra->concat = false;
377 <VALUE>[0-9a-z_/-]+ { yyextra->attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
378 <VALUE_W_ATTR>[^[:blank:]]+ { yyextra->value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
379 <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;}
380 <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;}
382 <INCLUDE>[^[:blank:]\n]+ {
383 if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
384 g_string_append_printf(yyextra->error, "$INCLUDE files nested too deeply\n");
388 yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER;
390 if (g_path_is_absolute(yytext)) {
391 yyextra->fullpaths[yyextra->include_stack_ptr] = ws_strdup(yytext);
393 yyextra->fullpaths[yyextra->include_stack_ptr] = g_build_filename(yyextra->directory, yytext, NULL);
396 FILE *old_yyin = yyin;
397 yyin = ws_fopen( yyextra->fullpaths[yyextra->include_stack_ptr], "r" );
401 g_string_append_printf(yyextra->error,
402 "Could not open file: '%s', error: %s\n",
403 yyextra->fullpaths[yyextra->include_stack_ptr],
406 g_string_append_printf(yyextra->error,
407 "Could not open file: '%s', no errno\n",
408 yyextra->fullpaths[yyextra->include_stack_ptr]);
410 g_free(yyextra->fullpaths[yyextra->include_stack_ptr]);
411 yyextra->fullpaths[yyextra->include_stack_ptr] = NULL;
412 yyextra->include_stack_ptr--;
415 if (g_path_is_absolute(yytext)) {
416 g_free(yyextra->directory);
418 * Switch the directory for any relative $INCLUDEs in
420 * XXX - Follow symlinks (#6466)? FreeRADIUS doesn't.
421 * Use g_file_test() + g_file_read_link() to do so.
422 * In that case, make sure to follow symlinks when
423 * saving the fullpath too, for restoring the directory
426 yyextra->directory = g_path_get_dirname(yytext);
428 yyextra->linenums[yyextra->include_stack_ptr] = 1;
429 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner);
441 g_free(yyextra->directory);
443 if ( --yyextra->include_stack_ptr < 0 ) {
446 g_free(yyextra->fullpaths[yyextra->include_stack_ptr+1]);
447 yyextra->fullpaths[yyextra->include_stack_ptr+1] = NULL;
449 yyextra->directory = g_path_get_dirname(yyextra->fullpaths[yyextra->include_stack_ptr]);
451 Radius__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
452 Radius__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner);
458 \n { yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; }
464 * Turn diagnostics back on, so we check the code that we've written.
468 static void add_vendor(Radius_scanner_state_t* state, const char* name, uint32_t id, unsigned type_octets, unsigned length_octets, bool has_flags) {
469 radius_vendor_info_t* v;
471 v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_id, GUINT_TO_POINTER(id));
476 * Allocate a new entry and insert it into the by-ID and
477 * by-name hash tables.
479 v = g_new(radius_vendor_info_t,1);
480 v->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
483 v->name = g_strdup(name);
484 v->type_octets = type_octets;
485 v->length_octets = length_octets;
486 v->has_flags = has_flags;
488 g_hash_table_insert(state->dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
489 g_hash_table_insert(state->dict->vendors_by_name, (void *) v->name, v);
492 * This vendor is already in the table.
494 * Assume that the dictionary knows the 'ground truth' about
495 * the type/length/has_flags information and thus allow the
496 * dictionary to overwrite these values even for vendors that
497 * have already been loaded.
499 * XXX - this could be due to the vendor being in multiple
500 * dictionary files, rather than having been specially
501 * entered by the RADIUS dissector, as a side effect of
502 * specially entering an attribute; should we report vendors
503 * that appear in different dictionaries with different
506 v->type_octets = type_octets;
507 v->length_octets = length_octets;
508 v->has_flags = has_flags;
511 * Did the name change?
513 if (g_strcmp0(v->name, name) != 0) {
515 * Yes. Remove the entry from the by-name hash table
516 * and re-insert it with the new name.
518 g_hash_table_remove(state->dict->vendors_by_name, (void *) v->name);
519 g_free((void *) v->name);
520 v->name = g_strdup(name);
521 g_hash_table_insert(state->dict->vendors_by_name, (void *) v->name, v);
526 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) {
527 radius_attr_info_t* a;
529 radius_attr_type_t code;
530 uint8_t code0 = 0, code1 = 0;
531 char *dot, *buf = NULL;
534 return add_tlv(state, name, codestr, type, attr);
537 buf = g_strdup(codestr);
538 dot = strchr(codestr, '.');
541 code0 = (uint8_t) strtoul(buf, NULL, 10);
543 code1 = (uint8_t) strtoul(dot + 1, NULL, 10);
546 memset(&code, 0, sizeof(code));
548 if (state->current_vendor_evs_type) {
549 code.u8_code[0] = (uint8_t) state->current_vendor_evs_type;
550 code.u8_code[1] = code0;
552 code.u8_code[0] = code0;
556 radius_vendor_info_t* v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_name,vendor);
558 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] );
561 by_id = v->attrs_by_id;
564 code.u8_code[0] = code0;
565 code.u8_code[1] = code1;
567 by_id = state->dict->attrs_by_id;
571 * XXX - FreeRADIUS dict.c enforces that concat can only be used with
572 * type radius_octets, not with any other flags, and not with VSAs.
575 a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code.value));
580 * Allocate a new entry and insert it into the by-ID and
581 * by-name hash tables.
583 a = g_new(radius_attr_info_t,1);
585 a->name = g_strdup(name);
587 a->encrypt = encrypted_flag;
598 a->tlvs_by_id = NULL;
599 g_hash_table_insert(by_id, GUINT_TO_POINTER(code.value),a);
600 g_hash_table_insert(state->dict->attrs_by_name,(void *) (a->name),a);
603 * This attribute is already in the table.
605 * Overwrite the encrypted flag, tagged property, concat
606 * property, and type; the other properties don't get set
607 * until after we've finished reading the dictionaries.
609 * XXX - this could be due to the attribute being in
610 * multiple dictionary files, rather than having been
611 * specially entered by the RADIUS dissector to give it
612 * a special dissection routine; should we report attributes
613 * that appear in different dictionaries with different
616 a->encrypt = encrypted_flag;
622 * Did the name change?
624 if (g_strcmp0(a->name, name) != 0) {
626 * Yes. Steal the entry from the by-name hash table
627 * and re-insert it with the new name. (Don't
628 * remove it - that calls the free routine, which
629 * frees up the entry.)
631 g_hash_table_steal(state->dict->attrs_by_name, (void *) (a->name));
632 g_free((void *) a->name);
633 a->name = g_strdup(name);
634 g_hash_table_insert(state->dict->attrs_by_name, (void *) (a->name),a);
640 static bool add_tlv(Radius_scanner_state_t* state, const char* name, const char* codestr, radius_attr_dissector_t type, const char* attr) {
641 radius_attr_info_t* a;
642 radius_attr_info_t* s;
643 radius_attr_type_t code;
645 a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->attrs_by_name, attr);
648 a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->tlvs_by_name, attr);
652 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]);
656 if (!a->tlvs_by_id) {
657 a->tlvs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
660 memset(&code, 0, sizeof(code));
661 code.u8_code[0] = (uint8_t) strtoul(codestr, NULL, 10);
663 s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code.value));
667 * This TLV doesn't yet exist in this attribute's TLVs-by-ID
668 * hash table. Add it.
670 s = g_new(radius_attr_info_t,1);
671 s->name = g_strdup(name);
685 s->tlvs_by_id = NULL;
687 g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code.value),s);
688 g_hash_table_insert(state->dict->tlvs_by_name,(void *) (s->name),s);
692 * If it *does* exist, leave it alone; there shouldn't be duplicate
693 * entries by name in the dictionaries (even if there might be
694 * multiple entries for a given attribute in the dictionaries, each
695 * one adding some TLV values), and we don't directly add entries
696 * for TLVs in the RADIUS dissector.
698 * XXX - report the duplicate entries?
703 void add_value(Radius_scanner_state_t* state, const char* attrib_name, const char* repr, uint32_t value) {
705 GArray* a = (GArray*)g_hash_table_lookup(state->value_strings,attrib_name);
708 /* Ensure that the array is zero terminated. */
709 a = g_array_new(true, true, sizeof(value_string));
710 g_hash_table_insert(state->value_strings, g_strdup(attrib_name), a);
714 v.strptr = g_strdup(repr);
716 g_array_append_val(a,v);
719 static void setup_tlvs(void *k _U_, void *v, void *p) {
720 radius_attr_info_t* s = (radius_attr_info_t*)v;
721 Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
729 if (g_hash_table_lookup_extended(state->value_strings, s->name, &key, &vs.p)) {
730 g_hash_table_steal(state->value_strings, key);
731 s->vs = (value_string*)(void *)g_array_free(vs.a, false);
736 g_hash_table_foreach(s->tlvs_by_id, setup_tlvs, p);
740 static void setup_attrs(void *k _U_, void *v, void *p) {
741 radius_attr_info_t* a = (radius_attr_info_t*)v;
742 Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
750 if (g_hash_table_lookup_extended(state->value_strings, a->name, &key, &vs.p) ) {
751 g_hash_table_steal(state->value_strings, key);
752 a->vs = (value_string*)(void *)g_array_free(vs.a, false);
757 g_hash_table_foreach(a->tlvs_by_id, setup_tlvs, p);
761 static void setup_vendors(void *k _U_, void *v, void *p) {
762 radius_vendor_info_t* vnd = (radius_vendor_info_t*)v;
764 g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p);
767 static void destroy_value_strings(void *v) {
768 value_string* vs = (value_string*)(void *)(((GArray*)v)->data);
770 for (;vs->strptr;vs++) {
771 g_free((void*)vs->strptr);
774 g_array_free((GArray*)v,true);
777 bool radius_load_dictionary (radius_dictionary_t* d, char* dir, const char* filename, char** err_str) {
780 Radius_scanner_state_t state;
783 state.include_stack_ptr = 0;
786 state.value_strings = NULL;
788 state.attr_name = NULL;
789 state.attr_id = NULL;
790 state.attr_type = NULL;
791 state.attr_vendor = NULL;
792 state.vendor_name = NULL;
794 state.vendor_type_octets = 1;
795 state.vendor_length_octets = 1;
796 state.vendor_has_flags = false;
797 state.value_repr = NULL;
799 state.has_tag = false;
800 state.concat = false;
801 state.current_vendor = NULL;
802 state.current_vendor_evs_type = 0;
804 state.directory = g_strdup(dir);
806 state.fullpaths[0] = ws_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
807 state.directory,filename);
808 state.linenums[0] = 1;
809 state.tlv_stack[0] = NULL;
810 for (i = 1; i < MAX_INCLUDE_DEPTH; i++) {
811 state.fullpaths[i] = NULL;
812 state.linenums[i] = 1;
813 state.tlv_stack[i] = NULL;
816 state.tlv_stack_ptr = 0;
818 state.error = g_string_new("");
820 in = ws_fopen(state.fullpaths[0],"r");
823 g_string_append_printf(state.error, "Could not open file: '%s', error: %s\n", state.fullpaths[0], g_strerror(errno));
824 g_free(state.fullpaths[0]);
825 *err_str = g_string_free(state.error,FALSE);
829 state.value_strings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_value_strings);
831 if (Radius_lex_init(&scanner) != 0) {
832 g_string_append_printf(state.error, "Can't initialize scanner: %s",
835 g_free(state.fullpaths[0]);
836 *err_str = g_string_free(state.error,FALSE);
840 Radius_set_in(in, scanner);
842 /* Associate the state with the scanner */
843 Radius_set_extra(&state, scanner);
847 Radius_lex_destroy(scanner);
849 * XXX - can the lexical analyzer terminate without closing
850 * all open input files?
853 for (i = 0; i < MAX_INCLUDE_DEPTH; i++) {
854 g_free(state.fullpaths[i]);
855 g_free(state.tlv_stack[i]);
858 g_hash_table_foreach(state.dict->attrs_by_id,setup_attrs,&state);
859 g_hash_table_foreach(state.dict->vendors_by_id,setup_vendors,&state);
860 g_hash_table_destroy(state.value_strings);
862 if (state.error->len > 0) {
863 *err_str = g_string_free(state.error,FALSE);
867 g_string_free(state.error,TRUE);
873 * Editor modelines - https://www.wireshark.org/tools/modelines.html
878 * indent-tabs-mode: t
881 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
882 * :indentSize=8:tabSize=8:noTabs=false: