SPOOLSS RPC dissector, from Tim Potter. This includes adding additional
authorguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 3 Jan 2002 20:42:41 +0000 (20:42 +0000)
committerguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 3 Jan 2002 20:42:41 +0000 (20:42 +0000)
DOS error codes to the table of them, and exporting that table to other
dissectors for protocols using DOS error codes.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@4470 f5534014-38df-0310-8fa8-9805f1628bb7

packet-dcerpc-lsa.c
packet-dcerpc-nt.c
packet-dcerpc-nt.h
packet-dcerpc-reg.c
packet-dcerpc-reg.h
packet-dcerpc-spoolss.c
packet-dcerpc.c
packet-dcerpc.h
packet-smb.c
smb.h

index 8021e04577ba7695bbbd5290df5c0b669aef7452..b54bd63c7c21209e7cf83ad8daa12cd8fd5662ef 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for SMB \PIPE\lsarpc packet disassembly
  * Copyright 2001, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-lsa.c,v 1.5 2001/12/17 08:31:26 guy Exp $
+ * $Id: packet-dcerpc-lsa.c,v 1.6 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
  *
  */
 
-/* Convert a (little endian) unicode string to ASCII.  We fake it by just
-   taking every odd byte. */
-
-static char *fake_unicode(guint16 *data, int len)
-{
-       char *buffer;
-       int i;
-
-       buffer = malloc(len + 1);
-
-       for (i = 0; i < len; i++)
-               buffer[i] = data[i] & 0xff;
-
-       buffer[len] = 0;
-
-       return buffer;
-}
-
 static int ett_UNISTR = -1;
 static int ett_UNISTR_hdr = -1;
 
@@ -257,7 +239,7 @@ static int LsaClose_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
        if (check_col(pinfo->cinfo, COL_INFO))
                col_set_str(pinfo->cinfo, COL_INFO, "ClosePolicy request");
 
-       offset = prs_policy_hnd(tvb, offset, pinfo, tree);
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
 
        return offset;
 }
@@ -268,7 +250,7 @@ static int LsaClose_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
        if (check_col(pinfo->cinfo, COL_INFO))
                col_set_str(pinfo->cinfo, COL_INFO, "ClosePolicy reply");
 
-       offset = prs_policy_hnd(tvb, offset, pinfo, tree);
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
        offset = prs_ntstatus(tvb, offset, pinfo, tree);
 
        return offset;
@@ -456,7 +438,7 @@ static int LsaOpenPolicy_r(tvbuff_t * tvb, int offset,
        if (check_col(pinfo->cinfo, COL_INFO))
                col_set_str(pinfo->cinfo, COL_INFO, "OpenPolicy reply");
 
-        offset = prs_policy_hnd(tvb, offset, pinfo, tree);
+        offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
         offset = prs_ntstatus(tvb, offset, pinfo, tree);
 
         return offset;
@@ -601,7 +583,7 @@ static int LsaQueryInfoPolicy_q(tvbuff_t *tvb, int offset,
        if (check_col(pinfo->cinfo, COL_INFO))
                col_set_str(pinfo->cinfo, COL_INFO, "QueryInfo request");
 
-       offset = prs_policy_hnd(tvb, offset, pinfo, tree);
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
        offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Info level");
 
        return offset;
@@ -864,7 +846,7 @@ static int LsaLookupNames_q(tvbuff_t *tvb, int offset,
        if (check_col(pinfo->cinfo, COL_INFO))
                col_set_str(pinfo->cinfo, COL_INFO, "LookupNames request");
 
-       offset = prs_policy_hnd(tvb, offset, pinfo, tree);
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
 
        offset = prs_uint32(tvb, offset, pinfo, tree, &count, "Num names");
 
@@ -1092,7 +1074,7 @@ static int LsaLookupSids_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
        if (check_col(pinfo->cinfo, COL_INFO))
                col_set_str(pinfo->cinfo, COL_INFO, "LookupSids request");
 
-       offset = prs_policy_hnd(tvb, offset, pinfo, tree);
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
 
        offset = prs_SID_ARRAY(tvb, offset, pinfo, tree, flags, &ptr_list);
 
index 7af722d4b7670773811aa56acb018e26ba0a6f10..5fe5aeb242b52b0726338bf5a6cd1d82f8fbf350 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for DCERPC over SMB packet disassembly
  * Copyright 2001, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-nt.c,v 1.1 2001/12/16 20:17:10 guy Exp $
+ * $Id: packet-dcerpc-nt.c,v 1.2 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -314,7 +314,7 @@ guint32 prs_pop_ptr(GList **ptr_list, char *name)
    fake it by taking every odd byte.  )-:  The caller must free the
    result returned. */
 
-static char *fake_unicode(guint16 *data, int len)
+char *fake_unicode(guint16 *data, int len)
 {
        char *buffer;
        int i;
@@ -360,11 +360,18 @@ int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
 /* Parse a policy handle. */
 
 int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
-                  proto_tree *tree)
+                  proto_tree *tree, const guint8 **data)
 {
+       const guint8 *data8;
+
        offset = prs_align(offset, 4);
 
        proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle");
 
+       data8 = tvb_get_ptr(tvb, offset, 20);
+       
+       if (data)
+               *data = data8;
+
        return offset + 20;
 }
index be3a813e0768fdfa442b831059799d33462b90f0..0eefb97e137acdd07321ce6ce41bd8f396cb6624 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for DCERPC over SMB packet disassembly
  * Copyright 2001, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-nt.h,v 1.1 2001/12/16 20:17:10 guy Exp $
+ * $Id: packet-dcerpc-nt.h,v 1.2 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -55,11 +55,13 @@ int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
 /* Parse some common RPC structures */
 
+char *fake_unicode(guint16 *data, int len);
+
 int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
                proto_tree *tree, int flags, char **data, char *name);
 
 int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
-                  proto_tree *tree);
+                  proto_tree *tree, const guint8 **data);
 
 /* Routines for handling deferral of referants in NDR */
 
index 7c7900c029c9b40ff959fac173447daa7200c8a2..98097d89b2c801b4f411620144a06f9d0f22510e 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for SMB \\PIPE\\winreg packet disassembly
  * Copyright 2001, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-reg.c,v 1.1 2001/11/21 02:08:57 guy Exp $
+ * $Id: packet-dcerpc-reg.c,v 1.2 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #include "packet-dcerpc.h"
 #include "packet-dcerpc-reg.h"
 
+/* Registry data types */
+
+const value_string reg_datatypes[] = {
+       { REG_NONE, "REG_NONE" },
+       { REG_SZ, "REG_SZ" },
+       { REG_EXPAND_SZ, "REG_EXPAND_SZ" },
+       { REG_BINARY, "REG_BINARY" },
+       { REG_DWORD, "REG_DWORD" },
+       { REG_DWORD_LE, "REG_DWORD_LE" },
+       { REG_DWORD_BE, "REG_DWORD_BE" },
+       { REG_LINK, "REG_LINK" },
+       { REG_MULTI_SZ, "REG_MULTI_SZ" },
+       { REG_RESOURCE_LIST, "REG_RESOURCE_LIST" },
+       { REG_FULL_RESOURCE_DESCRIPTOR, "REG_FULL_RESOURCE_DESCRIPTOR" },
+       { REG_RESOURCE_REQUIREMENTS_LIST, "REG_RESOURCE_REQUIREMENTS_LIST" },
+       {0, NULL }
+};
+
 static int proto_dcerpc_reg = -1;
 static gint ett_dcerpc_reg = -1;
 
index 941937a2b7af381933abf1b7c27e070147321b49..84027af93811c49e959baeb2ca59bedb0505b911 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for SMB \PIPE\winreg packet disassembly
  * Copyright 2001, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-reg.h,v 1.3 2001/12/16 20:08:22 guy Exp $
+ * $Id: packet-dcerpc-reg.h,v 1.4 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #define REG_ABORT_SHUTDOWN     0x19
 #define REG_UNK_1A             0x1a
 
+/* Registry data types */
+
+#define REG_NONE                       0
+#define REG_SZ                        1
+#define REG_EXPAND_SZ                  2
+#define REG_BINARY                    3
+#define REG_DWORD                     4
+#define REG_DWORD_LE                  4        /* DWORD, little endian */
+#define REG_DWORD_BE                  5        /* DWORD, big endian */
+#define REG_LINK                       6
+#define REG_MULTI_SZ                  7
+#define REG_RESOURCE_LIST              8
+#define REG_FULL_RESOURCE_DESCRIPTOR   9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+
+extern const value_string reg_datatypes[];
+
 #endif /* packet-dcerpc-reg.h */
index 8129f298266f29eb379c7a63a39865592f7d23c6..7348abfb048f3365df2375410d8b19da31e68c92 100644 (file)
@@ -1,8 +1,8 @@
 /* packet-dcerpc-spoolss.c
- * Routines for SMB \\PIPE\\spoolss packet disassembly
+ * Routines for SMB \PIPE\spoolss packet disassembly
  * Copyright 2001, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-spoolss.c,v 1.1 2001/11/21 02:08:57 guy Exp $
+ * $Id: packet-dcerpc-spoolss.c,v 1.2 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #endif
 
 #include <glib.h>
+#include <string.h>
+
 #include "packet.h"
 #include "packet-dcerpc.h"
+#include "packet-dcerpc-nt.h"
 #include "packet-dcerpc-spoolss.h"
+#include "packet-dcerpc-reg.h"
+#include "smb.h"
+
+/*
+ * Hash table for matching responses to replies
+ */
+
+#define REQUEST_HASH_INIT_COUNT 100
+
+static GHashTable *request_hash;
+static GMemChunk *request_hash_key_chunk;
+static GMemChunk *request_hash_value_chunk;
+
+typedef struct {
+       dcerpc_info di;
+       guint16 opnum;
+} request_hash_key;
+
+typedef struct {
+       guint16 opnum;          /* Tag for union */
+
+       guint32 request_num;    /* Request frame number */
+       guint32 response_num;   /* Response frame number */
+
+       /* Per-request information */
+
+       union {
+               struct {
+                       char *printer_name;
+               } OpenPrinterEx;
+               struct {
+                       int level;
+               } GetPrinter;
+       } data;
+
+} request_hash_value;
+
+/* Hash a request */
+
+static guint hash_request(gconstpointer k)
+{
+       request_hash_key *r = (request_hash_key *)k;
+
+       return r->di.smb_fid + r->di.call_id + r->di.smb_fid;
+}
+
+/* Compare two requests */
+
+static gint compare_request(gconstpointer k1, gconstpointer k2)
+{
+       request_hash_key *r1 = (request_hash_key *)k1;
+       request_hash_key *r2 = (request_hash_key *)k2;
+
+       return r1->opnum == r2->opnum && r1->di.call_id == r2->di.call_id &&
+               r1->di.smb_fid == r2->di.smb_fid && 
+               r1->di.conv->index == r2->di.conv->index;
+}
+
+/* Store private information for a SPOOLSS request */
+
+static void store_request_info(request_hash_key *key, 
+                              request_hash_value *value)
+{
+       request_hash_key *chunk_key;
+       request_hash_value *chunk_value;
+
+       chunk_key = g_mem_chunk_alloc(request_hash_key_chunk);
+       chunk_value = g_mem_chunk_alloc(request_hash_value_chunk);
+
+       memcpy(chunk_key, key, sizeof(*key));
+       memcpy(chunk_value, value, sizeof(*value));
+
+       g_hash_table_insert(request_hash, chunk_key, chunk_value);
+}
+
+/* Store private information for a SPOOLSS call with no private
+   information.  This is basically for updating the request/response frame
+   numbers. */
+
+#define SPOOLSS_DUMMY (guint16)-1 /* Dummy opnum */
+
+static void store_request_info_none(packet_info *pinfo, dcerpc_info *di)
+{
+       request_hash_key key;
+       request_hash_value value;
+
+       memcpy(&key.di, di, sizeof(*di));
+       key.opnum = SPOOLSS_DUMMY;
+
+       value.opnum = SPOOLSS_DUMMY;
+       value.request_num = pinfo->fd->num;
+       value.response_num = 0;
+
+       store_request_info(&key, &value);
+}
+
+/* Store private information for a OpenPrinterEx request */
+
+static void store_request_info_OpenPrinterEx(packet_info *pinfo,
+                                            dcerpc_info *di,
+                                            char *printer_name)
+{
+       request_hash_key key;
+       request_hash_value value;
+
+       memcpy(&key.di, di, sizeof(*di));
+       key.opnum = SPOOLSS_OPENPRINTEREX;
+
+       value.opnum = SPOOLSS_OPENPRINTEREX;
+       value.data.OpenPrinterEx.printer_name = strdup(printer_name);
+       value.request_num = pinfo->fd->num;
+       value.response_num = 0;
+
+       store_request_info(&key, &value);
+}
+
+/* Store private information for a GetPrinter request */
+
+static void store_request_info_GetPrinter(packet_info *pinfo,
+                                         dcerpc_info *di,
+                                         int level)
+{
+       request_hash_key key;
+       request_hash_value value;
+
+       memcpy(&key.di, di, sizeof(*di));
+       key.opnum = SPOOLSS_GETPRINTER;
+
+       value.opnum = SPOOLSS_GETPRINTER;
+       value.data.GetPrinter.level = level;
+       value.request_num = pinfo->fd->num;
+       value.response_num = 0;
+
+       store_request_info(&key, &value);
+}
+
+/* Fetch private information for a SPOOLSS request */
+
+static request_hash_value *fetch_request_info(packet_info *pinfo, 
+                                             dcerpc_info *di,
+                                             guint16 opnum)
+{
+       request_hash_key key;
+       request_hash_value *result;
+
+       key.di = *di;
+       key.opnum = opnum;
+
+       result = g_hash_table_lookup(request_hash, &key);
+
+       if (result && result->opnum != opnum)
+               g_warning("Tag for response packet at frame %d is %d, not %d",
+                         pinfo->fd->num, result->opnum, opnum);
+       
+       return result;
+}
+
+/* Add a text item like "Response in frame %d" using some request_info */
+
+static void add_request_text(proto_tree *tree, tvbuff_t *tvb, int offset,
+                            request_hash_value *request_info)
+{
+       if (request_info && request_info->response_num)
+               proto_tree_add_text(tree, tvb, offset, 0,
+                                   "Response in frame %d",
+                                   request_info->response_num);
+}
+
+/* Add a text item like "Request in frame %d" using some request_info */
+
+static void add_response_text(proto_tree *tree, tvbuff_t *tvb, int offset,
+                            request_hash_value *request_info)
+{
+       if (request_info && request_info->request_num)
+               proto_tree_add_text(tree, tvb, offset, 0,
+                                   "Request in frame %d",
+                                   request_info->request_num);
+}
+
+/*
+ * Hash table for matching policy handles to printer names 
+ */
+
+static int printer_ndx;                /* Hack for printer names */
+
+#define POLICY_HND_HASH_INIT_COUNT 100
+
+static GHashTable *policy_hnd_hash;
+static GMemChunk *policy_hnd_hash_key_chunk;
+static GMemChunk *policy_hnd_hash_value_chunk;
+
+typedef struct {
+       guint8 policy_hnd[20];
+} policy_hnd_hash_key;
+
+typedef struct {
+       char *printer_name;
+} policy_hnd_hash_value;
+
+static void dump_policy_hnd(const guint8 *policy_hnd)
+{
+       int i, csum = 0;
+
+       for(i = 0; i < 20; i++) {
+               fprintf(stderr, "%02x ", policy_hnd[i]);
+               csum += policy_hnd[i];
+       }
+
+       fprintf(stderr, "- %d\n", csum);
+}
+
+static guint hash_policy_hnd(gconstpointer k)
+{
+        policy_hnd_hash_key *p = (policy_hnd_hash_key *)k;
+       guint hash;
+
+       /* Bytes 4-7 of the policy handle are a timestamp so should make a
+          reasonable hash value */
+
+       hash = p->policy_hnd[4] + (p->policy_hnd[5] << 8) +
+               (p->policy_hnd[6] << 16) + (p->policy_hnd[7] << 24);
+
+       return hash;
+}
+
+static gint compare_policy_hnd(gconstpointer k1, gconstpointer k2)
+{
+       policy_hnd_hash_key *p1 = (policy_hnd_hash_key *)k1;
+       policy_hnd_hash_key *p2 = (policy_hnd_hash_key *)k2;
+
+       return memcmp(p1->policy_hnd, p2->policy_hnd, 20) == 0;
+}
+
+static gboolean is_null_policy_hnd(const guint8 *policy_hnd)
+{
+       static guint8 null_policy_hnd[20];
+
+       return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
+}
+
+/* Associate a policy handle with a printer name */
+
+static void store_printer_name(const guint8 *policy_hnd, char *printer_name)
+{
+       policy_hnd_hash_key *key;
+       policy_hnd_hash_value *value;
+
+       if (is_null_policy_hnd(policy_hnd))
+               return;
+       
+       key = g_mem_chunk_alloc(policy_hnd_hash_key_chunk);
+       value = g_mem_chunk_alloc(policy_hnd_hash_value_chunk);
+
+       memcpy(key->policy_hnd, policy_hnd, 20);
+       value->printer_name = strdup(printer_name);
+
+       g_hash_table_insert(policy_hnd_hash, key, value);
+}
+
+/* Retrieve a printer name from a policy handle */
+
+static char *fetch_printer_name(const guint8 *policy_hnd)
+{
+       policy_hnd_hash_key key;
+       policy_hnd_hash_value *value;
+       
+       if (is_null_policy_hnd(policy_hnd))
+               return NULL;
+
+       memcpy(&key.policy_hnd, policy_hnd, 20);
+
+       value = g_hash_table_lookup(policy_hnd_hash, &key);
+
+       if (value)
+               return value->printer_name;
+
+       return NULL;
+}
+
+/* Delete the association between a policy handle and printer name */
+
+static void delete_printer_name(guint8 *policy_hnd)
+{
+}
+
+/* Read a policy handle and append the printer name associated with it to
+   the packet info column */
+
+static void append_printer_name(packet_info *pinfo, tvbuff_t *tvb,
+                               int offset, const guint8 *policy_hnd)
+{
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               char *printer_name;
+               
+               printer_name = fetch_printer_name(policy_hnd);
+
+               if (printer_name)
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
+                                       printer_name);
+       }
+}
+
+/* 
+ * New system for handling pointers and buffers.  We act more like the NDR
+ * specification and have a list of deferred pointers which are processed
+ * after a structure has been parsed.  
+ *
+ * Each structure has a parse function which takes as an argument a GList.
+ * As pointers are processed, they are appended onto this list.  When the
+ * structure is complete, the pointers (referents) are processed by calling
+ * prs_referents().  In the case of function arguments, the
+ * prs_struct_and_referents() function is called as pointers are always
+ * processed immediately after the argument.
+ */
+
+typedef int prs_fn(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                  proto_tree *tree, GList **dp_list, void **data);
+
+/* Deferred referent */
+
+struct deferred_ptr {
+       prs_fn *fn;             /* Parse function to call */
+       proto_tree *tree;       /* Tree context */
+};
+
+/* A structure to hold needed ethereal state to pass to GList foreach
+   iterator. */
+
+struct deferred_ptr_state {
+       tvbuff_t *tvb;
+       int *poffset;
+       packet_info *pinfo;
+       GList **dp_list;
+       void **ptr_data;
+};
+
+void defer_ptr(GList **list, prs_fn *fn, proto_tree *tree)
+{
+       struct deferred_ptr *dr;
+
+       dr = g_malloc(sizeof(struct deferred_ptr));
+
+       dr->fn = fn;
+       dr->tree = tree;
+       
+       *list = g_list_append(*list, dr);
+}
+
+/* Parse a pointer */
+
+static int prs_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                  proto_tree *tree, guint32 *data, char *name)
+{
+       guint32 ptr;
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &ptr, NULL);
+
+       if (tree && name)
+               proto_tree_add_text(tree, tvb, offset - 4, 4, 
+                                   "%s pointer: 0x%08x", name, ptr);
+
+       if (data)
+               *data = ptr;
+
+       return offset;
+}
+
+/* Iterator function for prs_referents */
+
+static void dr_iterator(gpointer data, gpointer user_data)
+{
+       struct deferred_ptr *dp = (struct deferred_ptr *)data;
+       struct deferred_ptr_state *s = (struct deferred_ptr_state *)user_data;
+
+       /* Parse pointer */
+
+       *s->poffset = dp->fn(s->tvb, *s->poffset, s->pinfo, dp->tree, 
+                            s->dp_list, s->ptr_data);
+
+       if (s->ptr_data)
+               s->ptr_data++;          /* Ready for next parse fn */
+}
+
+/* Call the parse function for each element in the deferred pointers list.
+   If there are any additional pointers in these structures they are pushed
+   onto parent_dp_list. */ 
+
+int prs_referents(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                 proto_tree *tree, GList **dp_list, GList **list,
+                 void ***ptr_data)
+{
+       struct deferred_ptr_state s;
+       int new_offset = offset, list_len;
+
+       /* Create a list of void pointers to store return data */
+
+       if (ptr_data) {
+               list_len = g_list_length(*dp_list);
+               *ptr_data = malloc(list_len * sizeof(void *));
+       }
+
+       /* Set up iterator data */
+
+       s.tvb = tvb;
+       s.poffset = &new_offset;
+       s.pinfo = pinfo;
+       s.dp_list = dp_list;
+       s.ptr_data = ptr_data ? *ptr_data : NULL;
+
+       g_list_foreach(*list, dr_iterator, &s); 
+
+       *list = NULL;           /* XXX: free list */
+
+       return new_offset;
+}
+
+/* Parse a structure then clean up any deferred referants it creates. */
+
+static int prs_struct_and_referents(tvbuff_t *tvb, int offset,
+                                   packet_info *pinfo, proto_tree *tree,
+                                   prs_fn *fn, void **data, void ***ptr_data)
+{
+       GList *dp_list = NULL;
+
+       offset = fn(tvb, offset, pinfo, tree, &dp_list, data);
+
+       offset = prs_referents(tvb, offset, pinfo, tree, &dp_list,
+                              &dp_list, ptr_data);
+
+       return offset;
+}
+
+/* Parse a Win32 error, basically a DOS error.  The spoolss API doesn't
+   use NT status codes. */
+
+static int prs_werror(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                     proto_tree *tree, guint32 *data)
+{
+       guint32 status;
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &status, NULL);
+
+       if (tree)
+               proto_tree_add_text(tree, tvb, offset - 4, 4, "Status: %s",
+                                   val_to_str(status, DOS_errors, 
+                                              "Unknown error"));
+
+       if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
+                               val_to_str(status, DOS_errors, 
+                                          "Unknown error"));
+
+       if (data)
+               *data = status;
+
+       return offset;
+}
+
+/*
+ * SpoolssClosePrinter
+ */
+
+static int SpoolssClosePrinter_q(tvbuff_t *tvb, int offset,
+                                packet_info *pinfo, proto_tree *tree, 
+                                char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       const guint8 *policy_hnd;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "ClosePrinter request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, &policy_hnd);
+
+       append_printer_name(pinfo, tvb, offset, policy_hnd);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssClosePrinter_r(tvbuff_t *tvb, int offset, 
+                                packet_info *pinfo, proto_tree *tree, 
+                                char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "ClosePrinter response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/* Parse a UNISTR2 structure */
+
+static gint ett_UNISTR2 = -1;
+
+static int prs_UNISTR2_dp(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                         proto_tree *tree, GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 length, the_offset, max_len;
+       int old_offset = offset;
+       guint16 *data16;
+       char *text;
+       
+       offset = prs_uint32(tvb, offset, pinfo, tree, &length, NULL);
+       offset = prs_uint32(tvb, offset, pinfo, tree, &the_offset, NULL);
+       offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, NULL);
+
+       offset = prs_uint16s(tvb, offset, pinfo, tree, max_len, &data16, NULL);
+       
+       text = fake_unicode(data16, max_len);
+
+       item = proto_tree_add_text(tree, tvb, old_offset, offset - old_offset,
+                                  "UNISTR2: %s", text);
+
+       subtree = proto_item_add_subtree(item, ett_UNISTR2);    
+
+       if (data)
+               *data = text;
+       else
+               free(text);
+
+       proto_tree_add_text(subtree, tvb, old_offset, 4, "Length: %d", length);
+
+       old_offset += 4;
+
+       proto_tree_add_text(subtree, tvb, old_offset, 4, "Offset: %d", 
+                           the_offset);
+
+       old_offset += 4;
+
+       proto_tree_add_text(subtree, tvb, old_offset, 4, "Max length: %d",
+                           max_len);
+
+       old_offset += 4;
+
+       proto_tree_add_text(subtree, tvb, old_offset, max_len * 2, "Data");
+
+       return offset;
+}
+
+/*
+ * SpoolssGetPrinterData
+ */
+
+static int SpoolssGetPrinterData_q(tvbuff_t *tvb, int offset,
+                                  packet_info *pinfo, proto_tree *tree, 
+                                  char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       char *value_name;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "GetPrinterData request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, (void **)&value_name,
+                                         NULL);
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", value_name);
+
+       free(value_name);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Size");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssGetPrinterData_r(tvbuff_t *tvb, int offset,
+                                  packet_info *pinfo, proto_tree *tree, 
+                                  char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       guint32 size, type;
+
+       /* Update information fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "GetPrinterData response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &type, NULL);
+  
+       proto_tree_add_text(tree, tvb, offset - 4, 4, "Type: %s",
+                           val_to_str(type, reg_datatypes, "Unknown type"));
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &size, "Size");
+  
+       offset = prs_uint8s(tvb, offset, pinfo, tree, size, NULL, "Data");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Needed");
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/*
+ * SpoolssGetPrinterDataEx
+ */
+
+static int SpoolssGetPrinterDataEx_q(tvbuff_t *tvb, int offset,
+                                    packet_info *pinfo, proto_tree *tree, 
+                                    char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       char *key_name, *value_name;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "GetPrinterDataEx request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, (void **)&key_name,
+                                         NULL);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, (void **)&value_name,
+                                         NULL);
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s/%s", 
+                               key_name, value_name);
+
+       free(key_name);
+       free(value_name);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Size");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssGetPrinterDataEx_r(tvbuff_t *tvb, int offset,
+                                    packet_info *pinfo, proto_tree *tree, 
+                                    char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       guint32 size, type;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "GetPrinterDataEx response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &type, NULL);
+  
+       proto_tree_add_text(tree, tvb, offset - 4, 4, "Type: %s",
+                           val_to_str(type, reg_datatypes, "Unknown type"));
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &size, "Size");
+  
+       offset = prs_uint8s(tvb, offset, pinfo, tree, size, NULL, "Data");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Needed");
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/*
+ * SpoolssSetPrinterData
+ */
+
+static int SpoolssSetPrinterData_q(tvbuff_t *tvb, int offset,
+                                  packet_info *pinfo, proto_tree *tree, 
+                                  char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       char *value_name;
+       guint32 type, max_len;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "SetPrinterData request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, (void **)&value_name,
+                                         NULL);
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", value_name);
+
+       free(value_name);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &type, NULL);
+
+       proto_tree_add_text(tree, tvb, offset - 4, 4, "Type: %s",
+                           val_to_str(type, reg_datatypes, "Unknown type"));
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, "Max length");
+
+       offset = prs_uint8s(tvb, offset, pinfo, tree, max_len, NULL,
+                           "Data");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Real length");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssSetPrinterData_r(tvbuff_t *tvb, int offset,
+                                  packet_info *pinfo, proto_tree *tree, 
+                                  char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "SetPrinterData response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/*
+ * SpoolssSetPrinterDataEx
+ */
+
+static int SpoolssSetPrinterDataEx_q(tvbuff_t *tvb, int offset,
+                                    packet_info *pinfo, proto_tree *tree, 
+                                    char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       char *key_name, *value_name;
+       guint32 type, max_len;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "SetPrinterDataEx request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, (void **)&key_name,
+                                         NULL);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, (void **)&value_name,
+                                         NULL);
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s/%s",
+                               key_name, value_name);
+
+       free(key_name);
+       free(value_name);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &type, NULL);
+
+       proto_tree_add_text(tree, tvb, offset - 4, 4, "Type: %s",
+                           val_to_str(type, reg_datatypes, "Unknown type"));
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, "Max length");
+
+       offset = prs_uint8s(tvb, offset, pinfo, tree, max_len, NULL,
+                           "Data");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Real length");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssSetPrinterDataEx_r(tvbuff_t *tvb, int offset,
+                                    packet_info *pinfo, proto_tree *tree, 
+                                    char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "SetPrinterDataEx response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/* Yet another way to represent a unicode string - sheesh. */
+
+static int prs_uint16uni(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                        proto_tree *tree, void **data, char *name)
+{
+       gint len = 0, remaining, i;
+       guint16 *ptr;
+       char *text;
+
+       offset = prs_align(offset, 2);
+
+       /* Get a pointer to remaining data in buffer */
+
+       remaining = tvb_length_remaining(tvb, offset);
+       ptr = (guint16 *)tvb_get_ptr(tvb, offset, remaining);
+
+       for (i = 0; i < remaining / 2; i++) {
+               if (ptr[i] == 0)
+                       break;
+               len++;
+       }
+
+       text = fake_unicode(ptr, len);
+
+       if (name) 
+               proto_tree_add_text(tree, tvb, offset, (len + 1) * 2, 
+                                   "%s: %s", name ? name : "UINT16UNI", 
+                                   text);
+
+       if (data)
+               *data = text;
+       else
+               free(text);
+
+       return offset + (len + 1) * 2;
+}
+
+/*
+ * DEVMODE
+ */
+
+static gint ett_DEVMODE = -1;
+
+static int prs_DEVMODE(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                      proto_tree *tree, GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+       guint16 extra;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "DEVMODE");
+
+       subtree = proto_item_add_subtree(item, ett_DEVMODE);
+
+       offset = prs_uint16uni(tvb, offset, pinfo, subtree, NULL, "Devicename");
+
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Spec version");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Driver version");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Size");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, &extra, "Driver extra");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Fields");
+
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Orientation");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Paper size");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Paper length");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Paper width");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Scale");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Copies");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Default source");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Print quality");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Color");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Duplex");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Y resolution");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "TT option");
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Collate");
+
+       offset = prs_uint16s(tvb, offset, pinfo, subtree, 32, NULL, "Buffer");
+
+       offset = prs_uint16uni(tvb, offset, pinfo, subtree, NULL, "Form name");
+
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Log pixels");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Bits per pel");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Pels width");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Pels height");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Display flags");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Display frequency");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "ICM method");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "ICM intent");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Media type");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Dither type");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Reserved");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Reserved");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Panning width");
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Panning height");
+       
+       if (extra != 0)
+               offset = prs_uint8s(tvb, offset, pinfo, subtree, extra, NULL,
+                                   "Private");
+
+       return offset;
+}
+
+/*
+ * Relative string given by offset into the current buffer.  Note that
+ * the offset for subsequent relstrs are against the structure start, not
+ * the point where the offset is parsed from.
+ */
+
+static gint ett_RELSTR = -1;
+
+static int prs_relstr(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                     proto_tree *tree, GList **dp_list, int struct_start,
+                     void **data, char *name)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 relstr_offset, relstr_start, relstr_end;
+       guint16 *ptr;
+       char *text;
+       gint len = 0, remaining, i;
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &relstr_offset, NULL);
+
+       relstr_start = relstr_offset + struct_start;
+
+       relstr_end = prs_uint16uni(tvb, relstr_start, pinfo, tree, 
+                                  (void **)&text, NULL);
+
+       item = proto_tree_add_text(tree, tvb, relstr_start, 
+                                  relstr_end - relstr_start, "%s: %s", 
+                                  name ? name : "RELSTR", text);
+
+       subtree = proto_item_add_subtree(item, ett_RELSTR);
+
+       if (data)
+               *data = text;
+       else
+               free(text);
+
+       proto_tree_add_text(subtree, tvb, offset - 4, 4, 
+                           "Relative offset: %d", relstr_offset);
+
+       proto_tree_add_text(subtree, tvb, relstr_start, 
+                           relstr_end - relstr_start, "Data");
+
+       return offset;
+}
+
+/*
+ * PRINTER_INFO_0
+ */
+
+static gint ett_PRINTER_INFO_0 = -1;
+
+static int prs_PRINTER_INFO_0(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, GList **dp_list, void **data)
+{
+       int struct_start = offset;
+
+       offset = prs_relstr(tvb, offset, pinfo, tree, dp_list, struct_start,
+                           NULL, "Printer name");
+       offset = prs_relstr(tvb, offset, pinfo, tree, dp_list, struct_start,
+                           NULL, "Server name");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "CJobs");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Total jobs");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Total bytes");
+
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Year");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Month");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Day of week");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Day");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Hour");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Minute");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Second");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Milliseconds");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Global counter");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Total pages");
+
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Major version");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Build version");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Session counter");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Printer errors");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Change id");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Status");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "C_setprinter");
+
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+       offset = prs_uint16(tvb, offset, pinfo, tree, NULL, "Unknown");
+
+       return offset;
+}
+
+/*
+ * PRINTER_INFO_1
+ */
+
+static gint ett_PRINTER_INFO_1 = -1;
+
+static int prs_PRINTER_INFO_1(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, GList **dp_list, void **data)
+{
+       return offset;
+}
+
+/*
+ * PRINTER_INFO_2
+ */
+
+static gint ett_PRINTER_INFO_2 = -1;
+
+static int prs_PRINTER_INFO_2(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, GList **dp_list, void **data)
+{
+       return offset;
+}
+
+/*
+ * PRINTER_INFO_3
+ */
+
+static gint ett_PRINTER_INFO_3 = -1;
+
+static int prs_PRINTER_INFO_3(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, GList **dp_list, void **data)
+{
+       return offset;
+}
+
+/*
+ * DEVMODE_CTR
+ */
+
+static gint ett_DEVMODE_CTR = -1;
+
+static int prs_DEVMODE_CTR(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                          proto_tree *tree, GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "DEVMODE_CTR");
+
+       subtree = proto_item_add_subtree(item, ett_DEVMODE_CTR);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Size");
+               
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "Devicemode");
+
+       if (ptr)
+               defer_ptr(dp_list, prs_DEVMODE, subtree);
+
+       return offset;
+}
+
+/*
+ * PRINTER_DEFAULT structure
+ */
+
+static gint ett_PRINTER_DEFAULT = -1;
+
+static int prs_PRINTER_DEFAULT(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                              proto_tree *tree, GList **dp_list, void **data)
+{
+       GList *child_dp_list = NULL;
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0, access;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "PRINTER_DEFAULT");
+
+       subtree = proto_item_add_subtree(item, ett_PRINTER_DEFAULT);
+
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "Datatype");
+
+       /* Not sure why this isn't a deferred pointer.  I think this may be
+          two structures stuck together. */
+
+       if (ptr)
+               offset = prs_UNISTR2_dp(tvb, offset, pinfo, subtree, dp_list, 
+                                       NULL);
+
+       offset = prs_DEVMODE_CTR(tvb, offset, pinfo, subtree,
+                                &child_dp_list, NULL);
+               
+       offset = prs_uint32(tvb, offset, pinfo, subtree, &access, NULL);
+
+       proto_tree_add_text(subtree, tvb, offset - 4, 4, 
+                           "Access required: 0x%08x", access);
+
+       offset = prs_referents(tvb, offset, pinfo, subtree, dp_list,
+                              &child_dp_list, NULL);
+
+       return offset;
+}
+
+/*
+ * USER_LEVEL_1 structure
+ */
+
+static gint ett_USER_LEVEL_1 = -1;
+
+static int prs_USER_LEVEL_1(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                           proto_tree *tree, GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "USER_LEVEL_1");
+
+       subtree = proto_item_add_subtree(item, ett_USER_LEVEL_1);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Size");
+
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "Client name");
+
+       if (ptr)
+               defer_ptr(dp_list, prs_UNISTR2_dp, subtree);
+
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "User name");
+
+       if (ptr)
+               defer_ptr(dp_list, prs_UNISTR2_dp, subtree);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Build");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Major");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Minor");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Processor");
+       
+       return offset;
+}
+
+/*
+ * USER_LEVEL structure
+ */
+
+static gint ett_USER_LEVEL = -1;
+
+static int prs_USER_LEVEL(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                         proto_tree *tree, GList **parent_dp_list,
+                         void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+       guint32 level;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "USER_LEVEL");
+
+       subtree = proto_item_add_subtree(item, ett_USER_LEVEL);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, &level, "Info level");
+
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "User level");
+
+       if (ptr) {
+               switch (level) {
+               case 1:
+                       defer_ptr(parent_dp_list, prs_USER_LEVEL_1, subtree);
+                       break;
+               default:
+                       proto_tree_add_text(
+                               tree, tvb, offset, 0, 
+                               "[GetPrinter level %d not decoded]", level);
+                       break;
+               }
+       }
+
+       return offset;
+}
+
+/*
+ * SpoolssOpenPrinterEx
+ */
+
+static int SpoolssOpenPrinterEx_q(tvbuff_t *tvb, int offset, 
+                                 packet_info *pinfo, proto_tree *tree, 
+                                 char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       char *printer_name;
+       guint32 ptr = 0;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "OpenPrinterEx request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_OPENPRINTEREX);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+
+       /* Parse packet */
+
+       offset = prs_ptr(tvb, offset, pinfo, tree, &ptr, "Printer name");
+
+       if (ptr) {
+               char *printer_name;
+
+               offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                                 prs_UNISTR2_dp, 
+                                                 (void **)&printer_name,
+                                                 NULL); 
+
+               if (check_col(pinfo->cinfo, COL_INFO))
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
+                                       printer_name);
+               
+               if (!request_info) {
+
+                       /* Store printer name to match with response packet */
+
+                       store_request_info_OpenPrinterEx(pinfo, di, 
+                                                        printer_name);
+               }
+
+               free(printer_name);
+       }
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_PRINTER_DEFAULT, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "User switch");
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_USER_LEVEL, NULL, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssOpenPrinterEx_r(tvbuff_t *tvb, int offset,
+                                 packet_info *pinfo, proto_tree *tree, 
+                                 char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       int start_offset = offset;
+       guint32 status;
+
+       /* Display informational data */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "OpenPrinterEx response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_OPENPRINTEREX);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_werror(tvb, offset, pinfo, tree, &status);
+
+       if (status == 0) {
+               const guint8 *policy_hnd;
+
+               /* Associate the returned printer handle with a name */
+
+               policy_hnd = tvb_get_ptr(tvb, start_offset, 20);
+
+               if (request_info) {
+                       char *printer_name;
+
+                       printer_name = 
+                               request_info->data.OpenPrinterEx.printer_name;
+
+                       if (printer_name)
+                               store_printer_name(policy_hnd, printer_name);
+               }
+       }
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/*
+ * NOTIFY_OPTION_DATA structure
+ */
+
+static gint ett_NOTIFY_OPTION_DATA = -1;
+
+static int prs_NOTIFY_OPTION_DATA(tvbuff_t *tvb, int offset, 
+                                 packet_info *pinfo, proto_tree *tree,
+                                 GList **parent_dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 count, i;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "NOTIFY_OPTION_DATA");
+
+       subtree = proto_item_add_subtree(item, ett_NOTIFY_OPTION_DATA);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, &count, "Count");
+
+       for (i = 0; i < count; i++)
+               offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, 
+                                   "Field");
+
+       return offset;
+}
+
+/*
+ * NOTIFY_OPTION structure
+ */
+
+static gint ett_NOTIFY_OPTION = -1;
+
+static int prs_NOTIFY_OPTION(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                            proto_tree *tree, GList **parent_dp_list,
+                            void **data) 
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "NOTIFY_OPTION");
+
+       subtree = proto_item_add_subtree(item, ett_NOTIFY_OPTION);
+
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Type");
+
+       offset = prs_uint16(tvb, offset, pinfo, subtree, NULL, "Reserved");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Reserved");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Reserved");
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Count");
+
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "Fields");
+
+       if (ptr)
+               defer_ptr(parent_dp_list, prs_NOTIFY_OPTION_DATA, subtree);
+
+       return offset;
+}
+
+/*
+ * NOTIFY_OPTION_CTR structure
+ */
+
+static gint ett_NOTIFY_OPTION_CTR = -1;
+
+static int prs_NOTIFY_OPTION_CTR(tvbuff_t *tvb, int offset, 
+                                packet_info *pinfo, proto_tree *tree,
+                                GList **dp_list, void **data)
+{
+       GList *child_dp_list = NULL;
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 count, i, ptr;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, 
+                                  "NOTIFY_OPTION_CTR");
+
+       subtree = proto_item_add_subtree(item, ett_NOTIFY_OPTION_CTR);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, &count, "Count");
+
+       for (i = 0; i < count; i++)
+               offset = prs_NOTIFY_OPTION(tvb, offset, pinfo, subtree, 
+                                          &child_dp_list, NULL);
+
+       offset = prs_referents(tvb, offset, pinfo, subtree, dp_list,
+                              &child_dp_list, NULL);
+
+       return offset;
+}
+
+/*
+ * NOTIFY_OPTION structure
+ */
+
+gint ett_NOTIFY_OPTION_ARRAY = -1;
+
+static int prs_NOTIFY_OPTION_ARRAY(tvbuff_t *tvb, int offset,
+                                  packet_info *pinfo, proto_tree *tree,
+                                  GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, 
+                                  "NOTIFY_OPTION_ARRAY");
+
+       subtree = proto_item_add_subtree(item, ett_NOTIFY_OPTION_ARRAY);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Version");
+       
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Flags");
+       
+       offset = prs_uint32(tvb, offset, pinfo, subtree, NULL, "Count");
+       
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "Option type");
+
+       if (ptr)
+               defer_ptr(dp_list, prs_NOTIFY_OPTION_CTR, subtree);
+
+       return offset;
+}
+
+/*
+ * SpoolssRFFPCNEX
+ */
+
+static int SpoolssRFFPCNEX_q(tvbuff_t *tvb, int offset, 
+                            packet_info *pinfo, proto_tree *tree, 
+                            char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       char *printer_name;
+       guint32 ptr = 0;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "RFFPCNEX request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Flags");
+       
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Options");
+       
+       offset = prs_ptr(tvb, offset, pinfo, tree, &ptr, "Local machine");
+
+       if (ptr) {
+               offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                                 prs_UNISTR2_dp,
+                                                 (void *)&printer_name, NULL);
+
+               if (check_col(pinfo->cinfo, COL_INFO))
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
+                                       printer_name);
+       }
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Printer local");
+       
+       offset = prs_ptr(tvb, offset, pinfo, tree, &ptr, "Option");
+       
+       if (ptr) {
+               offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                                 prs_NOTIFY_OPTION_ARRAY,
+                                                 NULL, NULL);
+       }
+       
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+static int SpoolssRFFPCNEX_r(tvbuff_t *tvb, int offset,
+                            packet_info *pinfo, proto_tree *tree, 
+                            char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "RFFPCNEX response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}
+
+/*
+ * SpoolssReplyOpenPrinter
+ */
+
+static int SpoolssReplyOpenPrinter_q(tvbuff_t *tvb, int offset, 
+                                    packet_info *pinfo, proto_tree *tree, 
+                                    char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       guint32 ptr = 0, type;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "ReplyOpenPrinter request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_UNISTR2_dp, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Printer");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &type, NULL);
+
+       proto_tree_add_text(tree, tvb, offset - 4, 4, "Type: %s",
+                           val_to_str(type, reg_datatypes, "Unknown type"));
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Unknown"); 
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssReplyOpenPrinter_r(tvbuff_t *tvb, int offset, 
+                                    packet_info *pinfo, proto_tree *tree, 
+                                    char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "ReplyOpenPrinter response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * BUFFER_DATA
+ */
+
+static gint ett_BUFFER_DATA = -1;
+static gint ett_BUFFER_DATA_BUFFER = -1;
+
+struct BUFFER_DATA {
+       guint8 *data8;          /* Pointer to buffer data */
+       proto_item *item;       /* proto_item holding proto_tree */
+       proto_tree *tree;       /* proto_tree holding buffer data */
+       tvbuff_t *tvb;          
+       int offset;             /* Offset where data starts in tvb*/
+};
+
+static int prs_BUFFER_DATA(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                          proto_tree *tree, GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree, *subsubtree;
+       guint32 ptr = 0, size;
+       guint8 *data8;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "BUFFER_DATA");
+
+       subtree = proto_item_add_subtree(item, ett_BUFFER_DATA);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, &size, "Size");
+
+       item = proto_tree_add_text(subtree, tvb, offset, size, "Data");
+
+       subsubtree = proto_item_add_subtree(item, ett_BUFFER_DATA_BUFFER);
+
+       offset = prs_uint8s(tvb, offset, pinfo, subsubtree, size, &data8, 
+                           NULL);
+
+       /* Return some info which will help the caller "cast" the buffer
+          data and dissect it further. */
+
+       if (data) {
+               struct BUFFER_DATA *bd;
+
+               bd = (struct BUFFER_DATA *)malloc(sizeof(struct BUFFER_DATA));
+
+               bd->data8 = data8;
+               bd->item = item;
+               bd->tree = subsubtree;
+               bd->tvb = tvb;
+               bd->offset = offset - size;
+
+               *data = bd;
+       }
+
+       return offset;
+}
+
+/*
+ * BUFFER
+ */
+
+static gint ett_BUFFER = -1;
+
+static int prs_BUFFER(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                     proto_tree *tree, GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, "BUFFER");
+
+       subtree = proto_item_add_subtree(item, ett_BUFFER);
+
+       offset = prs_ptr(tvb, offset, pinfo, subtree, &ptr, "Data");
+
+       if (ptr)
+               defer_ptr(dp_list, prs_BUFFER_DATA, subtree);
+
+       return offset;
+}
+
+/*
+ * SpoolssGetPrinter
+ */
+
+static int SpoolssGetPrinter_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                              proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       guint32 level;
+       const guint8 *policy_hnd;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "GetPrinter request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_GETPRINTER);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, &policy_hnd);
+
+       append_printer_name(pinfo, tvb, offset, policy_hnd);
 
-static int proto_dcerpc_spoolss = -1;
-static gint ett_dcerpc_spoolss = -1;
+       offset = prs_uint32(tvb, offset, pinfo, tree, &level, "Level");
 
-static e_uuid_t uuid_dcerpc_spoolss = {
-        0x12345678, 0x1234, 0xabcd,
-        { 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }
-};
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", level %d", level);
 
-static guint16 ver_dcerpc_spoolss = 1;
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_BUFFER, NULL, NULL);
+
+       if (!request_info) {
+               
+               /* Store info level to match with response packet */
+
+               store_request_info_GetPrinter(pinfo, di, level);
+       }
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Offered");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssGetPrinter_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                               proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       void **data_list;
+       struct BUFFER_DATA *bd;
+       guint8 *data8;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "GetPrinter response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_GETPRINTER);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_BUFFER, (void **)&data8, 
+                                         &data_list);
+
+       bd = (struct BUFFER_DATA *)data_list[0];
+
+       if (bd->tree && request_info) {
+               gint16 level = request_info->data.GetPrinter.level;
+
+               proto_item_append_text(bd->item, ", PRINTER_INFO_%d", level);
+
+               switch (level) {
+               case 0:
+                       prs_PRINTER_INFO_0(bd->tvb, bd->offset, pinfo, 
+                                          bd->tree, &dp_list, NULL);
+                       break;
+
+               default:
+                       proto_tree_add_text(tree, tvb, offset, 0,
+                                           "[Unimplemented info level %d]",
+                                           level);
+                       break;
+               }
+       }
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Needed");
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * SPOOL_PRINTER_INFO_LEVEL
+ */
+
+static gint ett_SPOOL_PRINTER_INFO_LEVEL = -1;
+
+static int prs_SPOOL_PRINTER_INFO_LEVEL(tvbuff_t *tvb, int offset, 
+                                       packet_info *pinfo, proto_tree *tree, 
+                                       GList **dp_list, void **data)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       guint32 ptr = 0, level;
+
+       item = proto_tree_add_text(tree, tvb, offset, 0, 
+                                  "SPOOL_PRINTER_INFO_LEVEL");
+
+       subtree = proto_item_add_subtree(item, ett_SPOOL_PRINTER_INFO_LEVEL);
+
+       offset = prs_uint32(tvb, offset, pinfo, subtree, &level, "Level");
+
+       /* ptr */
+
+       switch(level) {
+       }
+
+       return offset;
+}
+
+/*
+ * SpoolssSetPrinter
+ */
+
+static int SpoolssSetPrinter_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                              proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       guint32 level;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "SetPrinter request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &level, "Level");
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", level %d", level);
+
+       /* printer_info_level */
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_SPOOL_PRINTER_INFO_LEVEL,
+                                         NULL, NULL);
+
+       /* devmode_ctr */
+
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Command");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssSetPrinter_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                               proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "SetPrinter response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * SpoolssEnumForms
+ */
+
+static int SpoolssEnumForms_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+       guint32 level;
+       const guint8 *policy_hnd;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "EnumForms request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, &policy_hnd);
+       
+       append_printer_name(pinfo, tvb, offset, policy_hnd);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &level, "Level");
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", level %d", level);
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                         prs_BUFFER, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Offered");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssEnumForms_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       GList *dp_list = NULL;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "EnumForms response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree, 
+                                          prs_BUFFER, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Needed");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Num entries");
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);    
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * SpoolssDeletePrinter
+ */
+
+static int SpoolssDeletePrinter_q(tvbuff_t *tvb, int offset, 
+                                 packet_info *pinfo, proto_tree *tree, 
+                                 char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       const guint8 *policy_hnd;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "DeletePrinter request");
+       
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, &policy_hnd);
+
+       append_printer_name(pinfo, tvb, offset, policy_hnd);
+       
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssDeletePrinter_r(tvbuff_t *tvb, int offset, 
+                                 packet_info *pinfo, proto_tree *tree, 
+                                 char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "DeletePrinter response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * AddPrinterEx
+ */
+
+static int SpoolssAddPrinterEx_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                                proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       int start_offset = offset;
+       guint32 status;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "AddPrinterEx response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, NULL);
+
+       offset = prs_werror(tvb, offset, pinfo, tree, &status); 
+
+       if (status == 0) {
+               const guint8 *policy_hnd;
+               char *printer_name;
+
+               /* Associate the returned printer handle with a name */
+
+               policy_hnd = tvb_get_ptr(tvb, start_offset, 20);
+
+               printer_name = strdup("<printer name here>");
+
+               store_printer_name(policy_hnd, printer_name);
+
+               if (check_col(pinfo->cinfo, COL_INFO))
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", 
+                                       printer_name);
+
+               free(printer_name);
+       }
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * SpoolssEnumPrinterData
+ */
+
+static int SpoolssEnumPrinterData_q(tvbuff_t *tvb, int offset, 
+                                   packet_info *pinfo, proto_tree *tree, 
+                                   char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       const guint8 *policy_hnd;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "EnumPrinterData request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_policy_hnd(tvb, offset, pinfo, tree, &policy_hnd);
+
+       append_printer_name(pinfo, tvb, offset, policy_hnd);
+       
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Index");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Value size");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Data size");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssEnumPrinterData_r(tvbuff_t *tvb, int offset, 
+                                   packet_info *pinfo, proto_tree *tree, 
+                                   char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       guint32 data_size, type;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, 
+                           "EnumPrinterData response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Value size");
+
+       /* unistr2_dp */
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Real value size");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &type, NULL);
+
+       proto_tree_add_text(tree, tvb, offset - 4, 4, "Type: %s",
+                           val_to_str(type, reg_datatypes, "Unknown type"));
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &data_size, "Data size");
+
+       offset = prs_uint8s(tvb, offset, pinfo, tree, data_size, NULL,
+                           "Data");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Real data size");
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);    
+       
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+/*
+ * SpoolssEnumPrinters
+ */
+
+static int SpoolssEnumPrinters_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                                proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+       guint32 ptr, level;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "EnumPrinters request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Flags");
+
+       offset = prs_ptr(tvb, offset, pinfo, tree, &ptr, "Devicemode");
+
+       if (ptr)
+               offset = prs_struct_and_referents(tvb, offset, pinfo, tree,
+                                                 prs_UNISTR2_dp, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, &level, "Level");
+       
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", level, %d", level);
+       
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree, 
+                                         prs_BUFFER, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Offered");
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssEnumPrinters_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                                proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "EnumPrinters response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       offset = prs_struct_and_referents(tvb, offset, pinfo, tree, 
+                                         prs_BUFFER, NULL, NULL);
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Needed");
+
+       offset = prs_uint32(tvb, offset, pinfo, tree, NULL, "Returned");
+
+       offset = prs_werror(tvb, offset, pinfo, tree, NULL);    
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+#if 0
+
+/* Templates for new subdissectors */
+
+/*
+ * FOO
+ */
+
+static int SpoolssFoo_q(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                       proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "Foo request");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+
+       if (request_info)
+               add_request_text(tree, tvb, offset, request_info);
+       else 
+               store_request_info_none(pinfo, di);
+
+       /* Parse packet */
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+static int SpoolssFoo_r(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                       proto_tree *tree, char *drep)
+{
+       dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+       request_hash_value *request_info;
+
+       /* Update informational fields */
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_set_str(pinfo->cinfo, COL_INFO, "Foo response");
+
+       request_info = fetch_request_info(pinfo, di, SPOOLSS_DUMMY);
+       add_response_text(tree, tvb, offset, request_info);
+
+       if (request_info)
+               request_info->response_num = pinfo->fd->num;
+
+       /* Parse packet */
+
+       if (tvb_length_remaining(tvb, offset) != 0)
+               proto_tree_add_text(tree, tvb, offset, 0, 
+                                   "[Long frame (%d bytes): SPOOLSS]",
+                                   tvb_length_remaining(tvb, offset));
+
+       return offset;
+}      
+
+#endif
+
+/*
+ * List of subdissectors for this pipe.
+ */
 
 static dcerpc_sub_dissector dcerpc_spoolss_dissectors[] = {
-        { SPOOLSS_ENUMPRINTERS, "SPOOLSS_ENUMPRINTERS", NULL, NULL },
+        { SPOOLSS_ENUMPRINTERS, "SPOOLSS_ENUMPRINTERS", 
+         SpoolssEnumPrinters_q, SpoolssEnumPrinters_r },
         { SPOOLSS_SETJOB, "SPOOLSS_SETJOB", NULL, NULL },
         { SPOOLSS_GETJOB, "SPOOLSS_GETJOB", NULL, NULL },
         { SPOOLSS_ENUMJOBS, "SPOOLSS_ENUMJOBS", NULL, NULL },
         { SPOOLSS_ADDPRINTER, "SPOOLSS_ADDPRINTER", NULL, NULL },
-        { SPOOLSS_DELETEPRINTER, "SPOOLSS_DELETEPRINTER", NULL, NULL },
-        { SPOOLSS_SETPRINTER, "SPOOLSS_SETPRINTER", NULL, NULL },
-        { SPOOLSS_GETPRINTER, "SPOOLSS_GETPRINTER", NULL, NULL },
+        { SPOOLSS_DELETEPRINTER, "SPOOLSS_DELETEPRINTER", 
+         SpoolssDeletePrinter_q, SpoolssDeletePrinter_r },
+        { SPOOLSS_SETPRINTER, "SPOOLSS_SETPRINTER", 
+         SpoolssSetPrinter_q, SpoolssSetPrinter_r },
+        { SPOOLSS_GETPRINTER, "SPOOLSS_GETPRINTER", 
+         SpoolssGetPrinter_q, SpoolssGetPrinter_r },
         { SPOOLSS_ADDPRINTERDRIVER, "SPOOLSS_ADDPRINTERDRIVER", NULL, NULL },
         { SPOOLSS_ENUMPRINTERDRIVERS, "SPOOLSS_ENUMPRINTERDRIVERS", NULL, NULL },
         { SPOOLSS_GETPRINTERDRIVERDIRECTORY, "SPOOLSS_GETPRINTERDRIVERDIRECTORY", NULL, NULL },
@@ -65,47 +2602,137 @@ static dcerpc_sub_dissector dcerpc_spoolss_dissectors[] = {
         { SPOOLSS_ENDDOCPRINTER, "SPOOLSS_ENDDOCPRINTER", NULL, NULL },
         { SPOOLSS_ADDJOB, "SPOOLSS_ADDJOB", NULL, NULL },
         { SPOOLSS_SCHEDULEJOB, "SPOOLSS_SCHEDULEJOB", NULL, NULL },
-        { SPOOLSS_GETPRINTERDATA, "SPOOLSS_GETPRINTERDATA", NULL, NULL },
-        { SPOOLSS_SETPRINTERDATA, "SPOOLSS_SETPRINTERDATA", NULL, NULL },
-        { SPOOLSS_CLOSEPRINTER, "SPOOLSS_CLOSEPRINTER", NULL, NULL },
+        { SPOOLSS_GETPRINTERDATA, "SPOOLSS_GETPRINTERDATA", 
+         SpoolssGetPrinterData_q, SpoolssGetPrinterData_r },   
+        { SPOOLSS_SETPRINTERDATA, "SPOOLSS_SETPRINTERDATA", 
+         SpoolssSetPrinterData_q, SpoolssSetPrinterData_r },
+        { SPOOLSS_CLOSEPRINTER, "SPOOLSS_CLOSEPRINTER", 
+         SpoolssClosePrinter_q, SpoolssClosePrinter_r },
         { SPOOLSS_ADDFORM, "SPOOLSS_ADDFORM", NULL, NULL },
         { SPOOLSS_DELETEFORM, "SPOOLSS_DELETEFORM", NULL, NULL },
         { SPOOLSS_GETFORM, "SPOOLSS_GETFORM", NULL, NULL },
         { SPOOLSS_SETFORM, "SPOOLSS_SETFORM", NULL, NULL },
-        { SPOOLSS_ENUMFORMS, "SPOOLSS_ENUMFORMS", NULL, NULL },
+        { SPOOLSS_ENUMFORMS, "SPOOLSS_ENUMFORMS", 
+         SpoolssEnumForms_q, SpoolssEnumForms_r },
         { SPOOLSS_ENUMPORTS, "SPOOLSS_ENUMPORTS", NULL, NULL },
         { SPOOLSS_ENUMMONITORS, "SPOOLSS_ENUMMONITORS", NULL, NULL },
         { SPOOLSS_ENUMPRINTPROCDATATYPES, "SPOOLSS_ENUMPRINTPROCDATATYPES", NULL, NULL },
         { SPOOLSS_GETPRINTERDRIVER2, "SPOOLSS_GETPRINTERDRIVER2", NULL, NULL },
         { SPOOLSS_FCPN, "SPOOLSS_FCPN", NULL, NULL },
-        { SPOOLSS_REPLYOPENPRINTER, "SPOOLSS_REPLYOPENPRINTER", NULL, NULL },
+        { SPOOLSS_REPLYOPENPRINTER, "SPOOLSS_REPLYOPENPRINTER", 
+         SpoolssReplyOpenPrinter_q, SpoolssReplyOpenPrinter_r },
         { SPOOLSS_REPLYCLOSEPRINTER, "SPOOLSS_REPLYCLOSEPRINTER", NULL, NULL },
-        { SPOOLSS_RFFPCNEX, "SPOOLSS_RFFPCNEX", NULL, NULL },
+        { SPOOLSS_RFFPCNEX, "SPOOLSS_RFFPCNEX",
+         SpoolssRFFPCNEX_q, SpoolssRFFPCNEX_r },
         { SPOOLSS_RRPCN, "SPOOLSS_RRPCN", NULL, NULL },
         { SPOOLSS_RFNPCNEX, "SPOOLSS_RFNPCNEX", NULL, NULL },
-        { SPOOLSS_OPENPRINTEREX, "SPOOLSS_OPENPRINTEREX", NULL, NULL },
-        { SPOOLSS_ADDPRINTEREX, "SPOOLSS_ADDPRINTEREX", NULL, NULL },
-        { SPOOLSS_ENUMPRINTERDATA, "SPOOLSS_ENUMPRINTERDATA", NULL, NULL },
+        { SPOOLSS_OPENPRINTEREX, "SPOOLSS_OPENPRINTEREX", 
+         SpoolssOpenPrinterEx_q, SpoolssOpenPrinterEx_r },
+        { SPOOLSS_ADDPRINTEREX, "SPOOLSS_ADDPRINTEREX", 
+         NULL, SpoolssAddPrinterEx_r },
+        { SPOOLSS_ENUMPRINTERDATA, "SPOOLSS_ENUMPRINTERDATA", 
+         SpoolssEnumPrinterData_q, SpoolssEnumPrinterData_r },
         { SPOOLSS_DELETEPRINTERDATA, "SPOOLSS_DELETEPRINTERDATA", NULL, NULL },
-        { SPOOLSS_GETPRINTERDATAEX, "SPOOLSS_GETPRINTERDATAEX", NULL, NULL },
-        { SPOOLSS_SETPRINTERDATAEX, "SPOOLSS_SETPRINTERDATAEX", NULL, NULL },
+        { SPOOLSS_GETPRINTERDATAEX, "SPOOLSS_GETPRINTERDATAEX", 
+         SpoolssGetPrinterDataEx_q, SpoolssGetPrinterDataEx_r },
+        { SPOOLSS_SETPRINTERDATAEX, "SPOOLSS_SETPRINTERDATAEX", 
+         SpoolssSetPrinterDataEx_q, SpoolssSetPrinterDataEx_r },
 
         {0, NULL, NULL,  NULL },
 };
 
+/*
+ * Dissector initialisation function
+ */
+
+static void spoolss_init(void)
+{
+       /* Initialise policy handle to printer name hash table */
+
+       if (policy_hnd_hash_key_chunk)
+               g_mem_chunk_destroy(policy_hnd_hash_key_chunk);
+
+       if (policy_hnd_hash_value_chunk)
+               g_mem_chunk_destroy(policy_hnd_hash_value_chunk);
+
+       policy_hnd_hash_key_chunk = g_mem_chunk_new(
+               "policy_hnd_hash_key_chunk", sizeof(policy_hnd_hash_key),
+               POLICY_HND_HASH_INIT_COUNT * sizeof(policy_hnd_hash_key),
+               G_ALLOC_ONLY);
+
+       policy_hnd_hash_value_chunk = g_mem_chunk_new(
+               "policy_hnd_hash_value_chunk", sizeof(policy_hnd_hash_value),
+               POLICY_HND_HASH_INIT_COUNT * sizeof(policy_hnd_hash_value),
+               G_ALLOC_ONLY);
+
+       policy_hnd_hash = g_hash_table_new(hash_policy_hnd,
+                                          compare_policy_hnd);
+
+       /* Initialise request/response matching hash table */
+
+       if (request_hash_key_chunk)
+               g_mem_chunk_destroy(request_hash_key_chunk);
+
+       request_hash_key_chunk = g_mem_chunk_new(
+               "request_hash_key_chunk", sizeof(request_hash_key),
+               REQUEST_HASH_INIT_COUNT * sizeof(request_hash_key),
+               G_ALLOC_ONLY);
+
+       request_hash_value_chunk = g_mem_chunk_new(
+               "request_hash_value_chunk", sizeof(request_hash_value),
+               REQUEST_HASH_INIT_COUNT * sizeof(request_hash_value),
+               G_ALLOC_ONLY);
+
+       request_hash = g_hash_table_new(hash_request, compare_request);
+}
+
+/* Protocol registration */
+
+static int proto_dcerpc_spoolss = -1;
+static gint ett_dcerpc_spoolss = -1;
+
 void 
 proto_register_dcerpc_spoolss(void)
 {
         static gint *ett[] = {
                 &ett_dcerpc_spoolss,
+               &ett_NOTIFY_OPTION_ARRAY,
+               &ett_NOTIFY_OPTION_CTR,
+               &ett_NOTIFY_OPTION,
+               &ett_NOTIFY_OPTION_DATA,
+               &ett_PRINTER_DEFAULT,
+               &ett_DEVMODE_CTR,
+               &ett_DEVMODE,
+               &ett_USER_LEVEL,
+               &ett_USER_LEVEL_1,
+               &ett_BUFFER,
+               &ett_BUFFER_DATA,
+               &ett_BUFFER_DATA_BUFFER,
+               &ett_UNISTR2,
+               &ett_PRINTER_INFO_0,
+               &ett_PRINTER_INFO_1,
+               &ett_PRINTER_INFO_2,
+               &ett_PRINTER_INFO_3,
+               &ett_RELSTR,
         };
 
         proto_dcerpc_spoolss = proto_register_protocol(
                 "Microsoft Spool Subsystem", "SPOOLSS", "spoolss");
 
         proto_register_subtree_array(ett, array_length(ett));
+
+       register_init_routine(spoolss_init);
 }
 
+/* Protocol handoff */
+
+static e_uuid_t uuid_dcerpc_spoolss = {
+        0x12345678, 0x1234, 0xabcd,
+        { 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }
+};
+
+static guint16 ver_dcerpc_spoolss = 1;
+
 void
 proto_reg_handoff_dcerpc_spoolss(void)
 {
index 3f66b627c51673e0927b4f6d078284ba00955edf..9f04aeca90cef0e8daabfc549c646ba9d05fd194 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for DCERPC packet disassembly
  * Copyright 2001, Todd Sabin <tas@webspan.net>
  *
- * $Id: packet-dcerpc.c,v 1.22 2001/12/17 23:08:51 guy Exp $
+ * $Id: packet-dcerpc.c,v 1.23 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -436,7 +436,7 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
                     tvbuff_t *tvb, gint offset,
                     e_uuid_t *uuid, guint16 ver, 
                     guint16 opnum, gboolean is_rqst,
-                    char *drep)
+                    char *drep, dcerpc_info *info)
 {
     dcerpc_uuid_key key;
     dcerpc_uuid_value *sub_proto;
@@ -446,6 +446,7 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
     gchar *name = NULL;
     dcerpc_dissect_fnct_t *sub_dissect;
     const char *saved_proto;
+    void *saved_private_data;
 
     key.uuid = *uuid;
     key.ver = ver;
@@ -498,9 +499,14 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
     sub_dissect = is_rqst ? proc->dissect_rqst : proc->dissect_resp;
     if (sub_dissect) {
         saved_proto = pinfo->current_proto;
+       saved_private_data = pinfo->private_data;
         pinfo->current_proto = sub_proto->name;
+       pinfo->private_data = (void *)info;
+
         sub_dissect (tvb, offset, pinfo, sub_tree, drep);
+
         pinfo->current_proto = saved_proto;
+       pinfo->private_data = saved_private_data;
     } else {
         length = tvb_length_remaining (tvb, offset);
         if (length > 0) {
@@ -841,6 +847,7 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
         dcerpc_conv_key key;
         dcerpc_conv_value *value;
         int length, reported_length, stub_length;
+       dcerpc_info di;
 
         key.conv = conv;
         key.ctx_id = ctx_id;
@@ -860,11 +867,14 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
               length = stub_length;
             if (reported_length > stub_length)
               reported_length = stub_length;
+           di.conv = conv;
+           di.call_id = hdr->call_id;
+           di.smb_fid = key.smb_fid;
             dcerpc_try_handoff (pinfo, tree, dcerpc_tree,
                                 tvb_new_subset (tvb, offset, length,
                                                 reported_length),
                                 0, &value->uuid, value->ver,
-                                opnum, TRUE, hdr->drep);
+                                opnum, TRUE, hdr->drep, &di);
         }
     }
 }
@@ -905,6 +915,8 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
         int length, reported_length, stub_length;
 
         if (value) {
+            dcerpc_info di;
+
             /* handoff this call */
             length = tvb_length_remaining(tvb, offset);
             reported_length = tvb_reported_length_remaining(tvb, offset);
@@ -913,11 +925,14 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
               length = stub_length;
             if (reported_length > stub_length)
               reported_length = stub_length;
+           di.conv = conv;
+           di.call_id = hdr->call_id;
+           di.smb_fid = get_smb_fid(pinfo->private_data);
             dcerpc_try_handoff (pinfo, tree, dcerpc_tree,
                                 tvb_new_subset (tvb, offset, length,
                                                 reported_length),
                                 0, &value->uuid, value->ver,
-                                value->opnum, FALSE, hdr->drep);
+                                value->opnum, FALSE, hdr->drep, &di);
         }
     }
 }
@@ -1346,22 +1361,26 @@ dissect_dcerpc_dg (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     /*
      * Packet type specific stuff is next.
      */
+
     switch (hdr.ptype) {
     case PDU_REQ:
         dcerpc_call_add_map (hdr.seqnum, conv, hdr.opnum,
                              hdr.if_ver, &hdr.if_id);
         dcerpc_try_handoff (pinfo, tree, dcerpc_tree, tvb, offset,
-                            &hdr.if_id, hdr.if_ver, hdr.opnum, TRUE, hdr.drep);
+                            &hdr.if_id, hdr.if_ver, hdr.opnum, TRUE,
+                           hdr.drep, NULL);
         break;
     case PDU_RESP:
         {
             dcerpc_call_value *v = dcerpc_call_lookup (hdr.seqnum, conv);
             if (v) {
                 dcerpc_try_handoff (pinfo, tree, dcerpc_tree, tvb, offset,
-                                    &v->uuid, v->ver, v->opnum, FALSE, hdr.drep);
+                                    &v->uuid, v->ver, v->opnum, FALSE, 
+                                   hdr.drep, NULL);
             } else {
                 dcerpc_try_handoff (pinfo, tree, dcerpc_tree, tvb, offset,
-                                    &hdr.if_id, hdr.if_ver, hdr.opnum, FALSE, hdr.drep);
+                                    &hdr.if_id, hdr.if_ver, hdr.opnum, FALSE, 
+                                   hdr.drep, NULL);
             }
         }
         break;
index c2799ccb17c75beb44f1fdfa5f42adb943455b74..5b36a622b29baefc13e3ac9f9a97b73451d7c9e2 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-dcerpc.h
  * Copyright 2001, Todd Sabin <tas@webspan.net>
  *
- * $Id: packet-dcerpc.h,v 1.5 2001/12/06 23:30:35 guy Exp $
+ * $Id: packet-dcerpc.h,v 1.6 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -25,6 +25,8 @@
 #ifndef __PACKET_DCERPC_H__
 #define __PACKET_DCERPC_H__
 
+#include "conversation.h"
+
 typedef struct _e_uuid_t {
     guint32 Data1;
     guint16 Data2;
@@ -155,4 +157,12 @@ typedef struct _dcerpc_private_info {
     } data;
 } dcerpc_private_info;
 
+/* Private data passed to subdissectors from the main DCERPC dissector. */
+
+typedef struct _dcerpc_info {
+       conversation_t *conv;   /* Which TCP stream we are in */
+       guint32 call_id;        /* Context id for this call */
+       guint16 smb_fid;        /* FID for DCERPC over SMB */
+} dcerpc_info;
+
 #endif /* packet-dcerpc.h */
index 04198c42f5fead1e1371b5fa8b3b12f0034d6513..154b709fd6731c28b267251a97c55a7631685c54 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
  * 2001  Rewrite by Ronnie Sahlberg and Guy Harris
  *
- * $Id: packet-smb.c,v 1.189 2001/12/18 08:55:49 guy Exp $
+ * $Id: packet-smb.c,v 1.190 2002/01/03 20:42:40 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -12343,7 +12343,9 @@ static const value_string errcls_types[] = {
   { 0, NULL }
 };
 
-static const value_string DOS_errors[] = {
+const value_string DOS_errors[] = {
+  {0, "Success"},
+  {SMBE_insufficientbuffer, "Insufficient buffer"},
   {SMBE_badfunc, "Invalid function (or system call)"},
   {SMBE_badfile, "File not found (pathname error)"},
   {SMBE_badpath, "Directory not found"},
@@ -12379,6 +12381,13 @@ static const value_string DOS_errors[] = {
   {SMBE_unknownipc, "Unknown IPC Operation"},
   {SMBE_noipc, "Don't support ipc"},
   {SMBE_alreadyexists, "File already exists"},
+  {SMBE_unknownprinterdriver, "Unknown printer driver"},
+  {SMBE_invalidprintername, "Invalid printer name"},
+  {SMBE_printeralreadyexists, "Printer already exists"},
+  {SMBE_invaliddatatype, "Invalid data type"},
+  {SMBE_invalidenvironment, "Invalid environment"},
+  {SMBE_printerdriverinuse, "Printer driver in use"},
+  {SMBE_invalidparam, "Invalid parameter"},
   {0, NULL}
   };
 
diff --git a/smb.h b/smb.h
index 646ab9cbc7024333afb67ea8472b5a1e2aef7912..f7101398353b7f6265b33ea8994293612ec0207a 100644 (file)
--- a/smb.h
+++ b/smb.h
@@ -2,7 +2,7 @@
  * Defines for smb packet dissection
  * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
  *
- * $Id: smb.h,v 1.30 2001/12/06 23:30:36 guy Exp $
+ * $Id: smb.h,v 1.31 2002/01/03 20:42:41 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #define SMBE_unsup 50              /* Request unsupported, returned by Win 95, RJS 20Jun98 */
 #define SMBE_nosuchshare 67        /* Share does not exits */
 #define SMBE_filexists 80          /* File in operation already exists */
+#define SMBE_invalidparam 87      /* Invalid parameter */
 #define SMBE_cannotopen 110        /* Cannot open the file specified */
+#define SMBE_insufficientbuffer 122/* Insufficient buffer size */
 #define SMBE_unknownlevel 124
 #define SMBE_alreadyexists 183     /* File already exists */
 #define SMBE_badpipe 230           /* Named pipe invalid */
 #define SMBE_unknownipc 2142
 #define SMBE_noipc 66              /* don't support ipc */
 
+/* These errors seem to be only returned by the NT printer driver system */
+
+#define SMBE_unknownprinterdriver 1797 /* Unknown printer driver */
+#define SMBE_invalidprintername 1801   /* Invalid printer name */
+#define SMBE_printeralreadyexists 1802 /* Printer already exists */
+#define SMBE_invaliddatatype 1804      /* Invalid datatype */
+#define SMBE_invalidenvironment 1805   /* Invalid environment */
+#define SMBE_printerdriverinuse 3001   /* Printer driver in use */
+
 /* Error codes for the ERRSRV class */
 
 #define SMBE_error 1               /* Non specific error code */
@@ -284,7 +295,8 @@ extern gboolean smb_dcerpc_reassembly;
 extern GHashTable *dcerpc_fragment_table;
 
 /*
- * NT error codes.
+ * NT and DOS error codes used by other dissectors.
  */
 extern const value_string NT_errors[];
+extern const value_string DOS_errors[];
 #endif