Wireshark-dev: Re: [Wireshark-dev] Has anyone created an XDR to Dissector tool?
From: Richard Sharpe <[email protected]>
Date: Mon, 5 Sep 2016 22:05:14 -0700
On Fri, Sep 2, 2016 at 8:03 AM, Richard Sharpe
<[email protected]> wrote:
> For those following along at home, here is a WIP patch against the
> glibc master version of rpcgen.
>
> It builds and runs and generates an incomplete dissector at this
> stage, but it shows where I think I am headed.
>
> I am sure I can do with some feedback even at this early stage, and
> will likely have something that generates a complete dissector by
> Monday some time.
>
> Of course, whether or not the glibc folks want it is another matter.

Made a lot more progress. This thing is doable and in a few more days
it will be complete, I think.

At the moment it still has some debug artifacts in it but it can
generate dissectors for most of the structures generated. However, it
does not yet generate code for requests or responses,  or the ett or
hf array initialization ...

-- 
Regards,
Richard Sharpe
(何以解憂?唯有杜康。--曹操)
From cc797fc511bd4324ee05df3df76c6b41f1c5887a Mon Sep 17 00:00:00 2001
From: Richard Sharpe <[email protected]>
Date: Sat, 3 Sep 2016 18:31:10 -0700
Subject: [PATCH] The WIP so far ...

---
 sunrpc/Makefile                |   2 +-
 sunrpc/rpc_main.c              |  65 ++++-
 sunrpc/rpc_wireshark.c         | 583 +++++++++++++++++++++++++++++++++++++++++
 sunrpc/rpc_wireshark.h         |  15 ++
 sunrpc/rpcsvc/wireshark_test.x |  79 ++++++
 5 files changed, 736 insertions(+), 8 deletions(-)
 create mode 100644 sunrpc/rpc_wireshark.c
 create mode 100644 sunrpc/rpc_wireshark.h
 create mode 100644 sunrpc/rpcsvc/wireshark_test.x

diff --git a/sunrpc/Makefile b/sunrpc/Makefile
index 789ef42..ed5e9f0 100644
--- a/sunrpc/Makefile
+++ b/sunrpc/Makefile
@@ -92,7 +92,7 @@ endif
 install-bin := rpcgen
 rpcgen-objs = rpc_main.o rpc_hout.o rpc_cout.o rpc_parse.o \
 	      rpc_scan.o rpc_util.o rpc_svcout.o rpc_clntout.o \
-	      rpc_tblout.o rpc_sample.o
+	      rpc_tblout.o rpc_sample.o rpc_wireshark.o
 extra-objs = $(rpcgen-objs) $(addprefix cross-,$(rpcgen-objs))
 others += rpcgen
 
diff --git a/sunrpc/rpc_main.c b/sunrpc/rpc_main.c
index 0a51e2c..463125a 100644
--- a/sunrpc/rpc_main.c
+++ b/sunrpc/rpc_main.c
@@ -49,6 +49,7 @@
 #include "rpc_parse.h"
 #include "rpc_util.h"
 #include "rpc_scan.h"
+#include "rpc_wireshark.h"
 #include "proto.h"
 
 #include "../version.h"
@@ -68,6 +69,7 @@ struct commandline
     int tflag;			/* dispatch Table file */
     int Ssflag;			/* produce server sample code */
     int Scflag;			/* produce client sample code */
+    int Wflag;                  /* Produce a wireshark dissector */
     int makefileflag;		/* Generate a template Makefile */
     const char *infile;		/* input module name */
     const char *outfile;	/* output module name */
@@ -125,6 +127,8 @@ static void svc_output (const char *infile, const char *define,
 			int extend, const char *outfile);
 static void clnt_output (const char *infile, const char *define,
 			 int extend, const char *outfile);
+static void wireshark_output (const char *infile, const char *define,
+			      int extend, const char *outfile);
 static void mkfile_output (struct commandline *cmd);
 static int do_registers (int argc, const char *argv[]);
 static void addarg (const char *cp);
@@ -183,7 +187,7 @@ main (int argc, const char *argv[])
     usage (stderr, 1);
 
   if (cmd.cflag || cmd.hflag || cmd.lflag || cmd.tflag || cmd.sflag ||
-      cmd.mflag || cmd.nflag || cmd.Ssflag || cmd.Scflag)
+      cmd.mflag || cmd.nflag || cmd.Ssflag || cmd.Scflag || cmd.Wflag)
     {
       checkfiles (cmd.infile, cmd.outfile);
     }
@@ -205,6 +209,8 @@ main (int argc, const char *argv[])
     svc_output (cmd.infile, "-DRPC_SERVER", DONT_EXTEND, cmd.outfile);
   else if (cmd.Scflag)
     clnt_output (cmd.infile, "-DRPC_CLIENT", DONT_EXTEND, cmd.outfile);
+  else if (cmd.Wflag)
+    wireshark_output (cmd.infile, "-DRPC_XDR", DONT_EXTEND, cmd.outfile);
   else if (cmd.makefileflag)
     mkfile_output (&cmd);
   else
@@ -364,7 +370,7 @@ open_input (const char *infile, const char *define)
       putarg (1, CPPFLAGS);
       addarg (define);
       if (infile)
-	addarg (infile);
+	addarg (infilename);
       addarg ((char *) NULL);
       close (1);
       dup2 (pd[1], 1);
@@ -909,6 +915,47 @@ clnt_output (const char *infile, const char *define, int extend,
   close_output (outfilename);
 }
 
+/*
+ * generate wireshark dissector
+ *
+ * Pass over the list of definitions as many times as needed to
+ * generate the following:
+ *
+ * 1. The Preamble containing includes, the program define and the
+ *    forward declarations needed.
+ * 2. The ett values
+ * 3. The hf values needed.
+ * 4. The value strings needed.
+ * 5. The structure dissections.
+ * 6. The program dissections.
+ * 7. The epilog containing initialization of the hf and ett values
+ *    and etc.
+ *
+ *  We can do that because we have all we need in the list.
+ */
+static void
+wireshark_output (const char *infile, const char *define, int extend,
+	  const char *outfile)
+{
+  definition *def;
+  const char *outfilename;
+
+  open_input (infile, define);
+  outfilename = extend ? extendfile (infile, outfile) : outfile;
+  open_output (infile, outfilename);
+  checkfiles (infile, outfilename);
+
+  while ((def = get_definition ()) != NULL)
+    {
+
+    }
+
+  write_wireshark();
+
+  close_input ();
+  close_output (outfilename);
+}
+
 static const char space[] = " ";
 
 static char *
@@ -1131,9 +1178,9 @@ checkfiles (const char *infile, const char *outfile)
 	  fprintf (stderr,
 		   /* TRANS: the file will not be removed; this is an
 		      TRANS: informative message.  */
-		   _("file `%s' already exists and may be overwritten\n"),
-		   outfile);
-	  crash ();
+		   _("file `%s' already exists and may be overwritten: %s\n"),
+		   outfile, strerror(errno));
+	  /*crash ();*/
 	}
     }
 }
@@ -1200,6 +1247,7 @@ parseargs (int argc, const char *argv[], struct commandline *cmd)
 		case 'l':
 		case 'm':
 		case 't':
+		case 'W':
 		  if (flag[c])
 		    return 0;
 		  flag[c] = 1;
@@ -1339,6 +1387,7 @@ parseargs (int argc, const char *argv[], struct commandline *cmd)
   cmd->tflag = flag['t'];
   cmd->Ssflag = flag['S'];
   cmd->Scflag = flag['C'];
+  cmd->Wflag = flag['W'];
   cmd->makefileflag = flag['M'];
 
 #ifndef _RPC_THREAD_SAFE_
@@ -1377,7 +1426,8 @@ parseargs (int argc, const char *argv[], struct commandline *cmd)
 
   /* check no conflicts with file generation flags */
   nflags = cmd->cflag + cmd->hflag + cmd->lflag + cmd->mflag +
-    cmd->sflag + cmd->nflag + cmd->tflag + cmd->Ssflag + cmd->Scflag;
+    cmd->sflag + cmd->nflag + cmd->tflag + cmd->Ssflag + cmd->Scflag +
+    cmd->Wflag;
 
   if (nflags == 0)
     {
@@ -1405,7 +1455,7 @@ static void
 usage (FILE *stream, int status)
 {
   fprintf (stream, _("usage: %s infile\n"), cmdname);
-  fprintf (stream, _("\t%s [-abkCLNTM][-Dname[=value]] [-i size] \
+  fprintf (stream, _("\t%s [-abkCLNTMW][-Dname[=value]] [-i size] \
 [-I [-K seconds]] [-Y path] infile\n"), cmdname);
   fprintf (stream, _("\t%s [-c | -h | -l | -m | -t | -Sc | -Ss | -Sm] \
 [-o outfile] [infile]\n"), cmdname);
@@ -1441,6 +1491,7 @@ options_usage (FILE *stream, int status)
   f_print (stream, _("-Sm \t\tgenerate makefile template \n"));
   f_print (stream, _("-t\t\tgenerate RPC dispatch table\n"));
   f_print (stream, _("-T\t\tgenerate code to support RPC dispatch tables\n"));
+  f_print (stream, _("-T\t\tgenerate code for a Wireshark dissector\n"));
   f_print (stream, _("-Y path\t\tdirectory name to find C preprocessor (cpp)\n"));
   f_print (stream, _("-5\t\tSysVr4 compatibility mode\n"));
   f_print (stream, _("--help\t\tgive this help list\n"));
diff --git a/sunrpc/rpc_wireshark.c b/sunrpc/rpc_wireshark.c
new file mode 100644
index 0000000..366ff0b
--- /dev/null
+++ b/sunrpc/rpc_wireshark.c
@@ -0,0 +1,583 @@
+/*      @(#)rpc_wireshark.c  1.0  16/09/01
+ *
+ * Copyright to be determined ...
+ *
+ * rpc_wireshark.h, Definitions for the generation of a wireshark
+ * dissector in rpcgen
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "proto.h"
+#include "rpc_wireshark.h"
+
+static void write_wireshark_boilerplate (definition *def);
+static void write_wireshark_forward_defs (definition *prog);
+static void write_wireshark_ett_fields (definition *prog);
+static void write_wireshark_hf_fields (definition *prog);
+static void write_wireshark_enum_decls (definition *prog);
+static void write_wireshark_structs_unions_dissect (definition *prog);
+static void write_wireshark_struct_dissect(definition *prog, definition *st);
+static void write_wireshark_union_dissect(definition *prog, definition *st);
+
+static typedef_list *typedefs = NULL;
+static typedef_list **typedefs_end = &typedefs;
+
+void write_wireshark(void)
+{
+  list *l;
+  definition *def, *prog = NULL;
+
+  /* Handle the prelude ... */
+  for (l = defined; l != NULL; l = l->next)
+    {
+      typedef_list *list_elt;
+
+      def = (definition *) l->val;
+        switch (def->def_kind)
+        {
+          case DEF_PROGRAM:
+	
+            prog = def;
+	    write_wireshark_boilerplate (def);
+            break;
+
+          case DEF_TYPEDEF:
+            list_elt = calloc(1, sizeof(typedef_list));
+            *typedefs_end = list_elt;
+            list_elt->def = def; 
+            typedefs_end = &list_elt->next;
+            break;
+
+          default:
+            break;
+       } 
+    }
+
+  /*
+   * Now, do all the forward declarations we need.
+   * We need one for each data structure and
+   * one for each function in version defs.
+  */
+  write_wireshark_forward_defs (prog);
+
+  write_wireshark_ett_fields (prog);
+
+  write_wireshark_hf_fields (prog);
+
+  write_wireshark_enum_decls (prog);
+
+  write_wireshark_structs_unions_dissect (prog);
+}
+
+static void write_wireshark_boilerplate(definition *def)
+{
+    fprintf(fout,
+"/* packet-%s.c\n"
+" * Routines for %s dissection\n"
+" * Copyright %s, %s\n"
+" *\n"
+" * Wireshark - Network traffic analyzer\n"
+" * By Gerald Combs <[email protected]>\n"
+" * Copyright 1998 Gerald Combs\n"
+" *\n"
+" * Portions copied shamelessly from packet-nfs.c\n"
+" *\n"
+" * This program is free software; you can redistribute it and/or\n"
+" * modify it under the terms of the GNU General Public License\n"
+" * as published by the Free Software Foundation; either version 2\n"
+" * of the License, or (at your option) any later version.\n"
+" *\n"
+" * This program is distributed in the hope that it will be useful,\n"
+" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+" * GNU General Public License for more details.\n"
+" *\n"
+" * You should have received a copy of the GNU General Public License\n"
+" * along with this program; if not, write to the Free Software\n"
+" * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n"
+" *\n"
+" * GENERATED BY RPCGEN. DO NOT DO SERIOUS EDITS.\n"
+" */\n"
+"\n"
+"#include \"config.h\"\n"
+"\n"
+"#include <stdio.h>\n"
+"\n"
+"#include <epan/packet.h>\n"
+"#include <epan/prefs.h>\n"
+"#include <epan/exceptions.h>\n"
+"#include <epan/expert.h>\n"
+"#include <epan/to_str.h>\n"
+"#include <epan/decode_as.h>\n"
+"#include <wsutil/crc16.h>\n"
+"#include <wsutil/crc32.h>\n"
+"#include \"packet-rpc.h\"\n"
+"\n"
+"#define %s %s\n\n"
+"void proto_register_%s(void);\n"
+"void proto_reg_handoff_%s(void);\n\n",
+           def->def_name, def->def_name, "@[email protected]", "@[email protected]",
+           def->def_name, def->def.pr.prog_num,
+	   def->def_name, def->def_name);
+
+  /* TODO: Fix the year and pickyp the copyright somehow. */
+}
+
+static void write_wireshark_forward_defs(definition *prog)
+{
+  list *l;
+  definition *def;
+
+  /*
+   * If there is no program definition, the structure names
+   * do not need qualification as they have to be unique anyway.
+  */
+
+  for (l = defined; l != NULL; l = l->next)
+    {
+      version_list *v = NULL;
+      proc_list *p = NULL;
+
+      def = (definition *) l->val;
+      switch (def->def_kind) {
+        case DEF_STRUCT:
+        case DEF_UNION:
+          fprintf(fout, "%sdissect_%s(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);\n",
+                  (prog ? "static " : ""), def->def_name);
+          break;
+
+        case DEF_PROGRAM:
+          for (v = def->def.pr.versions; v != NULL; v = v->next)
+            {
+              for (p = v->procs; p != NULL; p = p->next)
+                {
+                  const char * tplt = "static dissect_%s_%s(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);\n";
+                  fprintf(fout, tplt, p->proc_name, "call");
+                  fprintf(fout, tplt, p->proc_name, "reply");
+                }
+            }
+          break;
+
+        default:
+          break;
+      }
+    }
+
+  fprintf(fout, "\n");
+}
+
+static void write_wireshark_ett_fields (definition *prog)
+{
+  list *l;
+  definition *def;
+
+  if (prog)
+    fprintf(fout, "static gint ett_%s = -1;\n", prog->def_name);
+
+  for (l = defined; l != NULL; l = l->next)
+    {
+      version_list *v = NULL;
+      proc_list *p = NULL;
+
+      def = (definition *) l->val;
+      switch (def->def_kind) {
+        case DEF_STRUCT:
+        case DEF_UNION:
+          fprintf(fout, "static gint ett_%s = -1;\n", def->def_name);
+          break;
+
+        /* Not clear we need these */
+        case DEF_PROGRAM:
+          for (v = def->def.pr.versions; v != NULL; v = v->next)
+            {
+              for (p = v->procs; p != NULL; p = p->next)
+                {
+                  fprintf(fout, "static gint ett_%s = -1;\n", p->proc_name);
+                }
+            }
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  fprintf(fout, "\n");
+}
+
+static void write_wireshark_hf_fields (definition *prog)
+{
+  list *l;
+  definition *def;
+
+  if (prog)
+    fprintf(fout, "static gint hf_%s = -1;\n", prog->def_name);
+
+  for (l = defined; l != NULL; l = l->next)
+    {
+      decl_list *d = NULL;
+      version_list *v = NULL;
+      proc_list *p = NULL;
+
+      def = (definition *) l->val;
+      switch (def->def_kind)
+        {
+          case DEF_STRUCT:
+            for (d = def->def.st.decls; d != NULL; d = d->next)
+              {
+                /*
+                 * field names must be qualified by the parent because
+                 * because there could be two fields with the same
+                 * name in different structures.
+                 */
+                fprintf(fout, "static int hf_%s_%s = -1;\n",
+                        def->def_name, d->decl.name);
+              }
+            break;
+
+          case DEF_PROGRAM:
+            for (v = def->def.pr.versions; v != NULL; v = v->next)
+              {
+                for (p = v->procs; p != NULL; p = p->next)
+                  {
+                    fprintf(fout, "//PROC: N: %s, arg#: %d, an: %s\n",
+                            p->proc_name, p->arg_num, p->args.argname);
+                  }
+              }
+            break;
+
+          default:
+            break;
+        }
+    }
+
+  fprintf(fout, "\n");
+}
+
+static void write_wireshark_enum_decls (definition *prog)
+{
+
+  list *l;
+  definition *def;
+
+  for (l = defined; l != NULL; l = l->next)
+    {
+      def = (definition *) l->val;
+
+      if (def->def_kind == DEF_ENUM)
+        {
+          enumval_list *e = NULL;
+
+          fprintf(fout, "static const value_string %s_vals[] = {\n",
+                  def->def_name);
+
+          for (e = def->def.en.vals; e != NULL; e = e->next)
+            {
+              fprintf(fout, "\t{ %s, \"%s\" },\n", e->assignment, e->name);
+            }
+
+          fprintf(fout, "\t{ 0, NULL }\n};\n\n");
+
+        }
+    }
+
+  fprintf(fout, "\n");
+}
+
+static void write_wireshark_structs_unions_dissect (definition *prog)
+{
+  list *l;
+  definition *def;
+
+  for (l = defined; l != NULL; l = l->next)
+    {
+      def = (definition *) l->val;
+
+      switch (def->def_kind)
+      {
+        case DEF_STRUCT:
+          write_wireshark_struct_dissect (prog, def);
+          break;
+
+        case DEF_UNION:
+          write_wireshark_union_dissect (prog, def);
+          break;
+
+        default:
+          break;
+      }
+    }
+}
+
+/*
+ * Map the incoming types to base types if we have them ...
+ *
+ * We deal with opaque and string separately. Min size is 4 bytes, as per
+ * the spec. Will have to add float types.
+ *
+ * The first array must be kept sorted! They have to be kept in sync.
+ */
+static const char *known_types[] = {
+	"bool", "char", "long", "quad_t", "short", "u_char", "u_int",
+	"u_long", "u_quad_t", "u_short"
+};
+#if 0
+static const char *rpc_types[] = { "FT_INT32", "FT_BYTES", "FT_INT64",
+	"FT_INT16", "FT_UINT8", "FT_UINT32", "FT_UINT32", "FT_UINT64",
+	"FT_UINT16"
+};
+#endif
+static const int type_sizes[] = { 4, 4, 4, 8, 4, 4, 4, 4, 8, 4 };
+
+static const char *var_types[] = {
+	"guint32", "guint32", "guint32", "guint64", "guint32", "guint32",
+	"guint32", "guint32", "guint64", "guint32"
+};
+
+static const char *access_names[] = {
+        "ntohl", "ntohl", "ntohl", "ntoh64", "ntohl", "ntohl",
+	"htohl", "ntohl", "ntoh64", "ntohl"
+};
+
+static const char *proto_item_names[] = {
+        "int", "int", "int", "int64", "int", "uint", "uint",
+	"uint", "uint64", "uint"
+};
+
+/*
+ * Return 1 if the type is a base type, and then its size. We must
+ * resolve any typedefs along the way. So, search the list to see
+ * if the type resolves there, and keep resolving them if needed.
+ */
+definition *resolve_type(const char *type)
+{
+  definition *resolved = NULL;
+  typedef_list *l = typedefs;
+
+  /* Need to keep trying until cannot resolve again */
+  while (l)
+    {
+      if (strcmp(type, l->def->def_name) == 0)
+        {
+          resolved = l->def;
+          break;
+        }
+      l = l->next;
+    }
+  return resolved;
+}
+
+static const char *is_base_type(const char *type, int *len, int *index)
+{
+  definition *def = resolve_type(type);
+  int array_size = sizeof(known_types)/sizeof(char *), i;
+  const char *ret = NULL;
+
+  *len = 0; *index = -1;
+  if (def)
+    {
+      type = def->def.ty.old_type;
+    }
+
+  for (i = 0; i < array_size; i++)
+    {
+      if (strcmp(type, known_types[i]) == 0)
+        {
+          *len = type_sizes[i];
+          *index = i;
+          ret = type;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+static void write_wireshark_decl(declaration *decl, const char *parent_name,
+                                 int *iter_needed)
+{
+  switch (decl->rel)
+    {
+      int len = 0, index = 0;
+
+      case REL_VECTOR:
+	  /*
+           * Generate each of the items. If they are simple types,
+           * then just insert them, but if not, then will have to dissect 
+           * them.
+           *
+           * The basic unit is 4 bytes, unless a string or opaque, but
+           * even then, they still round up to 4 bytes.
+           *
+           * A vector of opaque bytes is different, however, as is
+           * a vector of bytes. Strings can only be arrays. No need
+           * to deal with them here.
+           */
+          fprintf(fout, "//V, P=%s T=%s N=%s L=%s\n", decl->prefix, decl->type,
+                  decl->name, decl->array_max);
+          if (strcmp(decl->type, "opaque") == 0)
+            {
+              // If it is opaque, just pull off that many bytes.
+              fprintf(fout, "\t\tproto_tree_add_item(subtree, hf_%s_%s, tvb, "
+                            "offset, %s, ENC_BIG_ENDIAN);\n",
+                            parent_name, decl->name, decl->array_max);
+              fprintf(fout, "\t\toffset += %s;\n", decl->array_max);
+              fprintf(fout, "\t\toffset += 4 - (%s %% 4);\n",
+                      decl->array_max); // Roundup
+              return;
+            }
+
+          // Add a subtree for the Array ... 
+          if (*iter_needed)
+            {
+              fprintf(fout, "\n\t\tint i = 0;\n"); /* Assumes C99? */
+              *iter_needed = 0;
+            }
+          fprintf(fout, "\t\tfor (i = 0; i < %s; i++) {\n",
+                  decl->array_max);
+          if (is_base_type(decl->type, &len, &index))
+            {
+              fprintf(fout, "\t\t\t%s val;\n\n", var_types[index]);
+              fprintf(fout, "\t\t\tval = tvb_get_%s(tvb, offset);\n",
+                            access_names[index]);
+              fprintf(fout, "\t\t\tproto_tree_add_%s_format(subtree, hf_%s_%s, "
+                            "tvb, offset, %d, val, \"%s[%%d]\", i);\n",
+                            proto_item_names[index], parent_name, decl->name,
+                            len, decl->name);
+              fprintf(fout, "\t\t\toffset += %d\n", type_sizes[index]);
+            }
+          else
+            {
+              fprintf(fout, "\t\t\tchar label[%lu];\n\n",
+                      strlen(decl->name) + 3 + strlen(decl->array_max));
+              fprintf(fout, "\t\t\tsprintf(label, \"%s[%%d]\",i);\n",
+                      decl->name);
+              fprintf(fout, "\t\t\toffset += dissect_%s(tvb, subtree, offset, "
+                            "label);\n", decl->type);
+            }
+          fprintf(fout, "\t\t}\n\n");
+          break;
+
+      case REL_ARRAY:
+          /* if it's a base type (including via typedef, then generate
+           * direct code, otherwise, call a separate dissect function
+           *
+           * This is for variable-length arrays, but strings are different.
+           */
+          fprintf(fout, "//A, P=%s T=%s N=%s L=%s\n", decl->prefix, decl->type,
+                  decl->name, decl->array_max);
+          // Insert code to get the length
+          fprintf(fout, "\t\tguint len = tvb_get_ntohl(tvb, offset);\n");
+          fprintf(fout, "\t\toffset += 4;\n\n");
+          // If a  bunch of opaque bytes ... just handle them.
+          if (strcmp(decl->type, "opaque") == 0)
+            {
+              fprintf(fout, "\t\tproto_tree_add_item(subtree, hf_%s_%s, tvb, "
+                            "offset, len, ENC_BIG_ENDIAN);\n",
+                            parent_name, decl->name);
+              fprintf(fout, "\t\toffset += len;\n");
+              fprintf(fout, "\t\toffset += 4 - (len %% 4);\n"); // Roundup
+              return;
+            }
+          if (strcmp(decl->type, "string") == 0) // Merge with above?
+            {
+              fprintf(fout, "\t\tproto_tree_add_item(subtree, hf_%s_%s, tvb, "
+                            "offset, len, ENC_BIG_ENDIAN);\n",
+                            parent_name, decl->name);
+              fprintf(fout, "\t\toffset += len;\n");
+              fprintf(fout, "\t\toffset += 4 - (len %% 4);\n"); // Roundup
+              return;
+            }
+          if (*iter_needed)
+            {
+              fprintf(fout, "\t\tint i = 0;\n");
+              *iter_needed = 0;
+            }
+          fprintf(fout, "\t\tfor (i = 0; i < len; i++) {\n");
+          // Insert stuff to dissect the others here ...
+          fprintf(fout, "\t\t}\n\n");
+	  break;
+
+      case REL_ALIAS:
+          // If it is a direct base type, or resolves to a direct base type
+          // we can handle it here. However, if it resolves to a vector or
+          // an array, we have to send it back through.
+          fprintf(fout, "//AL: P=%s, T=%s, N=%s\n", decl->prefix, decl->type,
+                  decl->name);
+          if (is_base_type(decl->type, &len, &index))
+            {
+	      fprintf(fout, "\t\tproto_tree_add_item(subtree, hf_%s_%s, tvb, "
+                            "offset, %d, ENC_BIG_ENDIAN);\n",
+                      parent_name, decl->name, len);
+              fprintf(fout, "\t\toffset += %d;\n", len);
+            }
+          else 
+            {
+              definition *def;
+              // It might be a typedef to an array, or something defined
+              // in another XDR file ... generate the correct stuff.
+              // Here, it cannot be a vector or array.
+              def = resolve_type(decl->type);
+              if (def)
+                {
+                  struct declaration new_decl;
+
+                  new_decl.name      = decl->name;
+                  new_decl.prefix    = def->def.ty.old_prefix;
+                  new_decl.type      = def->def.ty.old_type;
+                  new_decl.rel       = def->def.ty.rel;
+                  new_decl.array_max = def->def.ty.array_max;
+                  write_wireshark_decl(&new_decl, parent_name, iter_needed);
+                }
+              else
+                {
+                  fprintf(fout, "\t\toffset += dissect_%s(tvb, subtree, "
+                                "offset);", decl->name);
+                }
+            }
+          break;
+
+      default:
+        break;
+    }  
+}
+
+static void write_wireshark_struct_dissect(definition *prog, definition *st)
+{
+  struct decl_list *d = NULL;
+  int iter_needed = 1;       // Used in write_wireshark_decl
+
+  /*
+   * Make a pass for those fields that require two levels of dissecton.
+   */
+
+  fprintf(fout, "%sint\ndissect_%s(tvbuff_t *tvb, proto_tree *tree, "
+                "int offset, const char *label)\n",
+          (prog ? "static " : ""), st->def_name);
+  fprintf(fout, "{\n");
+  fprintf(fout, "\tif (tree) {\n");
+  fprintf(fout, "\t\tproto_tree subtree = NULL;\n"
+                "\t\tproto_item it = NULL;\n"
+                "\t\tint old_offset = offset;\n\n");
+  /* Insert a subtree named by this structure name */
+  fprintf(fout, "\t\tsubtree = proto_tree_add_subtree(tree, tvb, offset, -1, "
+          "ett_%s, &it, label);\n",
+          st->def_name);
+  for (d = st->def.st.decls; d != NULL; d = d->next)
+    {
+      write_wireshark_decl(&d->decl, st->def_name, &iter_needed);
+    }
+  /* Insert code to fix up the length of the byte stream covered by this */
+  fprintf(fout, "\n");
+  fprintf(fout, "\t\tproto_item_set_len(it, offset - old_offset);\n");
+  fprintf(fout, "\t}\n\n\treturn offset;\n}\n\n");
+}
+
+static void write_wireshark_union_dissect(definition *prog, definition *un)
+{
+
+}
diff --git a/sunrpc/rpc_wireshark.h b/sunrpc/rpc_wireshark.h
new file mode 100644
index 0000000..7a6abea
--- /dev/null
+++ b/sunrpc/rpc_wireshark.h
@@ -0,0 +1,15 @@
+/*      @(#)rpc_wireshark.h  1.0  16/09/01
+ *
+ * Copyright to be determined ...
+ *
+ * rpc_wireshark.h, Definitions for the generation of a wireshark
+ * dissector in rpcgen
+ */
+
+struct typedef_list {
+	struct definition *def;
+	struct typedef_list *next;
+};
+typedef struct typedef_list typedef_list;
+
+void write_wireshark (void);
diff --git a/sunrpc/rpcsvc/wireshark_test.x b/sunrpc/rpcsvc/wireshark_test.x
new file mode 100644
index 0000000..966260c
--- /dev/null
+++ b/sunrpc/rpcsvc/wireshark_test.x
@@ -0,0 +1,79 @@
+/*
+ * A test of the wireshark dissector.
+ */
+
+const STRING_LEN = 32;
+
+enum enum_1 {
+   ENUM_OK = 0,
+   ENUM_NOT = 1,
+   ENUM_OTHER = 2
+};
+
+struct mult_def_struct {
+	hyper big;
+	opaque op[12];
+	char str[13];
+	unsigned int smallu;
+	hyper big2;
+	hyper big3[10];
+	string sss<12>;
+	string var<>;
+};
+
+typedef opaque fixed_t[12];
+
+typedef opaque some_t<STRING_LEN>;
+
+struct fixed_and_variable {
+	fixed_t fixed;
+	some_t variable;
+};
+
+struct funny {
+	multi_def_struct parms[10];
+	multi_def_struct vars<>;
+};
+
+struct direct_etc {
+	opaque fixed[13];
+	opaque variable<23>;
+};
+
+typedef unsigned int uint32_t;
+typedef uint32_t my_type_t;
+
+typedef three_parm_struct list_of_parm<>;
+
+struct both {
+	uint32_t xxx[32];
+	uint32_t yyy<16>;
+};
+
+struct special {
+	char aaa[12];
+	unsigned char bbb<12>;
+};
+
+struct another {
+	uint32 aaa[12];
+};
+
+/* The program with two versions */
+program WIRESHARK_TEST {
+	/*
+	 * V1 had some simple functions
+	*/
+	version WIRESHARK_TEST_V1 {
+		void NULL(void) = 0;
+
+		unsigned int OneParmSimpleReturn(unsigned int) = 1;
+
+		int OneStructSimpleReturn(struct three_parm_struct parm1) = 2;
+
+		enum enum_1 OneStringParameReturnEnum(string str<STRING_LEN>) = 3;
+                unsigned int OneOpaqueTypedefReturnInt(Some_t) = 4;
+
+		unsigned int TwoTypesReturnInt(struct multi_def_struct, int) = 5;
+	} = 1;
+} = 123456;
-- 
2.4.3