Note that for THE3GPP_IPV6_DNS_SERVERS we probably *do* need to handle
[obnox/wireshark/wip.git] / packet-dcerpc-nt.c
index 5cdc91b30dd21aab9187d87a6657a42034bd5a34..8f79dded7d6d2902279c1a52ab9ce7cf04bc5144 100644 (file)
@@ -1,23 +1,23 @@
 /* packet-dcerpc-nt.c
  * Routines for DCERPC over SMB packet disassembly
- * Copyright 2001, Tim Potter <tpot@samba.org>
+ * Copyright 2001-2003, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc-nt.c,v 1.15 2002/03/13 07:38:34 sahlberg Exp $
+ * $Id: packet-dcerpc-nt.c,v 1.78 2004/01/19 20:10:35 jmayer Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
- * 
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  * dissectors for ethereal.
  */
 
-/* Align offset to a n-byte boundary */
+/*
+ * Used by several dissectors.
+ */
+const value_string platform_id_vals[] = {
+       { 300, "DOS" },
+       { 400, "OS/2" },
+       { 500, "Windows NT" },
+       { 600, "OSF" },
+       { 700, "VMS" },
+       { 0,   NULL }
+};
 
-int prs_align(int offset, int n)
-{
-       if (offset % n)
-               offset += n - (offset % n);
-       
-       return offset;
-}
+/* Parse some common RPC structures */
 
-/* Parse a 8-bit integer */
+gint ett_nt_unicode_string = -1; /* FIXME: make static */
 
-int prs_uint8(tvbuff_t *tvb, int offset, packet_info *pinfo,
-             proto_tree *tree, guint8 *data, char *name)
-{
-       guint8 i;
-       
-       /* No alignment required */
+/* Dissect a counted string as a callback to dissect_ndr_pointer_cb() */
 
-       i = tvb_get_guint8(tvb, offset);
-       offset++;
+static int hf_nt_cs_len = -1;
+static int hf_nt_cs_size = -1;
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset - 1, 1, 
-                                   "%s: %d", name, i);
+int
+dissect_ndr_counted_string_cb(tvbuff_t *tvb, int offset,
+                             packet_info *pinfo, proto_tree *tree,
+                             guint8 *drep, int hf_index,
+                             dcerpc_callback_fnct_t *callback,
+                             void *callback_args)
+{
+       dcerpc_info *di = pinfo->private_data;
+       guint16 len, size;
 
-       if (data)
-               *data = i;
+        /* Structure starts with short, but is aligned for longs */
 
-       return offset;
-}
+       ALIGN_TO_4_BYTES;
 
-int prs_uint8s(tvbuff_t *tvb, int offset, packet_info *pinfo,
-              proto_tree *tree, int count, guint8 **data, char *name)
-{
-       const guint8 *ptr;
-       
-       /* The tvb_get_ptr() function fails an assertion if count < -1 */
+       if (di->conformant_run)
+               return offset;
        
-       if (count < -1)
-               THROW(BoundsError);
-
-       /* No alignment required */
+       /* 
+           struct {
+               short len;
+               short size;
+               [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
+           } UNICODE_STRING;
 
-       ptr = tvb_get_ptr(tvb, offset, count);
+         */
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset, count, "%s", name);
+       offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
+                       hf_nt_cs_len, &len);
 
-       if (data)
-               *data = (guint8 *)ptr;
+       offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
+                       hf_nt_cs_size, &size);  
 
-       offset += count;
+       offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, tree, drep,
+                       dissect_ndr_wchar_cvstring, NDR_POINTER_UNIQUE,
+                       "Character Array", hf_index, callback, callback_args);
 
        return offset;
 }
 
-/* Parse a 16-bit integer */
+static gint ett_nt_counted_string = -1;
 
-int prs_uint16(tvbuff_t *tvb, int offset, packet_info *pinfo,
-              proto_tree *tree, guint16 *data, char *name)
+static int
+dissect_ndr_counted_string_helper(tvbuff_t *tvb, int offset,
+                                 packet_info *pinfo, proto_tree *tree,
+                                 guint8 *drep, int hf_index, int levels,
+                                 gboolean add_subtree)
 {
-       guint16 i;
-       
-       offset = prs_align(offset, 2);
-       
-       i = tvb_get_letohs(tvb, offset);
-       offset += 2;
+       proto_item *item;
+       proto_tree *subtree = tree;
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset - 2, 2, 
-                                   "%s: %d", name, i);
-       if (data)
-               *data = i;
+       if (add_subtree) {
 
-       return offset;
+               item = proto_tree_add_text(
+                       tree, tvb, offset, 0, 
+                       proto_registrar_get_name(hf_index));
+
+               subtree = proto_item_add_subtree(item, ett_nt_counted_string);
+       }
+
+       /*
+        * Add 2 levels, so that the string gets attached to the
+        * "Character Array" top-level item and to the top-level item
+        * added above.
+        */
+       return dissect_ndr_counted_string_cb(
+               tvb, offset, pinfo, subtree, drep, hf_index,
+               cb_wstr_postprocess, GINT_TO_POINTER(2 + levels));
 }
 
-/* Parse a number of uint16's */
+/* Dissect a counted string in-line. */
 
-int prs_uint16s(tvbuff_t *tvb, int offset, packet_info *pinfo,
-               proto_tree *tree, int count, guint16 **data, char *name)
+int
+dissect_ndr_counted_string(tvbuff_t *tvb, int offset,
+                          packet_info *pinfo, proto_tree *tree,
+                          guint8 *drep, int hf_index, int levels)
 {
-       const guint8 *ptr;
-       
-       /* The tvb_get_ptr() function fails an assertion if count < -1 */
-       
-       if (count < -1)
-               THROW(BoundsError);
-
-       offset = prs_align(offset, 2);
-       
-       ptr = tvb_get_ptr(tvb, offset, count * 2);
+       return dissect_ndr_counted_string_helper(
+               tvb, offset, pinfo, tree, drep, hf_index, levels, TRUE);
+}
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset, count * 2, 
-                                   "%s", name);
-       if (data)
-               *data = (guint16 *)ptr;
+/* Dissect a counted string as a callback to dissect_ndr_pointer().
+   This doesn't add a adds a proto item and subtreee for the string as
+   the pointer dissection already creates one. */
 
-       offset += count * 2;
+int
+dissect_ndr_counted_string_ptr(tvbuff_t *tvb, int offset,
+                              packet_info *pinfo, proto_tree *tree,
+                              guint8 *drep)
+{
+       dcerpc_info *di = pinfo->private_data;
 
-       return offset;
+       return dissect_ndr_counted_string_helper(
+               tvb, offset, pinfo, tree, drep, di->hf_index, 0, FALSE);
 }
 
-/* Parse a 32-bit integer */
+/* Dissect a counted byte_array as a callback to dissect_ndr_pointer_cb() */
+
+static gint ett_nt_counted_byte_array = -1;
 
-int prs_uint32(tvbuff_t *tvb, int offset, packet_info *pinfo,
-              proto_tree *tree, guint32 *data, char *name)
+/* Dissect a counted byte array in-line. */
+
+int
+dissect_ndr_counted_byte_array_cb(tvbuff_t *tvb, int offset,
+                                 packet_info *pinfo, proto_tree *tree,
+                                 guint8 *drep, int hf_index,
+                                 dcerpc_callback_fnct_t *callback,
+                                 void *callback_args)
 {
-       guint32 i;
-       
-       offset = prs_align(offset, 4);
-       
-       i = tvb_get_letohl(tvb, offset);
-       offset += 4;
+       dcerpc_info *di = pinfo->private_data;
+       proto_item *item;
+       proto_tree *subtree;
+       guint16 len, size;
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset - 4, 4, 
-                                   "%s: %d", name, i);
+        /* Structure starts with short, but is aligned for longs */
 
-       if (data)
-               *data = i;
+       ALIGN_TO_4_BYTES;
 
-       return offset;
-}
+       if (di->conformant_run)
+               return offset;
 
-/* Parse a number of 32-bit integers */
+       item = proto_tree_add_text(tree, tvb, offset, 0, 
+               proto_registrar_get_name(hf_index));
 
-int prs_uint32s(tvbuff_t *tvb, int offset, packet_info *pinfo,
-               proto_tree *tree, int count, guint32 **data, char *name)
-{
-       const guint8 *ptr;
+       subtree = proto_item_add_subtree(item, ett_nt_counted_byte_array);
        
-       /* The tvb_get_ptr() function fails an assertion if count < -1 */
-       
-       if (count < -1)
-               THROW(BoundsError);
+       /* 
+           struct {
+               short len;
+               short size;
+               [size_is(size), length_is(len), ptr] unsigned char *string;
+           } WHATEVER_THIS_IS_CALLED;
 
-       offset = prs_align(offset, 4);
-       
-       ptr = tvb_get_ptr(tvb, offset, count * 4);
+         */
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset - 4, 4, 
-                                   "%s", name);
-       if (data)
-               *data = (guint32 *)ptr;
+       offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
+                       hf_nt_cs_len, &len);
 
-       offset += count * 4;
+       offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
+                       hf_nt_cs_size, &size);  
+
+       offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, subtree, drep,
+                       dissect_ndr_char_cvstring, NDR_POINTER_UNIQUE,
+                       "Byte Array", hf_index, callback, callback_args);
 
        return offset;
 }
 
-/* Parse a NT status code */
+int
+dissect_ndr_counted_byte_array(tvbuff_t *tvb, int offset,
+                              packet_info *pinfo, proto_tree *tree,
+                              guint8 *drep, int hf_index)
+{
+       return dissect_ndr_counted_byte_array_cb(
+               tvb, offset, pinfo, tree, drep, hf_index, NULL, NULL);
+}
 
-int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
-                proto_tree *tree)
+/* This function is used to dissect a DCERPC encoded 64 bit time value.
+   XXX it should be fixed both here and in dissect_smb_64bit_time so
+   it can handle both BIG and LITTLE endian encodings
+ */
+int
+dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset,
+                       packet_info *pinfo, proto_tree *tree,
+                       guint8 *drep _U_, int hf_index)
 {
-       guint32 status;
+       dcerpc_info *di;
 
-       offset = prs_uint32(tvb, offset, pinfo, tree, &status, NULL);
+       di=pinfo->private_data;
+       if(di->conformant_run){
+               /*just a run to handle conformant arrays, nothing to dissect */
+               return offset;
+       }
 
-       if (tree)
-               proto_tree_add_text(tree, tvb, offset - 4, 4, "Status: %s",
-                                   val_to_str(status, NT_errors, "???"));
+       ALIGN_TO_4_BYTES;
 
+       offset = dissect_smb_64bit_time(tvb, tree, offset, hf_index);
        return offset;
 }
 
+/* Define this symbol to display warnings about request/response and
+   policy handle hash table collisions.  This happens when a packet with
+   the same conversation, smb fid and dcerpc call id occurs.  I think this
+   is due to a bug in the dcerpc/smb fragment reassembly code. */
+
+#undef DEBUG_HASH_COLL
+
 /*
- * We need to keep track of deferred referrents as they appear in the
- * packet after all the non-pointer objects.
- * to keep track of pointers as they are parsed as scalars and need to be
- * remembered for the next call to the prs function.
+ * Policy handle hashing.
  *
- * Pointers are stored in a linked list and pushed in the PARSE_SCALARS
- * section of the prs function and popped in the PARSE_BUFFERS section.  If
- * we try to pop off a referrent that has a different name then we are
- * expecting then something has gone wrong.
+ * We hash based on the policy handle value; the items in the hash table
+ * are lists of policy handle information about one or more policy
+ * handles with that value.  We have multiple values in case a given
+ * policy handle is opened in frame N, closed in frame M, and re-opened
+ * in frame O, where N < M < O.
+ *
+ * XXX - we really should also use a DCE RPC conversation/session handle
+ * of some sort, in case two separate sessions have the same handle
+ * value.  A transport-layer conversation might not be sufficient, as you
+ * might, for example, have multiple pipes in a single SMB connection,
+ * and you might have the same handle opened and closed separately on
+ * those two pipes.
+ *
+ * The policy handle information has "first frame" and "last frame"
+ * information; the entry should be used when dissecting a given frame
+ * only if that frame is within the interval [first frame,last frame].
+ * The list is sorted by "first frame".
+ *
+ * This doesn't handle the case of a handle being opened in frame N and
+ * re-opened in frame M, where N < M, with no intervening close, but I'm
+ * not sure anything can handle that if it's within the same DCE RPC
+ * session (if it's not, the conversation/session handle would fix that).
  */
 
-#undef DEBUG_PTRS
+typedef struct {
+       guint8 policy_hnd[20];
+} pol_hash_key;
 
-struct ptr {
-       char *name;
-       guint32 value;
-};
+typedef struct pol_value {
+       struct pol_value *next;          /* Next entry in hash bucket */
+       guint32 open_frame, close_frame; /* Frame numbers for open/close */
+       guint32 first_frame;             /* First frame in which this instance was seen */
+       guint32 last_frame;              /* Last frame in which this instance was seen */
+       char *name;                      /* Name of policy handle */
+} pol_value;
+
+typedef struct {
+       pol_value *list;                 /* List of policy handle entries */
+} pol_hash_value;
+
+#define POL_HASH_INIT_COUNT 100
 
-/* Create a new pointer */
+static GHashTable *pol_hash;
+static GMemChunk *pol_hash_key_chunk;
+static GMemChunk *pol_value_chunk;
+static GMemChunk *pol_hash_value_chunk;
 
-static struct ptr *new_ptr(char *name, guint32 value)
+/* Hash function */
+
+static guint pol_hash_fn(gconstpointer k)
 {
-       struct ptr *p;
+       const pol_hash_key *key = (const pol_hash_key *)k;
+
+       /* Bytes 4-7 of the policy handle are a timestamp so should make a
+          reasonable hash value */
 
-       p = g_malloc(sizeof(struct ptr));
+       return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
+               (key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
+}
 
-       p->name = g_strdup(name);
-       p->value = value;
+/* Return true if a policy handle is all zeros */
 
-       return p;
+static gboolean is_null_pol(e_ctx_hnd *policy_hnd)
+{
+       static guint8 null_policy_hnd[20];
+
+       return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
 }
 
-/* Free a pointer */
+/* Hash compare function */
 
-static void free_ptr(struct ptr *p)
+static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
 {
-       if (p) {
-               g_free(p->name);
-               g_free(p);
-       }
+       const pol_hash_key *key1 = (const pol_hash_key *)k1;
+       const pol_hash_key *key2 = (const pol_hash_key *)k2;
+
+       return memcmp(key1->policy_hnd, key2->policy_hnd,
+                     sizeof(key1->policy_hnd)) == 0;
 }
 
-/* Parse a pointer and store it's value in a linked list */
+/*
+ * Look up the instance of a policy handle value in whose range of frames
+ * the specified frame falls.
+ */
+static pol_value *find_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
+                                 pol_hash_value **valuep)
+{
+       pol_hash_key key;
+       pol_value *pol;
+
+       memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
+       if ((*valuep = g_hash_table_lookup(pol_hash, &key))) {
+               /*
+                * Look for the first value such that both:
+                *
+                *      1) the first frame in which it was seen is
+                *         <= the specified frame;
+                *
+                *      2) the last frame in which it was seen is
+                *         either unknown (meaning we haven't yet
+                *         seen a close or another open of the
+                *         same handle, which is assumed to imply
+                *         an intervening close that wasn't captured)
+                *         or is >= the specified frame.
+                *
+                * If there's more than one such frame, that's the
+                * case where a handle is opened in frame N and
+                * reopened in frame M, with no intervening close;
+                * there is no right answer for that, so the instance
+                * opened in frame N is as right as anything else.
+                */
+               for (pol = (*valuep)->list; pol != NULL; pol = pol->next) {
+                       if (pol->first_frame <= frame &&
+                           (pol->last_frame == 0 ||
+                            pol->last_frame >= frame))
+                               break;  /* found one */
+               }
+               return pol;
+       } else {
+               /*
+                * The handle isn't in the hash table.
+                */
+               return NULL;
+       }
+}
 
-int prs_push_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
-                proto_tree *tree, GList **ptr_list, char *name)
+static void add_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
+                          pol_value *pol, pol_hash_value *value)
 {
-       struct ptr *p;
-       guint32 value;
+       pol_hash_key *key;
+       pol_value *polprev, *polnext;
+
+       if (value == NULL) {
+               /*
+                * There's no hash value; create one, put the new
+                * value at the beginning of its policy handle list,
+                * and put the hash value in the policy handle hash
+                * table.
+                */
+               value = g_mem_chunk_alloc(pol_hash_value_chunk);
+               value->list = pol;
+               pol->next = NULL;
+               key = g_mem_chunk_alloc(pol_hash_key_chunk);
+               memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
+               g_hash_table_insert(pol_hash, key, value);
+       } else {
+               /*
+                * Put the new value in the hash value's policy handle
+                * list so that it's sorted by the first frame in
+                * which it appeared.
+                *
+                * Search for the first entry whose first frame number
+                * is greater than the current frame number, if any.
+                */
+               for (polnext = value->list, polprev = NULL;
+                   polnext != NULL && polnext->first_frame <= frame;
+                   polprev = polnext, polnext = polnext->next)
+                       ;
+
+               /*
+                * "polprev" points to the entry in the list after
+                * which we should put the new entry; if it's null,
+                * that means we should put it at the beginning of
+                * the list.
+                */
+               if (polprev == NULL)
+                       value->list = pol;
+               else
+                       polprev->next = pol;
+               
+               /*
+                * "polnext" points to the entry in the list before
+                * which we should put the new entry; if it's null,
+                * that means we should put it at the end of the list.
+                */
+               pol->next = polnext;
+       }
+}
 
-       offset = prs_uint32(tvb, offset, pinfo, tree, &value, NULL);
+/* Store the open and close frame numbers of a policy handle */
 
-       if (name && tree)
-               proto_tree_add_text(tree, tvb, offset - 4, 4, 
-                                   "%s pointer: 0x%08x", name, value);
+void dcerpc_smb_store_pol_pkts(e_ctx_hnd *policy_hnd, packet_info *pinfo,
+                              gboolean is_open, gboolean is_close)
+{
+       pol_hash_value *value;
+       pol_value *pol;
+
+       /*
+        * By the time the first pass is done, the policy handle database
+        * has been completely constructed.  If we've already seen this
+        * frame, there's nothing to do.
+        */
+       if (pinfo->fd->flags.visited)
+               return;
+
+       if (is_null_pol(policy_hnd))
+               return;
+
+       /* Look up existing value */
+       pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
+
+       if (pol != NULL) {
+               /*
+                * Update the existing value as appropriate.
+                */
+               if (is_open) {
+                       /*
+                        * This is an open; we assume that we missed
+                        * a close of this handle, so we set its
+                        * "last frame" value and act as if we didn't
+                        * see it.
+                        *
+                        * XXX - note that we might be called twice for
+                        * the same operation (see "dissect_pipe_dcerpc()",
+                        * which calls the DCE RPC dissector twice), so we
+                        * must first check to see if this is a handle we
+                        * just filled in.
+                        *
+                        * We check whether this handle's "first frame"
+                        * frame number is this frame and its "last frame
+                        * is 0; if so, this is presumably a duplicate call,
+                        * and we don't do an implicit close.
+                        */
+                       if (pol->first_frame == pinfo->fd->num &&
+                           pol->last_frame == 0)
+                               return;
+                       pol->last_frame = pinfo->fd->num;
+                       pol = NULL;
+               } else {
+                       if (is_close) {
+                               pol->close_frame = pinfo->fd->num;
+                               pol->last_frame = pinfo->fd->num;
+                       }
+                       return;
+               }
+       }
 
-       p = new_ptr(name, value);
+       /* Create a new value */
 
-       *ptr_list = g_list_append(*ptr_list, p);
+       pol = g_mem_chunk_alloc(pol_value_chunk);
 
-#ifdef DEBUG_PTRS
-       fprintf(stderr, "DEBUG_PTRS: pushing %s ptr = 0x%08x, %d ptrs in "
-               "list\n", name, value, g_list_length(*ptr_list));
-#endif
+       pol->open_frame = is_open ? pinfo->fd->num : 0;
+       pol->close_frame = is_close ? pinfo->fd->num : 0;
+       pol->first_frame = pinfo->fd->num;
+       pol->last_frame = pol->close_frame;     /* if 0, unknown; if non-0, known */
 
-       return offset;
+       pol->name = NULL;
+
+       add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
 }
 
-/* Pop a pointer of a given name.  Return it's value. */
+/* Store a text string with a policy handle */
 
-guint32 prs_pop_ptr(GList **ptr_list, char *name)
+void dcerpc_smb_store_pol_name(e_ctx_hnd *policy_hnd, packet_info *pinfo,
+                              char *name)
 {
-       GList *elt;
-       struct ptr *p;
-       guint32 result;
-
-       g_assert(g_list_length(*ptr_list) != 0);        /* List too short */
-
-       /* Get pointer at head of list */
+       pol_hash_value *value;
+       pol_value *pol;
+
+       /*
+        * By the time the first pass is done, the policy handle database
+        * has been completely constructed.  If we've already seen this
+        * frame, there's nothing to do.
+        */
+       if (pinfo->fd->flags.visited)
+               return;
+
+       if (is_null_pol(policy_hnd))
+               return;
+
+       /* Look up existing value */
+       pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
+
+       if (pol != NULL) {
+               /*
+                * This is the first pass; update the existing
+                * value as appropriate.
+                */
+               if (pol->name && name) {
+#ifdef DEBUG_HASH_COLL
+                       if (strcmp(pol->name, name) != 0)
+                               g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
+#endif
+                       free(pol->name);
+               }
 
-       elt = g_list_first(*ptr_list);
-       p = (struct ptr *)elt->data;
-       result = p->value;
+               pol->name = strdup(name);
 
-#ifdef DEBUG_PTRS
-       if (strcmp(p->name, name) != 0) {
-               fprintf(stderr, "DEBUG_PTRS: wrong pointer (%s != %s)\n",
-                       p->name, name);
+               return;
        }
-#endif
 
-       /* Free pointer record */
+       /* Create a new value */
 
-       *ptr_list = g_list_remove_link(*ptr_list, elt);
+       pol = g_mem_chunk_alloc(pol_value_chunk);
 
-#ifdef DEBUG_PTRS
-       fprintf(stderr, "DEBUG_PTRS: popping %s ptr = 0x%08x, %d ptrs in "
-               "list\n", p->name, p->value, g_list_length(*ptr_list));
-#endif
+       pol->open_frame = 0;
+       pol->close_frame = 0;
+       pol->first_frame = pinfo->fd->num;
+       pol->last_frame = 0;
 
-       free_ptr(p);
+       if (name)
+               pol->name = strdup(name);
+       else
+               pol->name = strdup("<UNKNOWN>");
 
-       return result;
+       add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
 }
 
 /*
- * Parse a UNISTR2 structure 
- *
- * typedef struct {
- *   short length;
- *   short size;
- *   [size_is(size/2)] [length_is(length/2)] [unique] wchar_t *string;
- * } UNICODE_STRING;
+ * Retrieve a policy handle.
  *
+ * XXX - should this get an "is_close" argument, and match even closed
+ * policy handles if the call is a close, so we can handle retransmitted
+ * close operations?
  */
 
-/* Convert a string from little-endian unicode to ascii.  At the moment we
-   fake it by taking every odd byte.  )-:  The caller must free the
-   result returned. */
-
-char *fake_unicode(guint16 *data, int len)
+gboolean dcerpc_smb_fetch_pol(e_ctx_hnd *policy_hnd, char **name,
+                             guint32 *open_frame, guint32 *close_frame,
+                             guint32 cur_frame)
 {
-       char *buffer;
-       int i;
+       pol_hash_value *value;
+       pol_value *pol;
+
+       /* Prevent uninitialised return vars */
+
+       if (name)
+               *name = NULL;
+
+       if (open_frame)
+               *open_frame = 0;
+
+       if (close_frame)
+               *close_frame = 0;
+
+       /* Look up existing value */
+       pol = find_pol_handle(policy_hnd, cur_frame, &value);
 
-       buffer = malloc(len + 1);
+       if (pol) {
+               if (name)
+                       *name = pol->name;
 
-       for (i = 0; i < len; i++)
-               buffer[i] = data[i] & 0xff;
+               if (open_frame)
+                       *open_frame = pol->open_frame;
 
-       buffer[len] = 0;
+               if (close_frame)
+                       *close_frame = pol->close_frame;
+       }
 
-       return buffer;
+       return pol != NULL;
 }
 
-/* Parse a UNISTR2 structure */
+/* Iterator to free a policy handle key/value pair, and all
+   the policy handle values to which the hash table value
+   points */
 
-int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
-               proto_tree *tree, int flags, char **data, char *name)
+static void free_pol_keyvalue(gpointer key _U_, gpointer value_arg,
+    gpointer user_data _U_)
 {
-       guint32 len = 0, unknown = 0, max_len = 0;
-
-       if (flags & PARSE_SCALARS) {
-               offset = prs_uint32(tvb, offset, pinfo, tree, &len, "Length");
-               offset = prs_uint32(tvb, offset, pinfo, tree, &unknown, 
-                                   "Offset");
-               offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, 
-                                   "Max length");
+       pol_hash_value *value = (pol_hash_value *)value_arg;
+       pol_value *pol;
+
+       /* Free user data */
+
+       for (pol = value->list; pol != NULL; pol = pol->next) {
+               free(pol->name);
+               pol->name = NULL;
        }
+}
+
+/* Initialise policy handle hash */
+
+static void init_pol_hash(void)
+{
+       /* Initialise memory chunks */
 
-       if (flags & PARSE_BUFFERS) {
-               guint16 *data16;
+       if (pol_hash_key_chunk)
+               g_mem_chunk_destroy(pol_hash_key_chunk);
 
-               offset = prs_uint16s(tvb, offset, pinfo, tree, max_len,
-                                    &data16, "Buffer");
+       pol_hash_key_chunk = g_mem_chunk_new(
+               "Policy handle hash keys", sizeof(pol_hash_key),
+               POL_HASH_INIT_COUNT * sizeof(pol_hash_key), G_ALLOC_ONLY);
 
-               if (data)
-                       *data = fake_unicode(data16, max_len);
+       if (pol_value_chunk)
+               g_mem_chunk_destroy(pol_value_chunk);
+
+       pol_value_chunk = g_mem_chunk_new(
+               "Policy handle values", sizeof(pol_value),
+               POL_HASH_INIT_COUNT * sizeof(pol_value), G_ALLOC_ONLY);
+
+       if (pol_hash_value_chunk)
+               g_mem_chunk_destroy(pol_hash_value_chunk);
+
+       pol_hash_value_chunk = g_mem_chunk_new(
+               "Policy handle hash values", sizeof(pol_hash_value),
+               POL_HASH_INIT_COUNT * sizeof(pol_hash_value), G_ALLOC_ONLY);
+
+       /* Initialise hash table */
+
+       if (pol_hash) {
+               g_hash_table_foreach(pol_hash, free_pol_keyvalue, NULL);
+               g_hash_table_destroy(pol_hash);
        }
 
-       return offset;
+       pol_hash = g_hash_table_new(pol_hash_fn, pol_hash_compare);
 }
 
-/* Parse a policy handle. */
+/* Dissect a NT status code */
 
-int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
-                  proto_tree *tree, const guint8 **data)
+int
+dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+                proto_tree *tree, guint8 *drep,
+                int hfindex, guint32 *pdata)
 {
-       const guint8 *data8;
-
-       offset = prs_align(offset, 4);
+       guint32 status;
 
-       proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle");
+       offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
+                                   hfindex, &status);
 
-       data8 = tvb_get_ptr(tvb, offset, 20);
-       
-       if (data)
-               *data = data8;
+       if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
+                               val_to_str(status, NT_errors,
+                                          "Unknown error 0x%08x"));
+       if (pdata)
+               *pdata = status;
 
-       return offset + 20;
+       return offset;
 }
 
+/* Dissect a DOS status code */
 
+int
+dissect_doserror(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+              proto_tree *tree, guint8 *drep,
+              int hfindex, guint32 *pdata)
+{
+       guint32 status;
 
-/* following are a few functions for dissecting common structures used by NT 
-   services. These might need to be cleaned up at a later time but at least we get
-   them out of the real service dissectors.
-*/
-
+       offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
+                                   hfindex, &status);
 
-/* UNICODE_STRING  BEGIN */
-/* functions to dissect a UNICODE_STRING structure, common to many 
-   NT services
-   struct {
-     short len;
-     short size;
-     [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
-   } UNICODE_STRING;
+       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 0x%08x"));
+       if (pdata)
+               *pdata = status;
 
-   these variables can be found in packet-dcerpc-samr.c 
-*/
-extern int hf_nt_str_len;
-extern int hf_nt_str_off;
-extern int hf_nt_str_max_len;
-extern int hf_nt_string_length;
-extern int hf_nt_string_size;
-extern gint ett_nt_unicode_string;
+       return offset;
+}
 
+/* Dissect a NT policy handle */
 
-/* this function will dissect the
-     [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
-  part of the unicode string
+static int hf_nt_policy_open_frame = -1;
+static int hf_nt_policy_close_frame = -1;
 
-   struct {
-     short len;
-     short size;
-     [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
-   } UNICODE_STRING;
-  structure used by NT to transmit unicode string values.
+static gint ett_nt_policy_hnd = -1;
 
-  This function also looks at di->levels to see if whoever called us wanted us to append
-  the name: string to any higher levels in the tree .
-*/
 int
-dissect_ndr_nt_UNICODE_STRING_str(tvbuff_t *tvb, int offset, 
-                       packet_info *pinfo, proto_tree *tree, 
-                       char *drep)
+dissect_nt_policy_hnd(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+                     proto_tree *tree, guint8 *drep, int hfindex,
+                     e_ctx_hnd *pdata, proto_item **pitem,
+                     gboolean is_open, gboolean is_close)
 {
-       guint32 len, off, max_len;
-       guint16 *data16;
-       char *text;
-       int old_offset;
+       proto_item *item;
+       proto_tree *subtree;
+       e_ctx_hnd hnd;
+       guint32 open_frame = 0, close_frame = 0;
+       char *name;
+       int old_offset = offset;
        dcerpc_info *di;
 
        di=pinfo->private_data;
        if(di->conformant_run){
-               /*just a run to handle conformant arrays, nothing to dissect */
+               /*
+                * just a run to handle conformant arrays, no scalars to
+                * dissect - and "dissect_ndr_ctx_hnd()" won't return
+                * a handle, so we can't do the hashing stuff in any
+                * case
+                */
                return offset;
        }
 
-       offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
-                       hf_nt_str_len, &len);
-       offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
-                       hf_nt_str_off, &off);
-       offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
-                       hf_nt_str_max_len, &max_len);
-
-       old_offset=offset;
-       offset = prs_uint16s(tvb, offset, pinfo, tree, max_len, &data16, NULL);
-       text = fake_unicode(data16, max_len);
-
-       proto_tree_add_string(tree, di->hf_index, tvb, old_offset,
-               offset-old_offset, text);
-
-       /* need to test di->levels before doing the proto_item_append_text()
-          since netlogon has these objects as top level objects in its representation
-          and trying to append to the tree object in that case will dump core */
-       if(tree && (di->levels>-1)){
-               proto_item_append_text(tree, ": %s", text);
-               if(di->levels>-1){
-                       tree=tree->parent;
-                       proto_item_append_text(tree, ": %s", text);
-                       while(di->levels>0){
-                               tree=tree->parent;
-                               proto_item_append_text(tree, " %s", text);
-                               di->levels--;
-                       }
-               }
+       /* Add to proto tree */
+
+       item = proto_tree_add_text(tree, tvb, offset, sizeof(e_ctx_hnd),
+                                  "Policy Handle");
+
+       subtree = proto_item_add_subtree(item, ett_nt_policy_hnd);
+
+       offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep,
+                                    hfindex, &hnd);
+
+       /*
+        * Create a new entry for this handle if it's not a null handle
+        * and no entry already exists, and, in any case, set the
+        * open, close, first, and last frame information as appropriate.
+        */
+       dcerpc_smb_store_pol_pkts(&hnd, pinfo, is_open, is_close);
+
+       /* Insert open/close/name information if known */
+
+       if (dcerpc_smb_fetch_pol(&hnd, &name, &open_frame, &close_frame,
+           pinfo->fd->num)) {
+
+               if (open_frame)
+                       proto_tree_add_uint(
+                               subtree, hf_nt_policy_open_frame, tvb,
+                               old_offset, sizeof(e_ctx_hnd), open_frame);
+
+               if (close_frame)
+                       proto_tree_add_uint(
+                               subtree, hf_nt_policy_close_frame, tvb,
+                               old_offset, sizeof(e_ctx_hnd), close_frame);
+
+               /*
+                * Don't append the handle name if pitem is null; that's
+                * an indication that our caller will do so, as we're
+                * supplying a pointer to the item so that they can do
+                * so.
+                */
+               if (name != NULL && pitem == NULL)
+                       proto_item_append_text(item, ": %s", name);
        }
-       return offset;
+
+       if (pdata)
+               *pdata = hnd;
+
+       if (pitem)
+               *pitem = item;
+
+       return offset;
 }
 
-/* this function will dissect the
-   struct {
-     short len;
-     short size;
-     [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
-   } UNICODE_STRING;
-  structure used by NT to transmit unicode string values.
-  the function takes one additional parameter, level
-  which specifies how many additional levels up in the tree where we should
-  append the string.  If unsure, specify levels as 0.
-*/
+/* Some helper routines to dissect a range of uint8 characters.  I don't
+   think these are "official" NDR representations and are probably specific
+   to NT so for the moment they're put here instead of in packet-dcerpc.c
+   and packet-dcerpc-ndr.c. */
+
 int
-dissect_ndr_nt_UNICODE_STRING(tvbuff_t *tvb, int offset, 
-                       packet_info *pinfo, proto_tree *parent_tree, 
-                       char *drep, int hf_index, int levels)
+dissect_dcerpc_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
+                      proto_tree *tree, guint8 *drep, int hfindex,
+                     int length, const guint8 **pdata)
 {
-       proto_item *item=NULL;
-       proto_tree *tree=NULL;
-       int old_offset=offset;
-       dcerpc_info *di;
-       char *name;
+    const guint8 *data;
 
-       ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
+    data = (const guint8 *)tvb_get_ptr(tvb, offset, length);
 
-       di=pinfo->private_data;
-       if(di->conformant_run){
-               /*just a run to handle conformant arrays, nothing to dissect */
-               return offset;
-       }
+    if (tree) {
+        proto_tree_add_item (tree, hfindex, tvb, offset, length, (drep[0] & 0x10));
+    }
 
-       name = proto_registrar_get_name(hf_index);
-       if(parent_tree){
-               item = proto_tree_add_text(parent_tree, tvb, offset, -1,
-                       "%s", name);
-               tree = proto_item_add_subtree(item, ett_nt_unicode_string);
-       }
+    if (pdata)
+        *pdata = data;
 
-       offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
-                       hf_nt_string_length, NULL);
-       offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
-                       hf_nt_string_size, NULL);
-       di->levels=1;
-       offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
-                       dissect_ndr_nt_UNICODE_STRING_str, NDR_POINTER_UNIQUE,
-                       name, hf_index, levels);
+    return offset + length;
+}
 
-       proto_item_set_len(item, offset-old_offset);
-       return offset;
+int
+dissect_ndr_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+                   proto_tree *tree, guint8 *drep,
+                   int hfindex, int length, const guint8 **pdata)
+{
+    dcerpc_info *di;
+
+    di=pinfo->private_data;
+    if(di->conformant_run){
+      /* just a run to handle conformant arrays, no scalars to dissect */
+      return offset;
+    }
+
+    /* no alignment needed */
+    return dissect_dcerpc_uint8s(tvb, offset, pinfo,
+                                 tree, drep, hfindex, length, pdata);
 }
-/* UNICODE_STRING  END */
-
-/* functions to dissect a STRING structure, common to many 
-   NT services
-   struct {
-     short len;
-     short size;
-     [size_is(size), length_is(len), ptr] char *string;
-   } STRING;
-*/
+
 int
-dissect_ndr_nt_STRING_string (tvbuff_t *tvb, int offset, 
-                             packet_info *pinfo, proto_tree *tree, 
-                             char *drep)
+dissect_dcerpc_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
+                      proto_tree *tree, guint8 *drep, int hfindex,
+                     int length)
 {
-       guint32 len, off, max_len;
-       guint8 *text;
-       int old_offset;
-       header_field_info *hfi;
-       dcerpc_info *di;
+    if (tree) {
+        proto_tree_add_item (tree, hfindex, tvb, offset, length * 2, (drep[0] & 0x10));
+    }
 
-       di=pinfo->private_data;
-       if(di->conformant_run){
-               /*just a run to handle conformant arrays, nothing to dissect */
-               return offset;
-       }
+    return offset + length * 2;
+}
 
-        offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
-                                     hf_nt_str_len, &len);
-        offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
-                                     hf_nt_str_off, &off);
-        offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
-                                     hf_nt_str_max_len, &max_len);
-
-       old_offset=offset;
-       hfi = proto_registrar_get_nth(di->hf_index);
-
-       switch(hfi->type){
-       case FT_STRING:
-               offset = prs_uint8s(tvb, offset, pinfo, tree, max_len, &text, NULL);
-               proto_tree_add_string_format(tree, di->hf_index, 
-                       tvb, old_offset, offset-old_offset,
-                       text, "%s: %s", hfi->name, text);
-               break;
-       case FT_BYTES:
-               text="";
-               proto_tree_add_item(tree, di->hf_index, tvb, offset, max_len, FALSE);
-               offset += max_len;
-               break;
-       default:
-               g_assert_not_reached();
+int
+dissect_ndr_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+                   proto_tree *tree, guint8 *drep,
+                   int hfindex, int length)
+{
+    dcerpc_info *di;
+
+    di=pinfo->private_data;
+    if(di->conformant_run){
+      /* just a run to handle conformant arrays, no scalars to dissect */
+      return offset;
+    }
+
+    if (offset % 2)
+        offset++;
+
+    return dissect_dcerpc_uint16s(tvb, offset, pinfo,
+                                 tree, drep, hfindex, length);
+}
+
+/*
+ * Helper routines for dissecting NDR strings
+ */
+
+void cb_wstr_postprocess(packet_info *pinfo, proto_tree *tree _U_,
+                       proto_item *item, tvbuff_t *tvb, 
+                       int start_offset, int end_offset,
+                       void *callback_args)
+{
+       gint options = GPOINTER_TO_INT(callback_args);
+       gint levels = CB_STR_ITEM_LEVELS(options);
+       char *s;
+
+       /* Align start_offset on 4-byte boundary. */
+
+       if (start_offset % 4)
+               start_offset += 4 - (start_offset % 4);
+
+       /* Get string value */
+
+       if ((end_offset - start_offset) <= 12)
+               return;         /* XXX: Use unistr2 dissector instead? */
+
+       /*
+        * XXX - need to handle non-printable characters here.
+        *
+        * XXX - this is typically called after the string has already
+        * been fetched and processed by some other routine; is there
+        * some way we can get that string, rather than duplicating the
+        * efforts of that routine?
+        */
+       s = tvb_fake_unicode(
+               tvb, start_offset + 12, (end_offset - start_offset - 12) / 2,
+               TRUE);
+
+       /* Append string to COL_INFO */
+
+       if (options & CB_STR_COL_INFO) {
+               if (check_col(pinfo->cinfo, COL_INFO))
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
        }
 
-       if(tree && (di->levels>-1)){
-               proto_item_append_text(tree, ": %s", text);
-               if(di->levels>-1){
-                       tree=tree->parent;
-                       proto_item_append_text(tree, ": %s", text);
-                       while(di->levels>0){
-                               tree=tree->parent;
-                               proto_item_append_text(tree, " %s", text);
-                               di->levels--;
+       /* Append string to upper-level proto_items */
+
+       if (levels > 0 && item && s && s[0]) {
+               proto_item_append_text(item, ": %s", s);
+               item = item->parent;
+               levels--;
+               if (levels > 0) {
+                       proto_item_append_text(item, ": %s", s);
+                       item = item->parent;
+                       levels--;
+                       while (levels > 0) {
+                               proto_item_append_text(item, " %s", s);
+                               item = item->parent;
+                               levels--;
                        }
                }
        }
-       return offset;
+
+       /* Save string to dcv->private_data */
+
+       if (options & CB_STR_SAVE) {
+               dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+               dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
+               
+               dcv->private_data = g_strdup(s);
+       }
+
+       g_free(s);
 }
 
-int
-dissect_ndr_nt_STRING (tvbuff_t *tvb, int offset
-                             packet_info *pinfo, proto_tree *parent_tree, 
-                             char *drep, int hf_index, int levels)
+void cb_str_postprocess(packet_info *pinfo, proto_tree *tree _U_,
+                       proto_item *item, tvbuff_t *tvb
+                       int start_offset, int end_offset,
+                       void *callback_args)
 {
-       proto_item *item=NULL;
-       proto_tree *tree=NULL;
-       int old_offset=offset;
-       dcerpc_info *di;
-       char *name;
+       gint options = GPOINTER_TO_INT(callback_args);
+       gint levels = CB_STR_ITEM_LEVELS(options);
+       char *s;
 
-       ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
+       /* Align start_offset on 4-byte boundary. */
 
-       di=pinfo->private_data;
-       if(di->conformant_run){
-               /*just a run to handle conformant arrays, nothing to dissect */
-               return offset;
-       }
+       if (start_offset % 4)
+               start_offset += 4 - (start_offset % 4);
 
-       name = proto_registrar_get_name(hf_index);
-       if(parent_tree){
-               item = proto_tree_add_text(parent_tree, tvb, offset, -1,
-                       "%s", name);
-               tree = proto_item_add_subtree(item, ett_nt_unicode_string);
-       }
+       /* Get string value */
 
-        offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
-                                     hf_nt_string_length, NULL);
-        offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
-                                     hf_nt_string_size, NULL);
-        offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
-                       dissect_ndr_nt_STRING_string, NDR_POINTER_UNIQUE,
-                       name, hf_index, levels);
+       if ((end_offset - start_offset) <= 12)
+               return;         /* XXX: Use unistr2 dissector instead? */
 
-       proto_item_set_len(item, offset-old_offset);
-       return offset;
-}
+       /*
+        * XXX - need to handle non-printable characters here.
+        *
+        * XXX - this is typically called after the string has already
+        * been fetched and processed by some other routine; is there
+        * some way we can get that string, rather than duplicating the
+        * efforts of that routine?
+        */
+       s = tvb_get_string(
+               tvb, start_offset + 12, (end_offset - start_offset - 12) );
 
+       /* Append string to COL_INFO */
 
-/* This function is used to dissect a DCERPC encoded 64 bit time value.
-   XXX it should be fixed both here and in dissect_smb_64bit_time so
-   it can handle both BIG and LITTLE endian encodings 
- */
-int
-dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset, 
-                       packet_info *pinfo, proto_tree *tree, 
-                       char *drep, int hf_index)
-{
-       dcerpc_info *di;
+       if (options & CB_STR_COL_INFO) {
+               if (check_col(pinfo->cinfo, COL_INFO))
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
+       }
 
-       di=pinfo->private_data;
-       if(di->conformant_run){
-               /*just a run to handle conformant arrays, nothing to dissect */
-               return offset;
+       /* Append string to upper-level proto_items */
+
+       if (levels > 0 && item && s && s[0]) {
+               proto_item_append_text(item, ": %s", s);
+               item = item->parent;
+               levels--;
+               if (levels > 0) {
+                       proto_item_append_text(item, ": %s", s);
+                       item = item->parent;
+                       levels--;
+                       while (levels > 0) {
+                               proto_item_append_text(item, " %s", s);
+                               item = item->parent;
+                               levels--;
+                       }
+               }
        }
 
-       ALIGN_TO_4_BYTES;
+       /* Save string to dcv->private_data */
 
-       offset = dissect_smb_64bit_time(tvb, pinfo, tree, offset,
-                hf_index);
-       return offset;
+       if (options & CB_STR_SAVE) {
+               dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
+               dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
+               
+               dcv->private_data = g_strdup(s);
+       }
+
+       g_free(s);
+}
+
+/* Dissect a pointer to a NDR string and append the string value to the
+   proto_item. */
+
+int dissect_ndr_str_pointer_item(tvbuff_t *tvb, gint offset, 
+                                packet_info *pinfo, proto_tree *tree, 
+                                guint8 *drep, int type, char *text, 
+                                int hf_index, int levels)
+{
+       return dissect_ndr_pointer_cb(
+               tvb, offset, pinfo, tree, drep, 
+               dissect_ndr_wchar_cvstring, type, text, hf_index, 
+               cb_wstr_postprocess, GINT_TO_POINTER(levels + 1));
 }
 
+/*
+ * Register ett/hf values and perform DCERPC over SMB specific
+ * initialisation.
+ */
+void dcerpc_smb_init(int proto_dcerpc)
+{
+       static hf_register_info hf[] = {
+
+               /* String handling */
+
+               { &hf_nt_cs_size,
+                 { "Size", "nt.str.size", FT_UINT16, BASE_DEC,
+                   NULL, 0x0, "Size of string in short integers", 
+                   HFILL }},
+               
+               { &hf_nt_cs_len,
+                 { "Length", "nt.str.len", FT_UINT16, BASE_DEC,
+                   NULL, 0x0, "Length of string in short integers", 
+                   HFILL }},
+               
+               /* Policy handles */
+
+               { &hf_nt_policy_open_frame,
+                 { "Frame handle opened", "dcerpc.nt.open_frame",
+                   FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                   "Frame handle opened", HFILL }},
+
+               { &hf_nt_policy_close_frame,
+                 { "Frame handle closed", "dcerpc.nt.close_frame",
+                   FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                   "Frame handle closed", HFILL }},
+       };
+
+       static gint *ett[] = {
+               &ett_nt_unicode_string,
+               &ett_nt_counted_string,
+               &ett_nt_counted_byte_array,
+               &ett_nt_policy_hnd,
+       };
+
+       /* Register ett's and hf's */
+
+       proto_register_subtree_array(ett, array_length(ett));
+       proto_register_field_array(proto_dcerpc, hf, array_length(hf));
+
+       /* Initialise policy handle hash */
+
+       init_pol_hash();
+}