2 * We don't use input, so don't generate code for it.
7 * We don't use unput, so don't generate code for it.
12 * We don't read interactively from the terminal.
14 %option never-interactive
17 * We want to stop processing when we get to the end of the input.
22 * The language we're scanning is case-insensitive.
27 * Prefix scanner routines with "Radius" rather than "yy", so this scanner
28 * can coexist with other scanners.
30 %option prefix="Radius"
32 %option outfile="radius_dict.c"
37 * RADIUS dictionary parser
39 * Wireshark - Network traffic analyzer
40 * By Gerald Combs <gerald@wireshark.org>
41 * Copyright 1998 Gerald Combs
43 * This program is free software; you can redistribute it and/or
44 * modify it under the terms of the GNU General Public License
45 * as published by the Free Software Foundation; either version 2
46 * of the License, or (at your option) any later version.
48 * This program is distributed in the hope that it will be useful,
49 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 * GNU General Public License for more details.
53 * You should have received a copy of the GNU General Public License
54 * along with this program; if not, write to the Free Software
55 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
65 #include <epan/packet.h>
66 #include <epan/dissectors/packet-radius.h>
67 #include "radius_dict_lex.h"
68 #include <wsutil/file_util.h>
71 /* disable Windows VC compiler warning "signed/unsigned mismatch" associated */
72 /* with YY_INPUT code generated by flex versions such as 2.5.35. */
73 #pragma warning (disable:4018)
77 #define MAX_INCLUDE_DEPTH 10
79 static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags);
80 static void add_value(const gchar* attrib_name,const gchar* repr, guint32 value);
81 static void add_tlv(const gchar* name, const gchar* code, radius_attr_dissector_t type, const gchar* attr);
82 static void add_attribute(const gchar*,const gchar*, radius_attr_dissector_t,const gchar*, guint, gboolean, const gchar*);
84 static YY_BUFFER_STATE include_stack[10];
85 static int include_stack_ptr = 0;
87 static radius_dictionary_t* dict = NULL;
88 static GHashTable* value_strings = NULL; /* GArray(value_string) by attribute name */
90 static gchar* attr_name = NULL;
91 static gchar* attr_id = NULL;
92 static radius_attr_dissector_t* attr_type = NULL;
93 static gchar* attr_vendor = NULL;
94 static gchar* vendor_name = NULL;
95 static guint32 vendor_id = 0;
96 static guint vendor_type_octets = 1;
97 static guint vendor_length_octets = 1;
98 static gboolean vendor_has_flags = FALSE;
99 static gchar* value_repr = NULL;
100 static guint encrypted = 0;
101 static gboolean has_tag = FALSE;
102 static gchar* current_vendor = NULL;
103 static gchar* current_attr = NULL;
105 static GString* error = NULL;
106 static gchar* directory = NULL;
107 static int linenums[] = {1,1,1,1,1,1,1,1,1,1};
108 static gchar* fullpaths[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
112 /* Note: FreeRadius allows VENDOR, ATTRIBUTE and VALUE names to contain any non-blank character.
113 * Using a negated "blank character class" pattern below for those names fails for some reason
114 * so for now the patterns for each name type include those characters found for the corresponding
115 * name types in the FreeRadius dictionaries.
118 %START WS_OUT VENDOR VENDOR_W_NAME ATTR ATTR_W_NAME ATTR_W_ID ATTR_W_TYPE ATTR_W_VENDOR VALUE VALUE_W_ATTR VALUE_W_NAME INCLUDE JUNK BEGIN_VENDOR END_VENDOR VENDOR_W_ID VENDOR_W_FORMAT VENDOR_W_TYPE_OCTETS VENDOR_W_LENGTH_OCTETS VENDOR_W_CONTINUATION BEGIN_TLV END_TLV
125 <WS_OUT>VENDOR { BEGIN VENDOR; }
126 <WS_OUT>ATTRIBUTE { BEGIN ATTR; }
127 <WS_OUT>VALUE { BEGIN VALUE; }
128 <WS_OUT>\$INCLUDE { BEGIN INCLUDE; }
129 <WS_OUT>BEGIN-VENDOR { BEGIN BEGIN_VENDOR; }
130 <WS_OUT>END-VENDOR { BEGIN END_VENDOR; }
131 <WS_OUT>BEGIN-TLV { BEGIN BEGIN_TLV; }
132 <WS_OUT>END-TLV { BEGIN END_TLV; }
134 <BEGIN_VENDOR>[0-9a-z_-]+ {
135 if (current_vendor) {
136 g_free(current_vendor);
138 current_vendor = g_strdup(yytext);
142 if (current_vendor) {
143 g_free(current_vendor);
144 current_vendor = NULL;
149 <BEGIN_TLV>[0-9a-z_-]+ {
151 g_free(current_attr);
153 current_attr = g_strdup(yytext);
158 g_free(current_attr);
164 <VENDOR>[0-9a-z_-]+ {
165 vendor_name = g_strdup(yytext);
166 vendor_type_octets = 1;
167 vendor_length_octets = 1;
168 vendor_has_flags = FALSE;
171 <VENDOR_W_NAME>[0-9]+ {
172 vendor_id = (guint32) strtoul(yytext,NULL,10);
175 <VENDOR_W_NAME>0x[0-9a-f]+ {
176 vendor_id = (guint32) strtoul(yytext,NULL,16);
179 <VENDOR_W_ID>format= {
180 BEGIN VENDOR_W_FORMAT;
182 <VENDOR_W_FORMAT>[124] {
183 vendor_type_octets = (guint) strtoul(yytext,NULL,10);
184 BEGIN VENDOR_W_TYPE_OCTETS;
186 <VENDOR_W_TYPE_OCTETS>,[012] {
187 vendor_length_octets = (guint) strtoul(yytext+1,NULL,10);
188 BEGIN VENDOR_W_LENGTH_OCTETS;
190 <VENDOR_W_LENGTH_OCTETS>,c {
191 vendor_has_flags = TRUE;
192 BEGIN VENDOR_W_CONTINUATION;
194 <VENDOR_W_FORMAT>\n |
195 <VENDOR_W_TYPE_OCTETS>\n |
196 <VENDOR_W_LENGTH_OCTETS>\n |
197 <VENDOR_W_CONTINUATION>\n |
199 add_vendor(vendor_name, vendor_id, vendor_type_octets, vendor_length_octets, vendor_has_flags);
204 <ATTR>[0-9a-z_/.-]+ { attr_name = g_strdup(yytext); encrypted = 0; has_tag = FALSE; BEGIN ATTR_W_NAME; }
205 <ATTR_W_NAME>[0-9]+ { attr_id = g_strdup(yytext); BEGIN ATTR_W_ID;}
206 <ATTR_W_NAME>0x[0-9a-f]+ { attr_id = g_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
207 <ATTR_W_ID>integer { attr_type = radius_integer; BEGIN ATTR_W_TYPE; }
208 <ATTR_W_ID>string { attr_type = radius_string; BEGIN ATTR_W_TYPE; }
209 <ATTR_W_ID>octets { attr_type = radius_octets; BEGIN ATTR_W_TYPE; }
210 <ATTR_W_ID>ipaddr { attr_type = radius_ipaddr; BEGIN ATTR_W_TYPE; }
211 <ATTR_W_ID>ipv6addr { attr_type = radius_ipv6addr; BEGIN ATTR_W_TYPE; }
212 <ATTR_W_ID>ipv6prefix { attr_type = radius_ipv6prefix; BEGIN ATTR_W_TYPE; }
213 <ATTR_W_ID>ipxnet { attr_type = radius_ipxnet; BEGIN ATTR_W_TYPE; }
214 <ATTR_W_ID>date { attr_type = radius_date; BEGIN ATTR_W_TYPE; }
215 <ATTR_W_ID>abinary { attr_type = radius_abinary; BEGIN ATTR_W_TYPE; }
216 <ATTR_W_ID>ether { attr_type = radius_ether; BEGIN ATTR_W_TYPE; }
217 <ATTR_W_ID>ifid { attr_type = radius_ifid; BEGIN ATTR_W_TYPE; }
218 <ATTR_W_ID>byte { attr_type = radius_integer; BEGIN ATTR_W_TYPE; }
219 <ATTR_W_ID>short { attr_type = radius_integer; BEGIN ATTR_W_TYPE; }
220 <ATTR_W_ID>signed { attr_type = radius_signed; BEGIN ATTR_W_TYPE; }
221 <ATTR_W_ID>combo-ip { attr_type = radius_combo_ip; BEGIN ATTR_W_TYPE; }
222 <ATTR_W_ID>tlv { attr_type = radius_tlv; BEGIN ATTR_W_TYPE; }
223 <ATTR_W_ID>[0-9a-z_-]+ { attr_type = radius_octets; BEGIN ATTR_W_TYPE; }
224 <ATTR_W_TYPE>has_tag[,]? { has_tag = TRUE; }
225 <ATTR_W_TYPE>encrypt=[123][,]? { encrypted = (guint) strtoul(yytext+8,NULL,10); }
226 <ATTR_W_TYPE>[0-9a-z_-]+=([^\n]*) ;
227 <ATTR_W_TYPE>[0-9a-z_-]+ {
228 attr_vendor = g_strdup(yytext);
229 add_attribute(attr_name,attr_id,attr_type,attr_vendor,encrypted,has_tag,current_attr);
239 add_attribute(attr_name,attr_id,attr_type,current_vendor,encrypted,has_tag,current_attr);
242 linenums[include_stack_ptr]++;
248 add_attribute(attr_name,attr_id,attr_type,attr_vendor,encrypted,has_tag,current_attr);
252 linenums[include_stack_ptr]++;
256 <VALUE>[0-9a-z_/-]+ { attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
257 <VALUE_W_ATTR>[^[:blank:]]+ { value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
258 <VALUE_W_NAME>[0-9]+ { add_value(attr_name,value_repr, (guint32) strtoul(yytext,NULL,10)); g_free(attr_name); g_free(value_repr); BEGIN WS_OUT;}
259 <VALUE_W_NAME>0x[0-9a-f]+ { add_value(attr_name,value_repr, (guint32) strtoul(yytext,NULL,16)); g_free(attr_name); g_free(value_repr); BEGIN WS_OUT;}
261 <INCLUDE>[^[:blank:]\n]+ {
262 if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
263 g_string_append_printf(error, "$INCLUDE files nested to deeply\n");
267 include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
269 fullpaths[include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
272 yyin = ws_fopen( fullpaths[include_stack_ptr], "r" );
276 g_string_append_printf(error,
277 "Could not open file: '%s', error: %s\n",
278 fullpaths[include_stack_ptr],
281 g_string_append_printf(error,
282 "Could not open file: '%s', no errno\n",
283 fullpaths[include_stack_ptr]);
285 g_free(fullpaths[include_stack_ptr]);
286 fullpaths[include_stack_ptr] = NULL;
289 linenums[include_stack_ptr] = 1;
290 yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ) );
302 if ( --include_stack_ptr < 0 ) {
305 g_free(fullpaths[include_stack_ptr+1]);
306 fullpaths[include_stack_ptr+1] = NULL;
308 yy_delete_buffer( YY_CURRENT_BUFFER );
309 yy_switch_to_buffer(include_stack[include_stack_ptr]);
315 \n { linenums[include_stack_ptr]++; BEGIN WS_OUT; }
320 static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags) {
321 radius_vendor_info_t* v;
323 v = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id, GUINT_TO_POINTER(id));
328 * Allocate a new entry and insert it into the by-ID and
329 * by-name hash tables.
331 v = g_new(radius_vendor_info_t,1);
332 v->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
335 v->name = g_strdup(name);
336 v->type_octets = type_octets;
337 v->length_octets = length_octets;
338 v->has_flags = has_flags;
340 g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
341 g_hash_table_insert(dict->vendors_by_name, (gpointer) v->name, v);
344 * This vendor is already in the table.
346 * Assume that the dictionary knows the 'ground truth' about
347 * the type/length/has_flags information and thus allow the
348 * dictionary to overwrite these values even for vendors that
349 * have already been loaded.
351 * XXX - this could be due to the vendor being in multiple
352 * dictionary files, rather than having been specially
353 * entered by the RADIUS dissector, as a side effect of
354 * specially entering an attribute; should we report vendors
355 * that appear in different dictionaries with different
358 v->type_octets = type_octets;
359 v->length_octets = length_octets;
360 v->has_flags = has_flags;
363 * Did the name change?
365 if (g_strcmp0(v->name, name) != 0) {
367 * Yes. Remove the entry from the by-name hash table
368 * and re-insert it with the new name.
370 g_hash_table_remove(dict->vendors_by_name, (gpointer) v->name);
371 g_free((gpointer) v->name);
372 v->name = g_strdup(name);
373 g_hash_table_insert(dict->vendors_by_name, (gpointer) v->name, v);
378 static void add_attribute(const gchar* name, const gchar* codestr, radius_attr_dissector_t type, const gchar* vendor, guint encrypted_flag, gboolean tagged, const gchar* attr) {
379 radius_attr_info_t* a;
384 add_tlv(name, codestr, type, attr);
390 radius_vendor_info_t* v;
391 v = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_name,vendor);
394 g_string_append_printf(error, "Vendor: '%s', does not exist in %s:%i \n", vendor, fullpaths[include_stack_ptr], linenums[include_stack_ptr] );
398 by_id = v->attrs_by_id;
401 by_id = dict->attrs_by_id;
404 code= (guint32) strtoul(codestr, NULL, 10);
406 a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code));
411 * Allocate a new entry and insert it into the by-ID and
412 * by-name hash tables.
414 a = g_new(radius_attr_info_t,1);
416 a->name = g_strdup(name);
418 a->encrypt = encrypted_flag;
427 a->tlvs_by_id = NULL;
428 g_hash_table_insert(by_id, GUINT_TO_POINTER(code),a);
429 g_hash_table_insert(dict->attrs_by_name,(gpointer) (a->name),a);
432 * This attribute is already in the table.
434 * Overwrite the encrypted flag, tagged property, and type;
435 * the other properties don't get set until after we've
436 * finished reading the dictionaries.
438 * XXX - this could be due to the attribute being in
439 * multiple dictionary files, rather than having been
440 * specially entered by the RADIUS dissector to give it
441 * a special dissection routine; should we report attributes
442 * that appear in different dictionaries with different
445 a->encrypt = encrypted_flag;
450 * Did the name change?
452 if (g_strcmp0(a->name, name) != 0) {
454 * Yes. Remove the entry from the by-name hash table
455 * and re-insert it with the new name.
457 g_hash_table_remove(dict->attrs_by_name, (gpointer) (a->name));
458 g_free((gpointer) a->name);
459 a->name = g_strdup(name);
460 g_hash_table_insert(dict->attrs_by_name, (gpointer) (a->name),a);
465 static void add_tlv(const gchar* name, const gchar* codestr, radius_attr_dissector_t type, const gchar* attr) {
466 radius_attr_info_t* a;
467 radius_attr_info_t* s;
470 a = (radius_attr_info_t*)g_hash_table_lookup(dict->attrs_by_name, attr);
473 g_string_append_printf(error, "Attr: '%s', does not exist in %s:%i \n", attr, fullpaths[include_stack_ptr], linenums[include_stack_ptr]);
478 if (type == radius_tlv) {
479 g_string_append_printf(error, "sub-TLV: '%s', sub-TLV's type is specified as tlv in %s:%i \n", name, fullpaths[include_stack_ptr], linenums[include_stack_ptr]);
485 if (! a->tlvs_by_id) {
486 a->tlvs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
489 code = (guint32) strtoul(codestr, NULL, 10);
491 s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code));
495 * This TLV doesn't yet exist in this attribute's TLVs-by-ID
496 * hash table. Add it.
498 s = g_new(radius_attr_info_t,1);
499 s->name = g_strdup(name);
512 s->tlvs_by_id = NULL;
514 g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code),s);
515 g_hash_table_insert(dict->tlvs_by_name,(gpointer) (s->name),s);
519 * If it *does* exist, leave it alone; there shouldn't be duplicate
520 * entries by name in the dictionaries (even if there might be
521 * multiple entries for a given attribute in the dictionaries, each
522 * one adding some TLV values), and we don't directly add entries
523 * for TLVs in the RADIUS dissector.
525 * XXX - report the duplicate entries?
529 void add_value(const gchar* attrib_name, const gchar* repr, guint32 value) {
531 GArray* a = (GArray*)g_hash_table_lookup(value_strings,attrib_name);
534 a = g_array_new(TRUE,TRUE,sizeof(value_string));
535 g_hash_table_insert(value_strings,g_strdup(attrib_name),a);
539 v.strptr = g_strdup(repr);
541 g_array_append_val(a,v);
544 static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p _U_) {
545 radius_attr_info_t* s = (radius_attr_info_t*)v;
553 if (g_hash_table_lookup_extended(value_strings, s->name, &key, &vs.p)) {
554 s->vs = (value_string*)(void *)vs.a->data;
555 g_array_free(vs.a, FALSE);
556 g_hash_table_remove(value_strings, key);
561 static void setup_attrs(gpointer k _U_, gpointer v, gpointer p _U_) {
562 radius_attr_info_t* a = (radius_attr_info_t*)v;
570 if (g_hash_table_lookup_extended(value_strings,a->name,&key,&vs.p) ) {
571 a->vs = (value_string*)(void *)vs.a->data;
572 g_array_free(vs.a,FALSE);
573 g_hash_table_remove(value_strings,key);
578 g_hash_table_foreach(a->tlvs_by_id, setup_tlvs, p);
582 static void setup_vendors(gpointer k _U_, gpointer v, gpointer p) {
583 radius_vendor_info_t* vnd = (radius_vendor_info_t*)v;
585 g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p);
588 static gboolean destroy_value_strings(gpointer k, gpointer v, gpointer p _U_) {
589 value_string* vs = (value_string*)(void *)(((GArray*)v)->data);
593 for (;vs->strptr;vs++) {
594 g_free((void*)vs->strptr);
597 g_array_free((GArray*)v,TRUE);
601 gboolean radius_load_dictionary (radius_dictionary_t* d, gchar* dir, const gchar* filename, gchar** err_str) {
607 fullpaths[include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
610 error = g_string_new("");
612 yyin = ws_fopen(fullpaths[include_stack_ptr],"r");
615 g_string_append_printf(error, "Could not open file: '%s', error: %s\n", fullpaths[include_stack_ptr], g_strerror(errno) );
616 g_free(fullpaths[include_stack_ptr]);
617 *err_str = g_string_free(error,FALSE);
621 value_strings = g_hash_table_new(g_str_hash,g_str_equal);
627 if (yyin != NULL) fclose(yyin);
630 for (i=0; i < 10; i++) {
631 if (fullpaths[i]) g_free(fullpaths[i]);
634 g_hash_table_foreach(dict->attrs_by_id,setup_attrs,NULL);
635 g_hash_table_foreach(dict->vendors_by_id,setup_vendors,NULL);
636 g_hash_table_foreach_remove(value_strings,destroy_value_strings,NULL);
638 if (error->len > 0) {
639 *err_str = g_string_free(error,FALSE);
643 g_string_free(error,TRUE);
649 * Editor modelines - http://www.wireshark.org/tools/modelines.html
654 * indent-tabs-mode: t
657 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
658 * :indentSize=8:tabSize=8:noTabs=false: