pytest.ini: declare minimum version requirement
[metze/wireshark/wip.git] / epan / radius_dict.l
index 91cfbca507a0319524840f9ddcd44672325e3954..1313dfb8c28cb79fb6d03999984f9be9ff82ac40 100644 (file)
@@ -1,3 +1,13 @@
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+}
+
+/*
+ * We want a reentrant scanner.
+ */
+%option reentrant
+
 /*
  * We don't use input, so don't generate code for it.
  */
 %option nounput
 
 /*
- * We don't read from the terminal.
+ * We don't read interactively from the terminal.
  */
 %option never-interactive
 
+/*
+ * We want to stop processing when we get to the end of the input.
+ */
+%option noyywrap
+
 /*
  * The language we're scanning is case-insensitive.
  */
 %option caseless
 
 /*
- * Prefix scanner routines with "Radius" rather than "yy", so this scanner
+ * The type for the state we keep for a scanner.
+ */
+%option extra-type="Radius_scanner_state_t*"
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+/*
+ * Prefix scanner routines with "Radius_" rather than "yy", so this scanner
  * can coexist with other scanners.
  */
-%option prefix="Radius"
+%option prefix="Radius_"
 
 %option outfile="radius_dict.c"
 
@@ -50,8 +83,6 @@
        * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
        */
 
-#include "config.h"
-
 #include <glib.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <epan/packet.h>
 #include <epan/dissectors/packet-radius.h>
-#include "radius_dict_lex.h"
 #include <wsutil/file_util.h>
 
-#ifdef _WIN32
-/* disable Windows VC compiler warning "signed/unsigned mismatch" associated  */
-/* with YY_INPUT code generated by flex versions such as 2.5.35.              */
-#pragma warning (disable:4018)
-#endif
+/*
+ * Disable diagnostics in the code generated by Flex.
+ */
+DIAG_OFF_FLEX
+
+/*
+ * See
+ *
+ *     http://freeradius.org/radiusd/man/dictionary.html
+ *
+ * for the format of RADIUS dictionary files.
+ *
+ * XXX - features not currently supported:
+ *
+ *     integer64, ipv4prefix, combo-prefix, bool, size, decimal,
+ *     timeval, struct, extended, long-extended, vsa, evs, vendor,
+ *     cidr, uint{8,16,32,64}, int{8,16,32,64} as attribute types
+ *     (some of these aren't documented);
+ *
+ *     octets[N], where N is an integer, as an attribute type
+ *     (not documented in the man page);
+ *
+ *     internal, array, concat, and virtual as attribute flags (not
+ *     documented in the man page);
+ *
+ * We should, perhaps, adopt FreeRADIUS's dictionary-parsing code in
+ * src/lib/dict.c and use that, rather than writing our own parser.
+ * See bug 13176.
+ */
+#define YY_USER_INIT BEGIN WS_OUT;
 
 #define ECHO
 #define MAX_INCLUDE_DEPTH 10
 
-       static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags);
-       static void add_value(const gchar* attrib_name,const  gchar* repr, guint32 value);
-       static void add_tlv(const gchar* name, const  gchar* code, radius_attr_dissector_t type, const gchar* attr);
-       static void add_attribute(const gchar*,const  gchar*, radius_attr_dissector_t,const  gchar*, guint, gboolean, const gchar*);
-
-       static YY_BUFFER_STATE include_stack[10];
-       static int include_stack_ptr = 0;
-
-       static radius_dictionary_t* dict = NULL;
-       static GHashTable* value_strings = NULL; /* GArray(value_string) by attribute name */
-
-       static gchar* attr_name = NULL;
-       static gchar* attr_id = NULL;
-       static radius_attr_dissector_t* attr_type = NULL;
-       static gchar* attr_vendor = NULL;
-       static gchar* vendor_name = NULL;
-       static guint32 vendor_id = 0;
-       static guint vendor_type_octets = 1;
-       static guint vendor_length_octets = 1;
-       static gboolean vendor_has_flags = FALSE;
-       static gchar* value_repr = NULL;
-       static guint encrypted = 0;
-       static gboolean has_tag = FALSE;
-       static gchar* current_vendor = NULL;
-       static gchar* current_attr = NULL;
-
-       static GString* error = NULL;
-       static gchar* directory = NULL;
-       static int linenums[] = {1,1,1,1,1,1,1,1,1,1};
-       static gchar* fullpaths[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+typedef struct {
+       YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+       int include_stack_ptr;
+
+       radius_dictionary_t* dict;
+       GHashTable* value_strings; /* GArray(value_string) by attribute name */
+
+       gchar* attr_name;
+       gchar* attr_id;
+       radius_attr_dissector_t* attr_type;
+       gchar* attr_vendor;
+       gchar* vendor_name;
+       guint32 vendor_id;
+       guint vendor_type_octets;
+       guint vendor_length_octets;
+       gboolean vendor_has_flags;
+       gchar* value_repr;
+       guint encrypted;
+       gboolean has_tag;
+       gchar* current_vendor;
+       guint current_vendor_evs_type;
+       gchar* current_attr;
+
+       gchar* directory;
+       gchar* fullpaths[MAX_INCLUDE_DEPTH];
+       int linenums[MAX_INCLUDE_DEPTH];
+
+       GString* error;
+} Radius_scanner_state_t;
+
+static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags);
+static gboolean add_attribute(Radius_scanner_state_t* state, const gchar*,const  gchar*, radius_attr_dissector_t,const  gchar*, guint, gboolean, const gchar*);
+static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const  gchar* code, radius_attr_dissector_t type, const gchar* attr);
+static void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const  gchar* repr, guint32 value);
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define Radius_alloc(size, yyscanner)          (void *)malloc(size)
+#define Radius_realloc(ptr, size, yyscanner)   (void *)realloc((char *)(ptr), (size))
+#define Radius_free(ptr, yyscanner)            free((char *)ptr)
 
 %}
 
  *       name types in the FreeRadius dictionaries.
  */
 
-%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
+%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
 %%
 [:blank:]   ;
 #[^\n]*                ;
 <WS_OUT>END-TLV { BEGIN END_TLV; }
 
 <BEGIN_VENDOR>[0-9a-z_-]+ {
-    if (current_vendor) {
-        g_free(current_vendor);
+    if (yyextra->current_vendor) {
+        g_free(yyextra->current_vendor);
+    }
+    yyextra->current_vendor = g_strdup(yytext);
+    BEGIN BEGIN_VENDOR_FORMAT;
+}
+<BEGIN_VENDOR_FORMAT>format=Extended-Vendor-Specific-[123456] {
+    if (strcmp(yytext, "format=Extended-Vendor-Specific-1") == 0) {
+        yyextra->current_vendor_evs_type = 241;
+    } else if(strcmp(yytext, "format=Extended-Vendor-Specific-2") == 0) {
+        yyextra->current_vendor_evs_type = 242;
+    } else if(strcmp(yytext, "format=Extended-Vendor-Specific-3") == 0) {
+        yyextra->current_vendor_evs_type = 243;
+    } else if(strcmp(yytext, "format=Extended-Vendor-Specific-4") == 0) {
+        yyextra->current_vendor_evs_type = 244;
+    } else if(strcmp(yytext, "format=Extended-Vendor-Specific-5") == 0) {
+        yyextra->current_vendor_evs_type = 245;
+    } else if(strcmp(yytext, "format=Extended-Vendor-Specific-6") == 0) {
+        yyextra->current_vendor_evs_type = 246;
     }
-    current_vendor = g_strdup(yytext);
     BEGIN WS_OUT;
 }
+<BEGIN_VENDOR_FORMAT>\n {BEGIN WS_OUT;}
+
 <END_VENDOR>[^\n]* {
-    if (current_vendor) {
-        g_free(current_vendor);
-        current_vendor = NULL;
+    if (yyextra->current_vendor) {
+        g_free(yyextra->current_vendor);
+        yyextra->current_vendor = NULL;
     }
+    yyextra->current_vendor_evs_type = 0;
     BEGIN WS_OUT;
 }
 
 <BEGIN_TLV>[0-9a-z_-]+ {
-    if (current_attr) {
-        g_free(current_attr);
+    if (yyextra->current_attr) {
+        g_free(yyextra->current_attr);
     }
-    current_attr = g_strdup(yytext);
+    yyextra->current_attr = g_strdup(yytext);
     BEGIN WS_OUT;
 }
 <END_TLV>[^\n]* {
-    if (current_attr) {
-        g_free(current_attr);
-        current_attr = NULL;
+    if (yyextra->current_attr) {
+        g_free(yyextra->current_attr);
+        yyextra->current_attr = NULL;
     }
     BEGIN WS_OUT;
 }
 
 <VENDOR>[0-9a-z_-]+   {
-    vendor_name = g_strdup(yytext);
-    vendor_type_octets = 1;
-    vendor_length_octets = 1;
-    vendor_has_flags = FALSE;
+    yyextra->vendor_name = g_strdup(yytext);
+    yyextra->vendor_type_octets = 1;
+    yyextra->vendor_length_octets = 1;
+    yyextra->vendor_has_flags = FALSE;
     BEGIN VENDOR_W_NAME;
 }
 <VENDOR_W_NAME>[0-9]+   {
-    vendor_id = (guint32) strtoul(yytext,NULL,10);
+    yyextra->vendor_id = (guint32) strtoul(yytext,NULL,10);
     BEGIN VENDOR_W_ID;
 }
 <VENDOR_W_NAME>0x[0-9a-f]+   {
-    vendor_id = (guint32) strtoul(yytext,NULL,16);
+    yyextra->vendor_id = (guint32) strtoul(yytext,NULL,16);
     BEGIN VENDOR_W_ID;
 }
 <VENDOR_W_ID>format= {
     BEGIN VENDOR_W_FORMAT;
 }
 <VENDOR_W_FORMAT>[124] {
-    vendor_type_octets = (guint) strtoul(yytext,NULL,10);
+    yyextra->vendor_type_octets = (guint) strtoul(yytext,NULL,10);
     BEGIN VENDOR_W_TYPE_OCTETS;
 }
 <VENDOR_W_TYPE_OCTETS>,[012] {
-    vendor_length_octets = (guint) strtoul(yytext+1,NULL,10);
+    yyextra->vendor_length_octets = (guint) strtoul(yytext+1,NULL,10);
     BEGIN VENDOR_W_LENGTH_OCTETS;
 }
 <VENDOR_W_LENGTH_OCTETS>,c {
-    vendor_has_flags = TRUE;
+    yyextra->vendor_has_flags = TRUE;
     BEGIN VENDOR_W_CONTINUATION;
 }
 <VENDOR_W_FORMAT>\n |
 <VENDOR_W_LENGTH_OCTETS>\n |
 <VENDOR_W_CONTINUATION>\n |
 <VENDOR_W_ID>\n {
-    add_vendor(vendor_name, vendor_id, vendor_type_octets, vendor_length_octets, vendor_has_flags);
-    g_free(vendor_name);
+    add_vendor(yyextra, yyextra->vendor_name, yyextra->vendor_id, yyextra->vendor_type_octets, yyextra->vendor_length_octets, yyextra->vendor_has_flags);
+    g_free(yyextra->vendor_name);
     BEGIN WS_OUT;
 }
 
-<ATTR>[0-9a-z_/.-]+                    { attr_name = g_strdup(yytext); encrypted = 0; has_tag = FALSE; BEGIN ATTR_W_NAME; }
-<ATTR_W_NAME>[0-9]+                    { attr_id = g_strdup(yytext);  BEGIN ATTR_W_ID;}
-<ATTR_W_NAME>0x[0-9a-f]+               { attr_id = g_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
-<ATTR_W_ID>integer                     { attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>string                      { attr_type = radius_string;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>octets                      { attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>ipaddr                      { attr_type = radius_ipaddr;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>ipv6addr                    { attr_type = radius_ipv6addr;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>ipv6prefix                  { attr_type = radius_ipv6prefix;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>ipxnet                      { attr_type = radius_ipxnet;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>date                                { attr_type = radius_date;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>abinary                     { attr_type = radius_abinary;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>ether                       { attr_type = radius_ether;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>ifid                                { attr_type = radius_ifid;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>byte                                { attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>short                       { attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>signed                      { attr_type = radius_signed;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>combo-ip                    { attr_type = radius_combo_ip;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>tlv                         { attr_type = radius_tlv;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_ID>[0-9a-z_-]+                 { attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
-<ATTR_W_TYPE>has_tag[,]?               { has_tag = TRUE; }
-<ATTR_W_TYPE>encrypt=[123][,]?         { encrypted = (guint) strtoul(yytext+8,NULL,10); }
+<ATTR>[0-9a-z_/.-]+                    { yyextra->attr_name = g_strdup(yytext); yyextra->encrypted = 0; yyextra->has_tag = FALSE; BEGIN ATTR_W_NAME; }
+<ATTR_W_NAME>[0-9.]+                   { yyextra->attr_id = g_strdup(yytext);  BEGIN ATTR_W_ID;}
+<ATTR_W_NAME>0x[0-9a-f]+               { yyextra->attr_id = g_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;}
+<ATTR_W_ID>integer                     { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>string                      { yyextra->attr_type = radius_string;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>octets                      { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>ipaddr                      { yyextra->attr_type = radius_ipaddr;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>ipv6addr                    { yyextra->attr_type = radius_ipv6addr;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>ipv6prefix                  { yyextra->attr_type = radius_ipv6prefix;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>ipxnet                      { yyextra->attr_type = radius_ipxnet;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>date                                { yyextra->attr_type = radius_date;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>abinary                     { yyextra->attr_type = radius_abinary;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>ether                       { yyextra->attr_type = radius_ether;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>ifid                                { yyextra->attr_type = radius_ifid;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>byte                                { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>short                       { yyextra->attr_type = radius_integer;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>signed                      { yyextra->attr_type = radius_signed;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>combo-ip                    { yyextra->attr_type = radius_combo_ip;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>tlv                         { yyextra->attr_type = radius_tlv;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_ID>[0-9a-z_-]+                 { yyextra->attr_type = radius_octets;  BEGIN ATTR_W_TYPE; }
+<ATTR_W_TYPE>has_tag[,]?               { yyextra->has_tag = TRUE; }
+<ATTR_W_TYPE>encrypt=[123][,]?         { yyextra->encrypted = (guint) strtoul(yytext+8,NULL,10); }
 <ATTR_W_TYPE>[0-9a-z_-]+=([^\n]*)      ;
 <ATTR_W_TYPE>[0-9a-z_-]+               {
-    attr_vendor = g_strdup(yytext);
-    add_attribute(attr_name,attr_id,attr_type,attr_vendor,encrypted,has_tag,current_attr);
-    g_free(attr_id);
-    g_free(attr_vendor);
-    g_free(attr_name);
-    attr_id = NULL;
-    attr_vendor = NULL;
-    attr_name = NULL;
-    BEGIN WS_OUT;
+    /*
+     * Support for "ATTRIBUTE name oid type vendor", where the token
+     * following the type matches neither has_tag nor encrypt={1,2,3},
+     * but is a sequence of digits, lower-case letters, underscores,
+     * and hyphens.
+     *
+     * We mark this as a vendor-specific attribute (VSA), with the token
+     * following the type being the vendor name; this notation is deprecated
+     * in favor of BEGIN-VENDOR/END-VENDOR blocks.
+     */
+    gboolean attribute_ok;
+
+    yyextra->attr_vendor = g_strdup(yytext);
+    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);
+    g_free(yyextra->attr_id);
+    g_free(yyextra->attr_vendor);
+    g_free(yyextra->attr_name);
+    yyextra->attr_id = NULL;
+    yyextra->attr_vendor = NULL;
+    yyextra->attr_name = NULL;
+    if (attribute_ok)
+        BEGIN WS_OUT;
+    else
+        BEGIN JUNK;
 }
 <ATTR_W_TYPE>\n                                                {
-    add_attribute(attr_name,attr_id,attr_type,current_vendor,encrypted,has_tag,current_attr);
-    g_free(attr_id);
-    g_free(attr_name);
-    linenums[include_stack_ptr]++;
-    has_tag = FALSE;
-    encrypted=FALSE;
+    add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->current_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr);
+    g_free(yyextra->attr_id);
+    g_free(yyextra->attr_name);
+    yyextra->linenums[yyextra->include_stack_ptr]++;
+    yyextra->has_tag = FALSE;
+    yyextra->encrypted=FALSE;
     BEGIN WS_OUT;
 }
-<ATTR_W_VENDOR>\n                                      {
-    add_attribute(attr_name,attr_id,attr_type,attr_vendor,encrypted,has_tag,current_attr);
-    g_free(attr_id);
-    g_free(attr_vendor);
-    g_free(attr_name);
-    linenums[include_stack_ptr]++;
-    BEGIN WS_OUT;
-};
 
-<VALUE>[0-9a-z_/-]+                            { attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
-<VALUE_W_ATTR>[^[:blank:]]+                    { value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
-<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;}
-<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;}
+<VALUE>[0-9a-z_/-]+                            { yyextra->attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; }
+<VALUE_W_ATTR>[^[:blank:]]+                    { yyextra->value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; }
+<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;}
+<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;}
 
 <INCLUDE>[^[:blank:]\n]+   {
-       if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
-               g_string_append_printf(error, "$INCLUDE files nested to deeply\n");
+       if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
+               g_string_append_printf(yyextra->error, "$INCLUDE files nested too deeply\n");
                yyterminate();
        }
 
-       include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
+       yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER;
 
-       fullpaths[include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
-           directory,yytext);
+       yyextra->fullpaths[yyextra->include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
+           yyextra->directory,yytext);
 
-       yyin = ws_fopen( fullpaths[include_stack_ptr], "r" );
+       yyin = ws_fopen( yyextra->fullpaths[yyextra->include_stack_ptr], "r" );
 
        if (!yyin) {
                if (errno) {
-                       g_string_append_printf(error,
+                       g_string_append_printf(yyextra->error,
                                        "Could not open file: '%s', error: %s\n",
-                                       fullpaths[include_stack_ptr],
+                                       yyextra->fullpaths[yyextra->include_stack_ptr],
                                        g_strerror(errno) );
                } else {
-                       g_string_append_printf(error,
+                       g_string_append_printf(yyextra->error,
                                        "Could not open file: '%s', no errno\n",
-                                       fullpaths[include_stack_ptr]);
+                                       yyextra->fullpaths[yyextra->include_stack_ptr]);
                }
-               g_free(fullpaths[include_stack_ptr]);
-               fullpaths[include_stack_ptr] = NULL;
-               include_stack_ptr--;
+               g_free(yyextra->fullpaths[yyextra->include_stack_ptr]);
+               yyextra->fullpaths[yyextra->include_stack_ptr] = NULL;
+               yyextra->include_stack_ptr--;
        } else {
-               linenums[include_stack_ptr] = 1;
-               yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ) );
+               yyextra->linenums[yyextra->include_stack_ptr] = 1;
+               yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner);
        }
 
 
        fclose(yyin);
        yyin = NULL;
 
-       if ( --include_stack_ptr < 0 ) {
+       if ( --yyextra->include_stack_ptr < 0 ) {
                yyterminate();
        } else {
-               g_free(fullpaths[include_stack_ptr+1]);
-               fullpaths[include_stack_ptr+1] = NULL;
+               g_free(yyextra->fullpaths[yyextra->include_stack_ptr+1]);
+               yyextra->fullpaths[yyextra->include_stack_ptr+1] = NULL;
 
-               yy_delete_buffer( YY_CURRENT_BUFFER );
-               yy_switch_to_buffer(include_stack[include_stack_ptr]);
+               Radius__delete_buffer(YY_CURRENT_BUFFER, yyscanner);
+               Radius__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner);
        }
 
        BEGIN WS_OUT;
 }
 
-\n     { linenums[include_stack_ptr]++; BEGIN WS_OUT; }
+\n     { yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; }
 
 
 %%
 
-static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags) {
+/*
+ * Turn diagnostics back on, so we check the code that we've written.
+ */
+DIAG_ON_FLEX
+
+static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags) {
        radius_vendor_info_t* v;
 
-       v = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id, GUINT_TO_POINTER(id));
+       v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_id, GUINT_TO_POINTER(id));
 
        if (!v) {
                /*
@@ -324,7 +426,7 @@ static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint l
                 * by-name hash tables.
                 */
                v = g_new(radius_vendor_info_t,1);
-               v->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
+               v->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
                v->code = id;
                v->ett = -1;
                v->name = g_strdup(name);
@@ -332,8 +434,8 @@ static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint l
                v->length_octets = length_octets;
                v->has_flags = has_flags;
 
-               g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
-               g_hash_table_insert(dict->vendors_by_name, (gpointer) v->name, v);
+               g_hash_table_insert(state->dict->vendors_by_id,GUINT_TO_POINTER(v->code),v);
+               g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v);
        } else {
                /*
                 * This vendor is already in the table.
@@ -342,6 +444,13 @@ static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint l
                 * the type/length/has_flags information and thus allow the
                 * dictionary to overwrite these values even for vendors that
                 * have already been loaded.
+                *
+                * XXX - this could be due to the vendor being in multiple
+                * dictionary files, rather than having been specially
+                * entered by the RADIUS dissector, as a side effect of
+                * specially entering an attribute; should we report vendors
+                * that appear in different dictionaries with different
+                * properties?
                 */
                v->type_octets = type_octets;
                v->length_octets = length_octets;
@@ -355,142 +464,192 @@ static void add_vendor(const gchar* name, guint32 id, guint type_octets, guint l
                         * Yes.  Remove the entry from the by-name hash table
                         * and re-insert it with the new name.
                         */
-                       g_hash_table_remove(dict->vendors_by_name, (gpointer) v->name);
+                       g_hash_table_remove(state->dict->vendors_by_name, (gpointer) v->name);
                        g_free((gpointer) v->name);
                        v->name = g_strdup(name);
-                       g_hash_table_insert(dict->vendors_by_name, (gpointer) v->name, v);
+                       g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v);
                }
        }
 }
 
-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) {
+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) {
        radius_attr_info_t* a;
        GHashTable* by_id;
-       guint32 code;
-       const gchar *tmpName = NULL;
-
+       radius_attr_type_t code;
+       guint8 code0 = 0, code1 = 0;
+       gchar *dot, *buf = NULL;
 
        if (attr){
-               add_tlv(name, codestr, type, attr);
-               return;
+               return add_tlv(state, name, codestr, type, attr);
        }
 
+       buf = g_strdup(codestr);
+       dot = strchr(codestr, '.');
+       if (dot)
+               *dot = '\0';
+       code0 = (guint8) strtoul(buf, NULL, 10);
+       if (dot)
+               code1 = (guint8) strtoul(dot + 1, NULL, 10);
+       g_free(buf);
 
+       memset(&code, 0, sizeof(code));
        if (vendor) {
-               radius_vendor_info_t* v;
-               v = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_name,vendor);
+               if (state->current_vendor_evs_type) {
+                       code.u8_code[0] = (guint8) state->current_vendor_evs_type;
+                       code.u8_code[1] = code0;
+               } else {
+                       code.u8_code[0] = code0;
+                       code.u8_code[1] = 0;
+               }
 
+               radius_vendor_info_t* v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_name,vendor);
                if (! v) {
-                       g_string_append_printf(error, "Vendor: '%s', does not exist in %s:%i \n", vendor, fullpaths[include_stack_ptr], linenums[include_stack_ptr] );
-                       BEGIN JUNK;
-                       return;
+                       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] );
+                       return FALSE;
                } else {
                        by_id = v->attrs_by_id;
                }
        } else {
-               by_id = dict->attrs_by_id;
-       }
+               code.u8_code[0] = code0;
+               code.u8_code[1] = code1;
 
-       code= (guint32) strtoul(codestr, NULL, 10);
+               by_id = state->dict->attrs_by_id;
+       }
 
-       a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code));
+       a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code.value));
 
        if (!a) {
+               /*
+                * New attribute.
+                * Allocate a new entry and insert it into the by-ID and
+                * by-name hash tables.
+                */
                a = g_new(radius_attr_info_t,1);
-               a->name = NULL;
+               a->code = code;
+               a->name = g_strdup(name);
                a->dissector = NULL;
-       }
-
-       a->code = code;
-       a->encrypt = encrypted_flag;
-       a->tagged =  tagged;
-       a->type = type;
-       a->vs = NULL;
-       a->hf = -1;
-       a->hf_alt = -1;
-       a->hf_tag = -1;
-       a->hf_len = -1;
-       a->ett = -1;
-       a->tlvs_by_id = NULL;
-
-       if (a->name) {
-               tmpName = a->name;
-       }
-       a->name = g_strdup(name);
-
-       g_hash_table_insert(by_id, GUINT_TO_POINTER(code),a);
-       g_hash_table_insert(dict->attrs_by_name,(gpointer) (a->name),a);
+               a->encrypt = encrypted_flag;
+               a->tagged =  tagged;
+               a->type = type;
+               a->vs = NULL;
+               a->hf = -1;
+               a->hf_alt = -1;
+               a->hf_tag = -1;
+               a->hf_len = -1;
+               a->ett = -1;
+               a->tlvs_by_id = NULL;
+               g_hash_table_insert(by_id, GUINT_TO_POINTER(code.value),a);
+               g_hash_table_insert(state->dict->attrs_by_name,(gpointer) (a->name),a);
+       } else {
+               /*
+                * This attribute is already in the table.
+                *
+                * Overwrite the encrypted flag, tagged property, and type;
+                * the other properties don't get set until after we've
+                * finished reading the dictionaries.
+                *
+                * XXX - this could be due to the attribute being in
+                * multiple dictionary files, rather than having been
+                * specially entered by the RADIUS dissector to give it
+                * a special dissection routine; should we report attributes
+                * that appear in different dictionaries with different
+                * properties?
+                */
+               a->encrypt = encrypted_flag;
+               a->tagged =  tagged;
+               a->type = type;
 
-       /* Don't free the old name until after the hash_table ops, since it
-          seems to end up being used in there somewhere, causing valgrind
-          errors. https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=7803 */
-       if (tmpName) {
-               g_free((gpointer) tmpName);
+               /*
+                * Did the name change?
+                */
+               if (g_strcmp0(a->name, name) != 0) {
+                       /*
+                        * Yes.  Steal the entry from the by-name hash table
+                        * and re-insert it with the new name.  (Don't
+                        * remove it - that calls the free routine, which
+                        * frees up the entry.)
+                        */
+                       g_hash_table_steal(state->dict->attrs_by_name, (gpointer) (a->name));
+                       g_free((gpointer) a->name);
+                       a->name = g_strdup(name);
+                       g_hash_table_insert(state->dict->attrs_by_name, (gpointer) (a->name),a);
+               }
        }
+       return TRUE;
 }
 
-static void add_tlv(const gchar* name, const  gchar* codestr, radius_attr_dissector_t type, const gchar* attr) {
+static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const  gchar* codestr, radius_attr_dissector_t type, const gchar* attr) {
        radius_attr_info_t* a;
        radius_attr_info_t* s;
-       guint32 code;
+       radius_attr_type_t code;
 
-       a = (radius_attr_info_t*)g_hash_table_lookup(dict->attrs_by_name, attr);
+       a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->attrs_by_name, attr);
 
        if (! a) {
-               g_string_append_printf(error, "Attr: '%s', does not exist in %s:%i \n", attr, fullpaths[include_stack_ptr], linenums[include_stack_ptr]);
-               BEGIN JUNK;
-               return;
+               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]);
+               return FALSE;
        }
 
        if (type == radius_tlv) {
-               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]);
-               BEGIN JUNK;
-               return;
+               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]);
+               return FALSE;
        }
 
 
        if (! a->tlvs_by_id) {
-               a->tlvs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
+               a->tlvs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
        }
 
-       code = (guint32) strtoul(codestr, NULL, 10);
+       memset(&code, 0, sizeof(code));
+       code.u8_code[0] = (guint8) strtoul(codestr, NULL, 10);
 
-       s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code));
+       s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code.value));
 
        if (!s) {
+               /*
+                * This TLV doesn't yet exist in this attribute's TLVs-by-ID
+                * hash table.  Add it.
+                */
                s = g_new(radius_attr_info_t,1);
-               s->name = NULL;
+               s->name = g_strdup(name);
                s->dissector = NULL;
+               s->code = code;
+               s->type = type;
+               s->encrypt = FALSE;
+               s->tagged = FALSE;
+               s->dissector = NULL;
+               s->vs = NULL;
+               s->hf = -1;
+               s->hf_alt = -1;
+               s->hf_tag = -1;
+               s->hf_len = -1;
+               s->ett = -1;
+               s->tlvs_by_id = NULL;
+
+               g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code.value),s);
+               g_hash_table_insert(state->dict->tlvs_by_name,(gpointer) (s->name),s);
        }
 
-       s->code = code;
-       s->type = type;
-       s->encrypt = FALSE;
-       s->tagged = FALSE;
-       s->dissector = NULL;
-       s->vs = NULL;
-       s->hf = -1;
-       s->hf_alt = -1;
-       s->hf_tag = -1;
-       s->hf_len = -1;
-       s->ett = -1;
-       s->tlvs_by_id = NULL;
-
-       if (s->name)
-               g_free((gpointer) s->name);
-       s->name = g_strdup(name);
-
-       g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code),s);
-       g_hash_table_insert(dict->tlvs_by_name,(gpointer) (s->name),s);
+       /*
+        * If it *does* exist, leave it alone; there shouldn't be duplicate
+        * entries by name in the dictionaries (even if there might be
+        * multiple entries for a given attribute in the dictionaries, each
+        * one adding some TLV values), and we don't directly add entries
+        * for TLVs in the RADIUS dissector.
+        *
+        * XXX - report the duplicate entries?
+        */
+       return TRUE;
 }
 
-void add_value(const gchar* attrib_name, const gchar* repr, guint32 value) {
+void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const gchar* repr, guint32 value) {
        value_string v;
-       GArray* a = (GArray*)g_hash_table_lookup(value_strings,attrib_name);
+       GArray* a = (GArray*)g_hash_table_lookup(state->value_strings,attrib_name);
 
        if (! a) {
-               a = g_array_new(TRUE,TRUE,sizeof(value_string));
-               g_hash_table_insert(value_strings,g_strdup(attrib_name),a);
+               /* Ensure that the array is zero terminated. */
+               a = g_array_new(TRUE, TRUE, sizeof(value_string));
+               g_hash_table_insert(state->value_strings, g_strdup(attrib_name), a);
        }
 
        v.value = value;
@@ -499,8 +658,9 @@ void add_value(const gchar* attrib_name, const gchar* repr, guint32 value) {
        g_array_append_val(a,v);
 }
 
-static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p _U_) {
+static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p) {
        radius_attr_info_t* s = (radius_attr_info_t*)v;
+       Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
        gpointer key;
 
        union {
@@ -508,16 +668,16 @@ static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p _U_) {
                gpointer p;
        } vs;
 
-       if (g_hash_table_lookup_extended(value_strings, s->name, &key, &vs.p)) {
-               s->vs = (value_string*)(void *)vs.a->data;
-               g_array_free(vs.a, FALSE);
-               g_hash_table_remove(value_strings, key);
+       if (g_hash_table_lookup_extended(state->value_strings, s->name, &key, &vs.p)) {
+               g_hash_table_steal(state->value_strings, key);
+               s->vs = (value_string*)(void *)g_array_free(vs.a, FALSE);
                g_free(key);
        }
 }
 
-static void setup_attrs(gpointer k _U_, gpointer v, gpointer p _U_) {
+static void setup_attrs(gpointer k _U_, gpointer v, gpointer p) {
        radius_attr_info_t* a = (radius_attr_info_t*)v;
+       Radius_scanner_state_t* state = (Radius_scanner_state_t*)p;
        gpointer key;
 
        union {
@@ -525,10 +685,9 @@ static void setup_attrs(gpointer k _U_, gpointer v, gpointer p _U_) {
                gpointer p;
        } vs;
 
-       if (g_hash_table_lookup_extended(value_strings,a->name,&key,&vs.p) ) {
-               a->vs = (value_string*)(void *)vs.a->data;
-               g_array_free(vs.a,FALSE);
-               g_hash_table_remove(value_strings,key);
+       if (g_hash_table_lookup_extended(state->value_strings, a->name, &key, &vs.p) ) {
+               g_hash_table_steal(state->value_strings, key);
+               a->vs = (value_string*)(void *)g_array_free(vs.a, FALSE);
                g_free(key);
        }
 
@@ -543,79 +702,106 @@ static void setup_vendors(gpointer k _U_, gpointer v, gpointer p) {
        g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p);
 }
 
-static gboolean destroy_value_strings(gpointer k, gpointer v, gpointer p _U_) {
+static void destroy_value_strings(gpointer v) {
        value_string* vs = (value_string*)(void *)(((GArray*)v)->data);
 
-       g_free(k);
-
        for (;vs->strptr;vs++) {
                g_free((void*)vs->strptr);
        }
 
        g_array_free((GArray*)v,TRUE);
-       return TRUE;
 }
 
 gboolean radius_load_dictionary (radius_dictionary_t* d, gchar* dir, const gchar* filename, gchar** err_str) {
+       FILE *in;
+       yyscan_t scanner;
+       Radius_scanner_state_t state;
        int i;
 
-       dict = d;
-       directory = dir;
+       state.include_stack_ptr = 0;
+
+       state.dict = d;
+       state.value_strings = NULL;
+
+       state.attr_name = NULL;
+       state.attr_id = NULL;
+       state.attr_type = NULL;
+       state.attr_vendor = NULL;
+       state.vendor_name = NULL;
+       state.vendor_id = 0;
+       state.vendor_type_octets = 1;
+       state.vendor_length_octets = 1;
+       state.vendor_has_flags = FALSE;
+       state.value_repr = NULL;
+       state.encrypted = 0;
+       state.has_tag = FALSE;
+       state.current_vendor = NULL;
+       state.current_vendor_evs_type = 0;
+       state.current_attr = NULL;
+
+       state.directory = dir;
+
+       state.fullpaths[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
+           state.directory,filename);
+       state.linenums[0] = 1;
+       for (i = 1; i < MAX_INCLUDE_DEPTH; i++) {
+               state.fullpaths[i] = NULL;
+               state.linenums[i] = 1;
+       }
 
-       fullpaths[include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
-           directory,filename);
+       state.error = g_string_new("");
 
-       error = g_string_new("");
+       in = ws_fopen(state.fullpaths[0],"r");
 
-       yyin = ws_fopen(fullpaths[include_stack_ptr],"r");
+       if (!in) {
+               g_string_append_printf(state.error, "Could not open file: '%s', error: %s\n", state.fullpaths[0], g_strerror(errno));
+               g_free(state.fullpaths[0]);
+               *err_str = g_string_free(state.error,FALSE);
+               return FALSE;
+       }
 
-       if (!yyin) {
-               g_string_append_printf(error, "Could not open file: '%s', error: %s\n", fullpaths[include_stack_ptr], g_strerror(errno) );
-               g_free(fullpaths[include_stack_ptr]);
-               *err_str = error->str;
-               g_string_free(error,FALSE);
+       state.value_strings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_value_strings);
+
+       if (Radius_lex_init(&scanner) != 0) {
+               g_string_append_printf(state.error, "Can't initialize scanner: %s",
+                   strerror(errno));
+               fclose(in);
+               g_free(state.fullpaths[0]);
+               *err_str = g_string_free(state.error,FALSE);
                return FALSE;
        }
 
-       value_strings = g_hash_table_new(g_str_hash,g_str_equal);
+       Radius_set_in(in, scanner);
 
-       BEGIN WS_OUT;
+       /* Associate the state with the scanner */
+       Radius_set_extra(&state, scanner);
 
-       yylex();
+       Radius_lex(scanner);
 
-       if (yyin != NULL) fclose(yyin);
-       yyin = NULL;
+       Radius_lex_destroy(scanner);
+       /*
+        * XXX - can the lexical analyzer terminate without closing
+        * all open input files?
+        */
 
-       for (i=0; i < 10; i++) {
-               if (fullpaths[i]) g_free(fullpaths[i]);
+       for (i = 0; i < MAX_INCLUDE_DEPTH; i++) {
+               g_free(state.fullpaths[i]);
        }
 
-       g_hash_table_foreach(dict->attrs_by_id,setup_attrs,NULL);
-       g_hash_table_foreach(dict->vendors_by_id,setup_vendors,NULL);
-       g_hash_table_foreach_remove(value_strings,destroy_value_strings,NULL);
+       g_hash_table_foreach(state.dict->attrs_by_id,setup_attrs,&state);
+       g_hash_table_foreach(state.dict->vendors_by_id,setup_vendors,&state);
+       g_hash_table_destroy(state.value_strings);
 
-       if (error->len > 0) {
-               *err_str = error->str;
-               g_string_free(error,FALSE);
+       if (state.error->len > 0) {
+               *err_str = g_string_free(state.error,FALSE);
                return FALSE;
        } else {
                *err_str = NULL;
-               g_string_free(error,TRUE);
+               g_string_free(state.error,TRUE);
                return TRUE;
        }
 }
 
-/*
- * We want to stop processing when we get to the end of the input.
- * (%option noyywrap is not used because if used then
- * some flex versions (eg: 2.5.35) generate code which causes
- * warnings by the Windows VC compiler).
- */
-
-int yywrap(void) {
-    return 1;
-}
-
 /*
  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
  *