/* 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.31 2002/05/07 00:35:14 tpot Exp $
+ * $Id: packet-dcerpc-nt.c,v 1.77 2003/07/01 00:59:43 guy 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 */
-
-int prs_align(int offset, int n)
-{
- if (offset % n)
- offset += n - (offset % n);
-
- return offset;
-}
-
-/* Parse a 8-bit integer */
-
-int prs_uint8(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, guint8 *data, char *name)
-{
- guint8 i;
-
- /* No alignment required */
-
- i = tvb_get_guint8(tvb, offset);
- offset++;
-
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset - 1, 1,
- "%s: %u", name, i);
-
- if (data)
- *data = i;
-
- return offset;
-}
-
-int prs_uint8s(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, int count, int *data_offset, char *name)
-{
- /* No alignment required */
-
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset, count, "%s", name);
-
- if (data_offset)
- *data_offset = offset;
-
- offset += count;
-
- return offset;
-}
-
-/* Parse a 16-bit integer */
-
-int prs_uint16(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, guint16 *data, char *name)
-{
- guint16 i;
-
- offset = prs_align(offset, 2);
-
- i = tvb_get_letohs(tvb, offset);
- offset += 2;
-
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset - 2, 2,
- "%s: %u", name, i);
- if (data)
- *data = i;
-
- return offset;
-}
-
-/* Parse a number of uint16's */
-
-int prs_uint16s(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, int count, int *data_offset, char *name)
-{
- offset = prs_align(offset, 2);
-
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset, count * 2,
- "%s", name);
- if (data_offset)
- *data_offset = offset;
-
- offset += count * 2;
-
- return offset;
-}
-
-/* Parse a 32-bit integer */
-
-int prs_uint32(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, guint32 *data, char *name)
-{
- guint32 i;
-
- offset = prs_align(offset, 4);
-
- i = tvb_get_letohl(tvb, offset);
- offset += 4;
-
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset - 4, 4,
- "%s: %u", name, i);
-
- if (data)
- *data = i;
-
- return offset;
-}
-
-/* Parse a number of 32-bit integers */
-
-int prs_uint32s(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, int count, int *data_offset, char *name)
-{
- offset = prs_align(offset, 4);
-
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset - 4, 4,
- "%s", name);
- if (data_offset)
- *data_offset = offset;
-
- offset += count * 4;
-
- return offset;
-}
-
-/* Parse a NT status code */
-
-int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
- proto_tree *tree)
-{
- 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, NT_errors, "???"));
-
- return offset;
-}
-
/*
- * 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.
- *
- * 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.
+ * Used by several dissectors.
*/
-
-#undef DEBUG_PTRS
-
-struct ptr {
- char *name;
- guint32 value;
+const value_string platform_id_vals[] = {
+ { 300, "DOS" },
+ { 400, "OS/2" },
+ { 500, "Windows NT" },
+ { 600, "OSF" },
+ { 700, "VMS" },
+ { 0, NULL }
};
-/* Create a new pointer */
+/* Parse some common RPC structures */
-static struct ptr *new_ptr(char *name, guint32 value)
-{
- struct ptr *p;
-
- p = g_malloc(sizeof(struct ptr));
-
- p->name = g_strdup(name);
- p->value = value;
+gint ett_nt_unicode_string = -1; /* FIXME: make static */
- return p;
-}
+/* Dissect a counted string as a callback to dissect_ndr_pointer_cb() */
-/* Free a pointer */
+static int hf_nt_cs_len = -1;
+static int hf_nt_cs_size = -1;
-static void free_ptr(struct ptr *p)
+int
+dissect_ndr_counted_string_cb(tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
+ char *drep, int hf_index,
+ dcerpc_callback_fnct_t *callback,
+ void *callback_args)
{
- if (p) {
- g_free(p->name);
- g_free(p);
- }
-}
+ dcerpc_info *di = pinfo->private_data;
+ guint16 len, size;
-/* Parse a pointer and store it's value in a linked list */
+ /* Structure starts with short, but is aligned for longs */
-int prs_push_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
- proto_tree *tree, GList **ptr_list, char *name)
-{
- struct ptr *p;
- guint32 value;
+ ALIGN_TO_4_BYTES;
- offset = prs_uint32(tvb, offset, pinfo, tree, &value, NULL);
+ if (di->conformant_run)
+ return offset;
+
+ /*
+ struct {
+ short len;
+ short size;
+ [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
+ } UNICODE_STRING;
- if (name && tree)
- proto_tree_add_text(tree, tvb, offset - 4, 4,
- "%s pointer: 0x%08x", name, value);
+ */
- p = new_ptr(name, value);
+ offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
+ hf_nt_cs_len, &len);
- *ptr_list = g_list_append(*ptr_list, p);
+ offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
+ hf_nt_cs_size, &size);
-#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
+ 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;
}
-/* Pop a pointer of a given name. Return it's value. */
+static gint ett_nt_counted_string = -1;
-guint32 prs_pop_ptr(GList **ptr_list, char *name _U_)
+static int
+dissect_ndr_counted_string_helper(tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
+ char *drep, int hf_index, int levels,
+ gboolean add_subtree)
{
- GList *elt;
- struct ptr *p;
- guint32 result;
-
- g_assert(g_list_length(*ptr_list) != 0); /* List too short */
-
- /* Get pointer at head of list */
-
- elt = g_list_first(*ptr_list);
- p = (struct ptr *)elt->data;
- result = p->value;
-
-#ifdef DEBUG_PTRS
- if (strcmp(p->name, name) != 0) {
- fprintf(stderr, "DEBUG_PTRS: wrong pointer (%s != %s)\n",
- p->name, name);
- }
-#endif
-
- /* Free pointer record */
-
- *ptr_list = g_list_remove_link(*ptr_list, elt);
-
-#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
-
- free_ptr(p);
+ proto_item *item;
+ proto_tree *subtree = tree;
- return result;
-}
+ if (add_subtree) {
-/* 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. */
+ item = proto_tree_add_text(
+ tree, tvb, offset, 0,
+ proto_registrar_get_name(hf_index));
-char *fake_unicode(tvbuff_t *tvb, int offset, int len)
-{
- char *buffer;
- int i;
- guint16 character;
-
- /* Make sure we have enough data before allocating the buffer,
- so we don't blow up if the length is huge.
- We do so by attempting to fetch the last character; it'll
- throw an exception if it's past the end. */
- tvb_get_letohs(tvb, offset + 2*(len - 1));
-
- /* We know we won't throw an exception, so we don't have to worry
- about leaking this buffer. */
- buffer = g_malloc(len + 1);
-
- for (i = 0; i < len; i++) {
- character = tvb_get_letohs(tvb, offset);
- buffer[i] = character & 0xff;
- offset += 2;
+ subtree = proto_item_add_subtree(item, ett_nt_counted_string);
}
- buffer[len] = 0;
-
- return buffer;
+ /*
+ * 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 UNISTR2 structure */
+/* Dissect a counted string in-line. */
-int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
- proto_tree *tree, int flags, char **data, char *name _U_)
+int
+dissect_ndr_counted_string(tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
+ char *drep, int hf_index, int levels)
{
- 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");
- }
-
- if (flags & PARSE_BUFFERS) {
- int data16_offset;
-
- offset = prs_uint16s(tvb, offset, pinfo, tree, max_len,
- &data16_offset, "Buffer");
-
- if (data)
- *data = fake_unicode(tvb, data16_offset, max_len);
- }
-
- return offset;
+ return dissect_ndr_counted_string_helper(
+ tvb, offset, pinfo, tree, drep, hf_index, levels, TRUE);
}
-/* Parse a policy handle. */
+/* 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. */
-int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
- proto_tree *tree, const guint8 **data)
+int
+dissect_ndr_counted_string_ptr(tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
+ char *drep)
{
- const guint8 *data8;
-
- offset = prs_align(offset, 4);
+ dcerpc_info *di = pinfo->private_data;
- proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle: %s",
- tvb_bytes_to_str(tvb, offset, 20));
-
- data8 = tvb_get_ptr(tvb, offset, 20);
-
- if (data)
- *data = data8;
-
- return offset + 20;
+ return dissect_ndr_counted_string_helper(
+ tvb, offset, pinfo, tree, drep, di->hf_index, 0, FALSE);
}
+/* Dissect a counted byte_array as a callback to dissect_ndr_pointer_cb() */
+static gint ett_nt_counted_byte_array = -1;
-/* 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.
-*/
-
-
-/* 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;
-
- 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;
+/* Dissect a counted byte array in-line. */
-gint ett_nt_unicode_string = -1;
-static gint ett_nt_policy_hnd = -1;
-
-/* this function will dissect the
- [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
- part of the unicode string
-
- 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.
-
- 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_ndr_counted_byte_array_cb(tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
+ char *drep, int hf_index,
+ dcerpc_callback_fnct_t *callback,
+ void *callback_args)
{
- guint32 len, off, max_len;
- int data16_offset;
- char *text;
- int old_offset;
- dcerpc_info *di;
+ dcerpc_info *di = pinfo->private_data;
+ proto_item *item;
+ proto_tree *subtree;
+ guint16 len, size;
- di=pinfo->private_data;
- if(di->conformant_run){
- /*just a run to handle conformant arrays, nothing to dissect */
- return offset;
- }
+ /* Structure starts with short, but is aligned for longs */
- offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
- hf_nt_str_max_len, &max_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_len, &len);
-
- old_offset=offset;
- offset = prs_uint16s(tvb, offset, pinfo, tree, len, &data16_offset,
- NULL);
- text = fake_unicode(tvb, data16_offset, 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);
- di->levels--;
- if(di->levels>-1){
- tree=tree->parent;
- proto_item_append_text(tree, ": %s", text);
- di->levels--;
- while(di->levels>-1){
- tree=tree->parent;
- proto_item_append_text(tree, " %s", text);
- di->levels--;
- }
- }
- }
- g_free(text);
- return offset;
-}
+ ALIGN_TO_4_BYTES;
-/* 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.
-*/
-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)
-{
- proto_item *item=NULL;
- proto_tree *tree=NULL;
- int old_offset=offset;
- dcerpc_info *di;
- char *name;
+ if (di->conformant_run)
+ return offset;
- ALIGN_TO_4_BYTES; /* strcture starts with short, but is aligned for longs */
+ item = proto_tree_add_text(tree, tvb, offset, 0,
+ proto_registrar_get_name(hf_index));
- di=pinfo->private_data;
- if(di->conformant_run){
- /*just a run to handle conformant arrays, nothing to dissect */
- return offset;
- }
+ subtree = proto_item_add_subtree(item, ett_nt_counted_byte_array);
+
+ /*
+ struct {
+ short len;
+ short size;
+ [size_is(size), length_is(len), ptr] unsigned char *string;
+ } WHATEVER_THIS_IS_CALLED;
- 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);
- }
+ */
- 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; /* XXX - is this necessary? */
- /* Add 1 level, for the extra level we added */
- offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
- dissect_ndr_nt_UNICODE_STRING_str, NDR_POINTER_UNIQUE,
- name, hf_index, levels + 1);
-
- proto_item_set_len(item, offset-old_offset);
- return offset;
-}
-/* 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)
-{
- guint32 len, off, max_len;
- int text_offset;
- const guint8 *text;
- int old_offset;
- header_field_info *hfi;
- dcerpc_info *di;
+ offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
+ hf_nt_cs_len, &len);
- di=pinfo->private_data;
- if(di->conformant_run){
- /*just a run to handle conformant arrays, nothing to dissect */
- return offset;
- }
+ offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
+ hf_nt_cs_size, &size);
- 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_offset, NULL);
- text = tvb_get_ptr(tvb, text_offset, max_len);
- 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 = NULL;
- proto_tree_add_item(tree, di->hf_index, tvb, offset, max_len, FALSE);
- offset += max_len;
- break;
- default:
- text = NULL;
- g_assert_not_reached();
- }
+ offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, subtree, drep,
+ dissect_ndr_char_cvstring, NDR_POINTER_UNIQUE,
+ "Byte Array", hf_index, callback, callback_args);
- if(tree && text && (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--;
- }
- }
- }
- return offset;
+ return offset;
}
int
-dissect_ndr_nt_STRING (tvbuff_t *tvb, int offset,
- packet_info *pinfo, proto_tree *parent_tree,
- char *drep, int hf_index, int levels)
+dissect_ndr_counted_byte_array(tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
+ char *drep, int hf_index)
{
- proto_item *item=NULL;
- proto_tree *tree=NULL;
- int old_offset=offset;
- dcerpc_info *di;
- char *name;
-
- ALIGN_TO_4_BYTES; /* strcture starts with short, but is aligned for longs */
-
- di=pinfo->private_data;
- if(di->conformant_run){
- /*just a run to handle conformant arrays, nothing to dissect */
- return offset;
- }
-
- 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);
- }
-
- 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);
-
- proto_item_set_len(item, offset-old_offset);
- return offset;
+ return dissect_ndr_counted_byte_array_cb(
+ tvb, offset, pinfo, tree, drep, hf_index, NULL, NULL);
}
-
/* 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
+ 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,
+dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset,
+ packet_info *pinfo, proto_tree *tree,
char *drep _U_, int hf_index)
{
dcerpc_info *di;
#undef DEBUG_HASH_COLL
/*
- * Policy handle hashing
+ * Policy handle hashing.
+ *
+ * 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).
*/
typedef struct {
guint8 policy_hnd[20];
} pol_hash_key;
-typedef struct {
+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
static GHashTable *pol_hash;
static GMemChunk *pol_hash_key_chunk;
+static GMemChunk *pol_value_chunk;
static GMemChunk *pol_hash_value_chunk;
/* Hash function */
static guint pol_hash_fn(gconstpointer k)
{
- pol_hash_key *key = (pol_hash_key *)k;
+ 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 */
-
+
return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
(key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
}
/* Return true if a policy handle is all zeros */
-static gboolean is_null_pol(const guint8 *policy_hnd)
+static gboolean is_null_pol(e_ctx_hnd *policy_hnd)
{
static guint8 null_policy_hnd[20];
static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
{
- pol_hash_key *key1 = (pol_hash_key *)k1;
- pol_hash_key *key2 = (pol_hash_key *)k2;
+ 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,
+ return memcmp(key1->policy_hnd, key2->policy_hnd,
sizeof(key1->policy_hnd)) == 0;
}
-/* Store a policy handle */
+/*
+ * 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;
+ }
+}
-void dcerpc_smb_store_pol(const guint8 *policy_hnd, char *name,
- guint32 open_frame, guint32 close_frame)
+static void add_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
+ pol_value *pol, pol_hash_value *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;
+ }
+}
+
+/* Store the open and close frame numbers of a policy handle */
+
+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;
+ }
+ }
- key = g_mem_chunk_alloc(pol_hash_key_chunk);
+ /* Create a new value */
- memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
+ pol = g_mem_chunk_alloc(pol_value_chunk);
- if ((value = g_hash_table_lookup(pol_hash, key))) {
+ 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 */
- /* Update existing value */
+ pol->name = NULL;
- if (value->name && name) {
-#ifdef DEBUG_HASH_COLL
- if (strcmp(value->name, name) != 0)
- g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
-#endif
- free(value->name);
- value->name = strdup(name);
- }
+ add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
+}
- if (open_frame) {
-#ifdef DEBUG_HASH_COLL
- if (value->open_frame != open_frame)
- g_warning("dcerpc_smb: pol_hash open frame collision %d/%d\n", value->open_frame, open_frame);
-#endif
- value->open_frame = open_frame;
- }
+/* Store a text string with a policy handle */
+
+void dcerpc_smb_store_pol_name(e_ctx_hnd *policy_hnd, packet_info *pinfo,
+ char *name)
+{
+ 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;
- if (close_frame) {
+ /* 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 (value->close_frame != close_frame)
- g_warning("dcerpc_smb: pol_hash close frame collision %d/%d\n", value->close_frame, close_frame);
+ if (strcmp(pol->name, name) != 0)
+ g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
#endif
- value->close_frame = close_frame;
+ free(pol->name);
}
+ pol->name = strdup(name);
+
return;
}
/* Create a new value */
- value = g_mem_chunk_alloc(pol_hash_value_chunk);
+ pol = g_mem_chunk_alloc(pol_value_chunk);
- value->open_frame = open_frame;
- value->close_frame = close_frame;
+ pol->open_frame = 0;
+ pol->close_frame = 0;
+ pol->first_frame = pinfo->fd->num;
+ pol->last_frame = 0;
if (name)
- value->name = strdup(name);
+ pol->name = strdup(name);
else
- value->name = strdup("UNKNOWN");
+ pol->name = strdup("<UNKNOWN>");
- g_hash_table_insert(pol_hash, key, value);
+ add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
}
-/* Retrieve a policy handle */
+/*
+ * 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?
+ */
-gboolean dcerpc_smb_fetch_pol(const guint8 *policy_hnd, char **name,
- guint32 *open_frame, guint32 *close_frame)
+gboolean dcerpc_smb_fetch_pol(e_ctx_hnd *policy_hnd, char **name,
+ guint32 *open_frame, guint32 *close_frame,
+ guint32 cur_frame)
{
- pol_hash_key key;
pol_hash_value *value;
+ pol_value *pol;
/* Prevent uninitialised return vars */
*close_frame = 0;
/* Look up existing value */
+ pol = find_pol_handle(policy_hnd, cur_frame, &value);
- memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
-
- value = g_hash_table_lookup(pol_hash, &key);
+ if (pol) {
+ if (name)
+ *name = pol->name;
- if (!value)
- return FALSE;
-
- /* Return name and frame numbers */
-
- if (name)
- *name = value->name;
-
- if (open_frame)
- *open_frame = value->open_frame;
+ if (open_frame)
+ *open_frame = pol->open_frame;
- if (close_frame)
- *close_frame = value->close_frame;
+ if (close_frame)
+ *close_frame = pol->close_frame;
+ }
- return TRUE;
+ return pol != NULL;
}
-/* Iterator to free a policy handle key/value pair */
+/* Iterator to free a policy handle key/value pair, and all
+ the policy handle values to which the hash table value
+ points */
-static void free_pol_keyvalue(gpointer key, gpointer value, gpointer user_data)
+static void free_pol_keyvalue(gpointer key _U_, gpointer value_arg,
+ gpointer user_data _U_)
{
- pol_hash_value *pol_value = (pol_hash_value *)value;
+ pol_hash_value *value = (pol_hash_value *)value_arg;
+ pol_value *pol;
/* Free user data */
- if (pol_value->name) {
- free(pol_value->name);
- pol_value->name = NULL;
+ for (pol = value->list; pol != NULL; pol = pol->next) {
+ free(pol->name);
+ pol->name = NULL;
}
}
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 (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 = g_hash_table_new(pol_hash_fn, pol_hash_compare);
}
-/*
- * Initialise global DCERPC/SMB data structures
- */
-
-static void dcerpc_smb_init(void)
-{
- /* Initialise policy handle hash */
-
- init_pol_hash();
-}
+/* Dissect a NT status code */
-/*
- * Register ett_ values, and register "dcerpc_smb_init()" as an
- * initialisation routine.
- */
-void proto_register_dcerpc_smb(void)
+int
+dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+ proto_tree *tree, char *drep,
+ int hfindex, guint32 *pdata)
{
- static gint *ett[] = {
- &ett_nt_unicode_string,
- &ett_nt_policy_hnd,
- };
-
-
- /* Register ett's */
-
- proto_register_subtree_array(ett, array_length(ett));
-
- /* Register a routine to be called whenever initialisation
- is done. */
-
- register_init_routine(dcerpc_smb_init);
-}
-
-/* Check if there is unparsed data remaining in a frame and display an
- error. I guess this could be made into an exception like the malformed
- frame exception. For the DCERPC over SMB dissectors a long frame
- indicates a bug in a dissector. */
+ guint32 status;
-void dcerpc_smb_check_long_frame(tvbuff_t *tvb, int offset,
- packet_info *pinfo, proto_tree *tree)
-{
- if (tvb_length_remaining(tvb, offset) != 0) {
+ offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
+ hfindex, &status);
- proto_tree_add_text(tree, tvb, offset, 0,
- "[Long frame (%d bytes): SPOOLSS]",
- tvb_length_remaining(tvb, offset));
+ 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;
- if (check_col(pinfo->cinfo, COL_INFO))
- col_append_fstr(pinfo->cinfo, COL_INFO,
- "[Long frame (%d bytes): SPOOLSS]",
- tvb_length_remaining(tvb, offset));
- }
+ return offset;
}
-/* Dissect a NT status code */
+/* Dissect a DOS status code */
int
-dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
- proto_tree *tree, char *drep,
- int hfindex, guint32 *pdata)
+dissect_doserror(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+ proto_tree *tree, char *drep,
+ int hfindex, guint32 *pdata)
{
guint32 status;
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"));
+ val_to_str(status, DOS_errors,
+ "Unknown error 0x%08x"));
if (pdata)
*pdata = status;
/* Dissect a NT policy handle */
+static int hf_nt_policy_open_frame = -1;
+static int hf_nt_policy_close_frame = -1;
+
+static gint ett_nt_policy_hnd = -1;
+
int
dissect_nt_policy_hnd(tvbuff_t *tvb, gint offset, packet_info *pinfo,
- proto_tree *tree, char *drep, int hfindex,
- e_ctx_hnd *pdata)
+ proto_tree *tree, char *drep, int hfindex,
+ e_ctx_hnd *pdata, proto_item **pitem,
+ gboolean is_open, gboolean is_close)
{
- dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
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, 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;
+ }
/* Add to proto tree */
- item = proto_tree_add_text(tree, tvb, offset, sizeof(e_ctx_hnd),
+ 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);
+ offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep,
+ hfindex, &hnd);
- /* Insert request/reply information if known */
+ /*
+ * 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);
- if (dcerpc_smb_fetch_pol((const guint8 *)&hnd, &name, &open_frame,
- &close_frame)) {
+ /* 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_text(subtree, tvb, old_offset,
- sizeof(e_ctx_hnd),
- "Opened in frame %u", 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_text(subtree, tvb, old_offset,
- sizeof(e_ctx_hnd),
- "Closed in frame %u", 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);
}
- /* Store request/reply information */
-
- if (di->request)
- dcerpc_smb_store_pol((const guint8 *)&hnd, NULL, 0,
- pinfo->fd->num);
- else
- dcerpc_smb_store_pol((const guint8 *)&hnd, NULL,
- pinfo->fd->num, 0);
-
if (pdata)
*pdata = hnd;
+ if (pitem)
+ *pitem = item;
+
return offset;
}
+
+/* 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_dcerpc_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
+ proto_tree *tree, char *drep, int hfindex,
+ int length, const guint8 **pdata)
+{
+ const guint8 *data;
+
+ data = (const guint8 *)tvb_get_ptr(tvb, offset, length);
+
+ if (tree) {
+ proto_tree_add_item (tree, hfindex, tvb, offset, length, (drep[0] & 0x10));
+ }
+
+ if (pdata)
+ *pdata = data;
+
+ return offset + length;
+}
+
+int
+dissect_ndr_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+ proto_tree *tree, char *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);
+}
+
+int
+dissect_dcerpc_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
+ proto_tree *tree, char *drep, int hfindex,
+ int length)
+{
+ if (tree) {
+ proto_tree_add_item (tree, hfindex, tvb, offset, length * 2, (drep[0] & 0x10));
+ }
+
+ return offset + length * 2;
+}
+
+int
+dissect_ndr_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+ proto_tree *tree, char *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);
+ }
+
+ /* 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--;
+ }
+ }
+ }
+
+ /* 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);
+}
+
+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)
+{
+ 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_get_string(
+ tvb, start_offset + 12, (end_offset - start_offset - 12) );
+
+ /* 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);
+ }
+
+ /* 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--;
+ }
+ }
+ }
+
+ /* 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);
+}
+
+/* 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,
+ char *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();
+}