From Aaron Woo (via Jeff Weston): Optimized Link State Routing Protocol
[obnox/wireshark/wip.git] / packet-dcerpc.c
index e4d9bbbbf3b3a277db108e5388933ec236a5b3f8..ef4ec86ab95b50a6ea51b4040021698a68e6e084 100644 (file)
@@ -1,8 +1,9 @@
 /* packet-dcerpc.c
  * Routines for DCERPC packet disassembly
  * Copyright 2001, Todd Sabin <tas@webspan.net>
+ * Copyright 2003, Tim Potter <tpot@samba.org>
  *
- * $Id: packet-dcerpc.c,v 1.96 2003/01/14 22:03:33 guy Exp $
+ * $Id: packet-dcerpc.c,v 1.158 2003/12/08 20:58:01 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #include "reassemble.h"
 #include "tap.h"
 #include "packet-frame.h"
-#include "packet-ntlmssp.h"
+#include "packet-dcerpc-nt.h"
 
 static int dcerpc_tap = -1;
 
+
 static const value_string pckt_vals[] = {
     { PDU_REQ,        "Request"},
     { PDU_PING,       "Ping"},
@@ -94,12 +96,6 @@ static const value_string drep_fp_vals[] = {
 /*
  * Authentication services.
  */
-#define DCE_C_RPC_AUTHN_PROTOCOL_NONE          0
-#define DCE_C_RPC_AUTHN_PROTOCOL_KRB5          1
-#define DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO         9
-#define DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP       10
-#define DCE_C_RPC_AUTHN_PROTOCOL_SEC_CHAN       68
-
 static const value_string authn_protocol_vals[] = {
        { DCE_C_RPC_AUTHN_PROTOCOL_NONE,    "None" },
        { DCE_C_RPC_AUTHN_PROTOCOL_KRB5,    "Kerberos 5" },
@@ -112,13 +108,6 @@ static const value_string authn_protocol_vals[] = {
 /*
  * Protection levels.
  */
-#define DCE_C_AUTHN_LEVEL_NONE         1
-#define DCE_C_AUTHN_LEVEL_CONNECT      2
-#define DCE_C_AUTHN_LEVEL_CALL         3
-#define DCE_C_AUTHN_LEVEL_PKT          4
-#define DCE_C_AUTHN_LEVEL_PKT_INTEGRITY        5
-#define DCE_C_AUTHN_LEVEL_PKT_PRIVACY  6
-
 static const value_string authn_level_vals[] = {
        { DCE_C_AUTHN_LEVEL_NONE,          "None" },
        { DCE_C_AUTHN_LEVEL_CONNECT,       "Connect" },
@@ -386,6 +375,7 @@ static int hf_dcerpc_dg_status = -1;
 static int hf_dcerpc_array_max_count = -1;
 static int hf_dcerpc_array_offset = -1;
 static int hf_dcerpc_array_actual_count = -1;
+static int hf_dcerpc_array_buffer = -1;
 static int hf_dcerpc_op = -1;
 static int hf_dcerpc_referent_id = -1;
 static int hf_dcerpc_fragments = -1;
@@ -395,20 +385,20 @@ static int hf_dcerpc_fragment_overlap_conflict = -1;
 static int hf_dcerpc_fragment_multiple_tails = -1;
 static int hf_dcerpc_fragment_too_long_fragment = -1;
 static int hf_dcerpc_fragment_error = -1;
+static int hf_dcerpc_reassembled_in = -1;
 
 static gint ett_dcerpc = -1;
 static gint ett_dcerpc_cn_flags = -1;
+static gint ett_dcerpc_cn_ctx = -1;
+static gint ett_dcerpc_cn_iface = -1;
 static gint ett_dcerpc_drep = -1;
 static gint ett_dcerpc_dg_flags1 = -1;
 static gint ett_dcerpc_dg_flags2 = -1;
 static gint ett_dcerpc_pointer_data = -1;
+static gint ett_dcerpc_string = -1;
 static gint ett_dcerpc_fragments = -1;
 static gint ett_dcerpc_fragment = -1;
-static gint ett_decrpc_krb5_auth_verf = -1;
-
-static dissector_handle_t ntlmssp_handle, ntlmssp_verf_handle,
-  ntlmssp_enc_payload_handle;
-static dissector_handle_t gssapi_handle, gssapi_verf_handle;
+static gint ett_dcerpc_krb5_auth_verf = -1;
 
 static const fragment_items dcerpc_frag_items = {
        &ett_dcerpc_fragments,
@@ -421,16 +411,25 @@ static const fragment_items dcerpc_frag_items = {
        &hf_dcerpc_fragment_multiple_tails,
        &hf_dcerpc_fragment_too_long_fragment,
        &hf_dcerpc_fragment_error,
+       NULL,
 
        "fragments"
 };
 
-typedef struct _dcerpc_auth_info {
-  guint8 auth_pad_len;
-  guint8 auth_level;
-  guint8 auth_type;
-  guint32 auth_size;
-} dcerpc_auth_info;
+
+
+static dcerpc_info *
+get_next_di(void)
+{
+       static dcerpc_info di[20];
+       static int di_counter=0;
+
+       di_counter++;
+       if(di_counter>=20){
+               di_counter=0;
+       }
+       return &di[di_counter];
+}
 
 /* try to desegment big DCE/RPC packets over TCP? */
 static gboolean dcerpc_cn_desegment = TRUE;
@@ -452,6 +451,122 @@ dcerpc_reassemble_init(void)
   fragment_table_init(&dcerpc_cl_reassemble_table);
 }
 
+/*
+ * Authentication subdissectors.  Used to dissect authentication blobs in
+ * DCERPC binds, requests and responses.
+ */
+
+typedef struct _dcerpc_auth_subdissector {
+       guint8 auth_level;
+       guint8 auth_type;
+       dcerpc_auth_subdissector_fns auth_fns;
+} dcerpc_auth_subdissector;
+
+static GSList *dcerpc_auth_subdissector_list;
+
+static dcerpc_auth_subdissector_fns *get_auth_subdissector_fns(
+       guint8 auth_level, guint8 auth_type)
+{
+       gpointer data;
+       int i;
+
+       for (i = 0; (data = g_slist_nth_data(dcerpc_auth_subdissector_list, i)); i++) {
+               dcerpc_auth_subdissector *asd = (dcerpc_auth_subdissector *)data;
+
+               if (asd->auth_level == auth_level && 
+                   asd->auth_type == auth_type)
+                       return &asd->auth_fns;
+       }
+
+       return NULL;
+}
+
+void register_dcerpc_auth_subdissector(guint8 auth_level, guint8 auth_type,
+                                      dcerpc_auth_subdissector_fns *fns)
+{
+       dcerpc_auth_subdissector *d;
+
+       if (get_auth_subdissector_fns(auth_level, auth_type))
+               return;
+
+       d = (dcerpc_auth_subdissector *)g_malloc(sizeof(dcerpc_auth_subdissector));
+
+       d->auth_level = auth_level;
+       d->auth_type = auth_type;
+       memcpy(&d->auth_fns, fns, sizeof(dcerpc_auth_subdissector_fns));
+
+       dcerpc_auth_subdissector_list = g_slist_append(dcerpc_auth_subdissector_list, d);
+}
+
+/* Hand off verifier data to a registered dissector */
+
+static void dissect_auth_verf(tvbuff_t *auth_tvb, packet_info *pinfo,
+                             proto_tree *tree, 
+                             dcerpc_auth_subdissector_fns *auth_fns,
+                             e_dce_cn_common_hdr_t *hdr, 
+                             dcerpc_auth_info *auth_info)
+{
+       dcerpc_dissect_fnct_t *volatile fn = NULL;
+
+       switch (hdr->ptype) {
+       case PDU_BIND:
+       case PDU_ALTER:
+               fn = auth_fns->bind_fn;
+               break;
+       case PDU_BIND_ACK:
+       case PDU_ALTER_ACK:
+               fn = auth_fns->bind_ack_fn;
+               break;
+       case PDU_AUTH3:
+               fn = auth_fns->auth3_fn;
+               break;
+       case PDU_REQ:
+               fn = auth_fns->req_verf_fn;
+               break;
+       case PDU_RESP:
+               fn = auth_fns->resp_verf_fn;
+               break;
+
+               /* Don't know how to handle authentication data in this 
+                  pdu type. */
+
+       default:
+               g_warning("attempt to dissect %s pdu authentication data",
+                         val_to_str(hdr->ptype, pckt_vals, "Unknown (%u)"));
+               break;
+       }
+
+       if (fn)
+               fn(auth_tvb, 0, pinfo, tree, hdr->drep);
+       else
+               proto_tree_add_text(tree, auth_tvb, 0, hdr->auth_len,
+                                   "%s Verifier", 
+                                   val_to_str(auth_info->auth_type, 
+                                              authn_protocol_vals,
+                                              "Unknown (%u)"));
+}
+
+/* Hand off payload data to a registered dissector */
+
+static tvbuff_t *decode_encrypted_data(tvbuff_t *enc_tvb, 
+                                      packet_info *pinfo,
+                                      dcerpc_auth_subdissector_fns *auth_fns,
+                                      gboolean is_request, 
+                                      dcerpc_auth_info *auth_info)
+{
+       dcerpc_decode_data_fnct_t *fn;
+
+       if (is_request)
+               fn = auth_fns->req_data_fn;
+       else
+               fn = auth_fns->resp_data_fn;
+
+       if (fn)
+               return fn(enc_tvb, 0, pinfo, auth_info);
+
+       return NULL;
+}
+
 /*
  * Subdissectors
  */
@@ -483,17 +598,22 @@ dcerpc_init_uuid (int proto, int ett, e_uuid_t *uuid, guint16 ver,
 {
     dcerpc_uuid_key *key = g_malloc (sizeof (*key));
     dcerpc_uuid_value *value = g_malloc (sizeof (*value));
+    header_field_info *hf_info;
 
     key->uuid = *uuid;
     key->ver = ver;
 
-    value->proto = proto;
+    value->proto = find_protocol_by_id(proto);
+    value->proto_id = proto;
     value->ett = ett;
-    value->name = proto_get_protocol_short_name (proto);
+    value->name = proto_get_protocol_short_name (value->proto);
     value->procs = procs;
     value->opnum_hf = opnum_hf;
 
     g_hash_table_insert (dcerpc_uuids, key, value);
+
+    hf_info = proto_registrar_get_nth(opnum_hf);
+    hf_info->strings = value_string_from_subdissectors(procs);
 }
 
 /* Function to find the name of a registered protocol
@@ -513,6 +633,51 @@ dcerpc_get_proto_name(e_uuid_t *uuid, guint16 ver)
     return sub_proto->name;
 }
 
+/* Function to find the opnum hf-field of a registered protocol
+ * or -1 if the protocol/version is not known to ethereal.
+ */
+int
+dcerpc_get_proto_hf_opnum(e_uuid_t *uuid, guint16 ver)
+{
+    dcerpc_uuid_key key;
+    dcerpc_uuid_value *sub_proto;
+
+    key.uuid = *uuid;
+    key.ver = ver;
+    if(!(sub_proto = g_hash_table_lookup (dcerpc_uuids, &key))){
+        return -1;
+    }
+    return sub_proto->opnum_hf;
+}
+
+/* Create a value_string consisting of DCERPC opnum and name from a
+   subdissector array. */
+
+value_string *value_string_from_subdissectors(dcerpc_sub_dissector *sd)
+{
+       value_string *vs = NULL;
+       int i, num_sd = 0;
+
+ again:
+       for (i = 0; sd[i].name; i++) {
+               if (vs) {
+                       vs[i].value = sd[i].num;
+                       vs[i].strptr = sd[i].name;
+               } else
+                       num_sd++;
+       }
+
+       if (!vs) {
+               vs = g_malloc((num_sd + 1) * sizeof(value_string));
+               goto again;
+       }
+
+       vs[num_sd].value = 0;
+       vs[num_sd].strptr = NULL;
+
+       return vs;
+}
+
 /* Function to find the subdissector table of a registered protocol
  * or NULL if the protocol/version is not known to ethereal.
  */
@@ -568,7 +733,8 @@ static guint
 dcerpc_bind_hash (gconstpointer k)
 {
     const dcerpc_bind_key *key = (const dcerpc_bind_key *)k;
-    return ((guint)key->conv) + key->ctx_id + key->smb_fid;
+    return GPOINTER_TO_UINT(key->conv) + key->ctx_id + key->smb_fid;
+
 }
 
 /*
@@ -607,18 +773,34 @@ dcerpc_call_hash (gconstpointer k)
 
 /* to keep track of matched calls/responses
    this one uses the same value struct as calls, but the key is the frame id
+   and call id; there can be more than one call in a frame.
+
+   XXX - why not just use the same keys as are used for calls?
 */
+
 static GHashTable *dcerpc_matched=NULL;
+
+typedef struct _dcerpc_matched_key {
+    guint32 frame;
+    guint32 call_id;
+} dcerpc_matched_key;
+
+static GMemChunk *dcerpc_matched_key_chunk=NULL;
+
 static gint
 dcerpc_matched_equal (gconstpointer k1, gconstpointer k2)
 {
-       return (guint32)k1 == (guint32)k2;
+    const dcerpc_matched_key *key1 = (const dcerpc_matched_key *)k1;
+    const dcerpc_matched_key *key2 = (const dcerpc_matched_key *)k2;
+    return (key1->frame == key2->frame
+            && key1->call_id == key2->call_id);
 }
 
 static guint
 dcerpc_matched_hash (gconstpointer k)
 {
-       return (guint32)k;
+    const dcerpc_matched_key *key = (const dcerpc_matched_key *)k;
+    return key->frame;
 }
 
 
@@ -857,7 +1039,7 @@ dissect_ndr_ucarray(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                di->conformant_run=1;
                di->conformant_eaten=offset-old_offset;
        } else {
-               /* we dont dont remember where  in the bytestream this fields was */
+               /* we don't remember where in the bytestream this field was */
                proto_tree_add_uint(tree, hf_dcerpc_array_max_count, tvb, di->array_max_count_offset, 4, di->array_max_count);
 
                /* real run, dissect the elements */
@@ -909,6 +1091,195 @@ dissect_ndr_ucvarray(tvbuff_t *tvb, gint offset, packet_info *pinfo,
        return offset;
 }
 
+/* Dissect an string of bytes.  This corresponds to
+   IDL of the form '[string] byte *foo'.
+
+   It can also be used for a conformant varying array of bytes if
+   the contents of the array should be shown as a big blob, rather
+   than showing each byte as an individual element.
+
+   XXX - which of those is really the IDL type for, for example,
+   the encrypted data in some MAPI packets?  (Microsoft haven't
+   released that IDL.)
+
+   XXX - does this need to do all the conformant array stuff that
+   "dissect_ndr_ucvarray()" does?  These are presumably for strings
+   that are conformant and varying - they're stored like conformant
+   varying arrays of bytes.  */
+int
+dissect_ndr_byte_array(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                            proto_tree *tree, char *drep)
+{
+    dcerpc_info *di;
+    guint32 len;
+
+    di=pinfo->private_data;
+    if(di->conformant_run){
+      /* just a run to handle conformant arrays, no scalars to dissect */
+      return offset;
+    }
+
+    /* NDR array header */
+
+    offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
+                                hf_dcerpc_array_max_count, NULL);
+
+    offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
+                                hf_dcerpc_array_offset, NULL);
+
+    offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
+                                hf_dcerpc_array_actual_count, &len);
+
+    if (tree && len)
+        proto_tree_add_item(tree, hf_dcerpc_array_buffer,
+                            tvb, offset, len, drep[0] & 0x10);
+
+    offset += len;
+
+    return offset;
+}
+
+/* For dissecting arrays that are to be interpreted as strings.  */
+
+/* Dissect an NDR conformant varying string of elements.
+   The length of each element is given by the 'size_is' parameter;
+   the elements are assumed to be characters or wide characters.
+
+   XXX - does this need to do all the conformant array stuff that
+   "dissect_ndr_ucvarray()" does?  */
+int
+dissect_ndr_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                    proto_tree *tree, char *drep, int size_is,
+                    int hfindex, gboolean add_subtree, char **data)
+{
+    dcerpc_info *di;
+    proto_item *string_item;
+    proto_tree *string_tree;
+    guint32 len, buffer_len;
+    char *s;
+    header_field_info *hfinfo;
+
+    di=pinfo->private_data;
+    if(di->conformant_run){
+      /* just a run to handle conformant arrays, no scalars to dissect */
+      return offset;
+    }
+
+    if (add_subtree) {
+        string_item = proto_tree_add_text(tree, tvb, offset, -1, "%s",
+                                          proto_registrar_get_name(hfindex));
+        string_tree = proto_item_add_subtree(string_item, ett_dcerpc_string);
+    } else {
+        string_item = NULL;
+        string_tree = tree;
+    }
+
+    /* NDR array header */
+
+    offset = dissect_ndr_uint32(tvb, offset, pinfo, string_tree, drep,
+                                hf_dcerpc_array_max_count, NULL);
+
+    offset = dissect_ndr_uint32(tvb, offset, pinfo, string_tree, drep,
+                                hf_dcerpc_array_offset, NULL);
+
+    offset = dissect_ndr_uint32(tvb, offset, pinfo, string_tree, drep,
+                                hf_dcerpc_array_actual_count, &len);
+
+    buffer_len = size_is * len;
+
+    /* Adjust offset */
+    if (offset % size_is)
+        offset += size_is - (offset % size_is);
+
+    if (size_is == sizeof(guint16)) {
+        /* XXX - use drep to determine the byte order? */
+        s = tvb_fake_unicode(tvb, offset, buffer_len / 2, TRUE);
+        /*
+         * XXX - we don't support a string type with Unicode
+         * characters, so if this is a string item, we make
+         * its value be the "fake Unicode" string.
+         */
+        if (tree && buffer_len) {
+            hfinfo = proto_registrar_get_nth(hfindex);
+            if (hfinfo->type == FT_STRING) {
+                proto_tree_add_string(string_tree, hfindex, tvb, offset,
+                                      buffer_len, s);
+            } else {
+                proto_tree_add_item(string_tree, hfindex, tvb, offset,
+                                    buffer_len, drep[0] & 0x10);
+            }
+        }
+    } else {
+        /*
+         * "tvb_get_string()" throws an exception if the entire string
+         * isn't in the tvbuff.  If the length is bogus, this should
+         * keep us from trying to allocate an immensely large buffer.
+         * (It won't help if the length is *valid* but immensely large,
+         * but that's another matter; in any case, that would happen only
+         * if we had an immensely large tvbuff....)
+         */
+        s = tvb_get_string(tvb, offset, buffer_len);
+        if (tree && buffer_len)
+            proto_tree_add_item(string_tree, hfindex, tvb, offset,
+                                buffer_len, drep[0] & 0x10);
+    }
+
+    if (string_item != NULL)
+        proto_item_append_text(string_item, ": %s", s);
+
+    if (data)
+           *data = s;
+    else
+           g_free(s);
+    
+    offset += buffer_len;
+
+    proto_item_set_end(string_item, tvb, offset);
+
+    return offset;
+}
+
+/* Dissect an conformant varying string of chars.
+   This corresponds to IDL of the form '[string] char *foo'.
+
+   XXX - at least according to the DCE RPC 1.1 spec, a string has
+   a null terminator, which isn't necessary as a terminator for
+   the transfer language (as there's a length), but is presumably
+   there for the benefit of null-terminated-string languages
+   such as C.  Is this ever used for purely counted strings?
+   (Not that it matters if it is.) */
+int
+dissect_ndr_char_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                       proto_tree *tree, char *drep)
+{
+    dcerpc_info *di;
+    di=pinfo->private_data;
+
+    return dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
+                               sizeof(guint8), di->hf_index,
+                               FALSE, NULL);
+}
+
+/* Dissect a conformant varying string of wchars (wide characters).
+   This corresponds to IDL of the form '[string] wchar *foo'
+
+   XXX - at least according to the DCE RPC 1.1 spec, a string has
+   a null terminator, which isn't necessary as a terminator for
+   the transfer language (as there's a length), but is presumably
+   there for the benefit of null-terminated-string languages
+   such as C.  Is this ever used for purely counted strings?
+   (Not that it matters if it is.) */
+int
+dissect_ndr_wchar_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, 
+                       proto_tree *tree, char *drep)
+{
+    dcerpc_info *di;
+    di=pinfo->private_data;
+
+    return dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
+                               sizeof(guint16), di->hf_index,
+                               FALSE, NULL);
+}
 
 /* ndr pointer handling */
 /* list of pointers encountered so far */
@@ -924,10 +1295,12 @@ static gboolean pointers_are_top_level = TRUE;
    hoping that his will not collide with any non-ref pointers */
 typedef struct ndr_pointer_data {
        guint32 id;
-       proto_tree *tree;
+       proto_item *item;       /* proto_item for pointer */
+       proto_tree *tree;       /* subtree of above item */
        dcerpc_dissect_fnct_t *fnct; /*if non-NULL, we have not called it yet*/
        int hf_index;
-       int levels;
+       dcerpc_callback_fnct_t *callback;
+       void *callback_args;
 } ndr_pointer_data_t;
 
 static void
@@ -959,25 +1332,27 @@ dissect_deferred_pointers(packet_info *pinfo, tvbuff_t *tvb, int offset, char *d
        int found_new_pointer;
        dcerpc_info *di;
        int old_offset;
+       int next_pointer;
 
+       next_pointer=0;
        di=pinfo->private_data;
        do{
                int i, len;
 
                found_new_pointer=0;
                len=g_slist_length(ndr_pointer_list);
-               for(i=0;i<len;i++){
+               for(i=next_pointer;i<len;i++){
                        ndr_pointer_data_t *tnpd;
                        tnpd=g_slist_nth_data(ndr_pointer_list, i);
                        if(tnpd->fnct){
                                dcerpc_dissect_fnct_t *fnct;
 
+                               next_pointer=i+1;
                                found_new_pointer=1;
                                fnct=tnpd->fnct;
                                tnpd->fnct=NULL;
                                ndr_pointer_list_pos=i+1;
                                di->hf_index=tnpd->hf_index;
-                               di->levels=tnpd->levels;
                                /* first a run to handle any conformant
                                   array headers */
                                di->conformant_run=1;
@@ -1034,7 +1409,10 @@ dissect_deferred_pointers(packet_info *pinfo, tvbuff_t *tvb, int offset, char *d
 
                                /* now we dissect the actual pointer */
                                di->conformant_run=0;
+                               old_offset = offset;
                                offset = (*(fnct))(tvb, offset, pinfo, tnpd->tree, drep);
+                               if (tnpd->callback)
+                                       tnpd->callback(pinfo, tnpd->tree, tnpd->item, tvb, old_offset, offset, tnpd->callback_args);
                                break;
                        }
                }
@@ -1045,8 +1423,9 @@ dissect_deferred_pointers(packet_info *pinfo, tvbuff_t *tvb, int offset, char *d
 
 
 static void
-add_pointer_to_list(packet_info *pinfo, proto_tree *tree,
-               dcerpc_dissect_fnct_t *fnct, guint32 id, int hf_index, int levels)
+add_pointer_to_list(packet_info *pinfo, proto_tree *tree, proto_item *item,
+                   dcerpc_dissect_fnct_t *fnct, guint32 id, int hf_index, 
+                   dcerpc_callback_fnct_t *callback, void *callback_args)
 {
        ndr_pointer_data_t *npd;
 
@@ -1083,9 +1462,11 @@ add_pointer_to_list(packet_info *pinfo, proto_tree *tree,
        npd=g_malloc(sizeof(ndr_pointer_data_t));
        npd->id=id;
        npd->tree=tree;
+       npd->item=item;
        npd->fnct=fnct;
        npd->hf_index=hf_index;
-       npd->levels=levels;
+       npd->callback=callback;
+       npd->callback_args=callback_args;
        ndr_pointer_list = g_slist_insert(ndr_pointer_list, npd,
                                        ndr_pointer_list_pos);
        ndr_pointer_list_pos++;
@@ -1124,15 +1505,17 @@ find_pointer_index(guint32 id)
  *   hf_index is what hf value we want to pass to the callback function when
  *   it is called, the callback can later pich this one up from di->hf_index.
  *
- *   levels is a generic int we want to pass to teh callback function.  the
- *   callback can later pick it up from di->levels
+ *   callback is executed after the pointer has been dereferenced.
+ *
+ *   callback_args is passed as an argument to the callback function
  *
  * See packet-dcerpc-samr.c for examples
  */
 int
-dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+dissect_ndr_pointer_cb(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                    proto_tree *tree, char *drep, dcerpc_dissect_fnct_t *fnct,
-                   int type, char *text, int hf_index, int levels)
+                   int type, char *text, int hf_index, 
+                   dcerpc_callback_fnct_t *callback, void *callback_args)
 {
        dcerpc_info *di;
 
@@ -1156,7 +1539,8 @@ dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        "%s", text);
                tr=proto_item_add_subtree(item,ett_dcerpc_pointer_data);
 
-               add_pointer_to_list(pinfo, tr, fnct, 0xffffffff, hf_index, levels);
+               add_pointer_to_list(pinfo, tr, item, fnct, 0xffffffff, 
+                                   hf_index, callback, callback_args);
                goto after_ref_id;
        }
 
@@ -1193,7 +1577,8 @@ dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        "%s", text);
                tr=proto_item_add_subtree(item,ett_dcerpc_pointer_data);
                proto_tree_add_uint(tr, hf_dcerpc_referent_id, tvb, offset-4, 4, id);
-               add_pointer_to_list(pinfo, tr, fnct, id, hf_index, levels);
+               add_pointer_to_list(pinfo, tr, item, fnct, id, hf_index, 
+                                   callback, callback_args);
                goto after_ref_id;
        }
        /*TOP LEVEL UNIQUE POINTER*/
@@ -1218,7 +1603,8 @@ dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        "%s", text);
                tr=proto_item_add_subtree(item,ett_dcerpc_pointer_data);
                proto_tree_add_uint(tr, hf_dcerpc_referent_id, tvb, offset-4, 4, id);
-               add_pointer_to_list(pinfo, tr, fnct, 0xffffffff, hf_index, levels);
+               add_pointer_to_list(pinfo, tr, item, fnct, 0xffffffff, 
+                                   hf_index, callback, callback_args);
                goto after_ref_id;
        }
 
@@ -1237,7 +1623,8 @@ dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        "%s",text);
                tr=proto_item_add_subtree(item,ett_dcerpc_pointer_data);
                proto_tree_add_uint(tr, hf_dcerpc_referent_id, tvb, offset-4, 4, id);
-               add_pointer_to_list(pinfo, tr, fnct, 0xffffffff, hf_index, levels);
+               add_pointer_to_list(pinfo, tr, item, fnct, 0xffffffff, 
+                                   hf_index, callback, callback_args);
                goto after_ref_id;
        }
 
@@ -1263,7 +1650,8 @@ dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        "%s",text);
                tr=proto_item_add_subtree(item,ett_dcerpc_pointer_data);
                proto_tree_add_uint(tr, hf_dcerpc_referent_id, tvb, offset-4, 4, id);
-               add_pointer_to_list(pinfo, tr, fnct, 0xffffffff, hf_index, levels);
+               add_pointer_to_list(pinfo, tr, item, fnct, 0xffffffff, 
+                                   hf_index, callback, callback_args);
                goto after_ref_id;
        }
 
@@ -1300,7 +1688,8 @@ dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        "%s", text);
                tr=proto_item_add_subtree(item,ett_dcerpc_pointer_data);
                proto_tree_add_uint(tr, hf_dcerpc_referent_id, tvb, offset-4, 4, id);
-               add_pointer_to_list(pinfo, tr, fnct, id, hf_index, levels);
+               add_pointer_to_list(pinfo, tr, item, fnct, id, hf_index, 
+                                   callback, callback_args);
                goto after_ref_id;
        }
 
@@ -1318,24 +1707,69 @@ after_ref_id:
        return offset;
 }
 
+int
+dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo,
+                   proto_tree *tree, char *drep, dcerpc_dissect_fnct_t *fnct,
+                   int type, char *text, int hf_index)
+{
+       return dissect_ndr_pointer_cb(
+               tvb, offset, pinfo, tree, drep, fnct, type, text, hf_index,
+               NULL, NULL);
+}
 
+static void
+show_stub_data (tvbuff_t *tvb, gint offset, proto_tree *dcerpc_tree,
+                dcerpc_auth_info *auth_info, gboolean is_encrypted)
+{
+    int length;
+
+    /*
+     * We don't show stub data unless we have some in the tvbuff;
+     * however, in the protocol tree, we show, as the number of
+     * bytes, the reported number of bytes, not the number of bytes
+     * that happen to be in the tvbuff.
+     */
+    if (tvb_length_remaining (tvb, offset) > 0) {
+        length = tvb_reported_length_remaining (tvb, offset);
+        if (auth_info != NULL &&
+            auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) {
+            if (is_encrypted) {
+                proto_tree_add_text(dcerpc_tree, tvb, offset, -1,
+                                    "Encrypted stub data (%d byte%s)",
+                                    length, plurality(length, "", "s"));
+            } else {
+                proto_tree_add_text(dcerpc_tree, tvb, offset, -1,
+                                    "Decrypted stub data (%d byte%s)",
+                                    length, plurality(length, "", "s"));
+            }
+        } else {
+            proto_tree_add_text (dcerpc_tree, tvb, offset, -1,
+                                 "Stub data (%d byte%s)", length,
+                                 plurality(length, "", "s"));
+        }
+    }
+}
 
 static int
 dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
                     proto_tree *dcerpc_tree,
-                    tvbuff_t *tvb, gint offset,
+                    tvbuff_t *volatile tvb, tvbuff_t *decrypted_tvb,
                     char *drep, dcerpc_info *info,
                     dcerpc_auth_info *auth_info)
 {
+    volatile gint offset = 0;
     dcerpc_uuid_key key;
     dcerpc_uuid_value *sub_proto;
-    int length;
-    proto_tree *sub_tree = NULL;
+    proto_tree *volatile sub_tree = NULL;
     dcerpc_sub_dissector *proc;
     gchar *name = NULL;
     dcerpc_dissect_fnct_t *volatile sub_dissect;
     const char *volatile saved_proto;
     void *volatile saved_private_data;
+    guint length, reported_length;
+    tvbuff_t *volatile stub_tvb;
+    volatile guint auth_pad_len;
+    volatile int auth_pad_offset;
 
     key.uuid = info->call_data->uuid;
     key.ver = info->call_data->ver;
@@ -1343,16 +1777,15 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
 
     if ((sub_proto = g_hash_table_lookup (dcerpc_uuids, &key)) == NULL
          || !proto_is_protocol_enabled(sub_proto->proto)) {
-       /*
-        * We don't have a dissector for this UUID, or the protocol
-        * for that UUID is disabled.
-        */
-        length = tvb_length_remaining (tvb, offset);
-        if (length > 0) {
-            proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                 "Stub data (%d byte%s)", length,
-                                 plurality(length, "", "s"));
-        }
+        /*
+         * We don't have a dissector for this UUID, or the protocol
+         * for that UUID is disabled.
+         */
+        if (decrypted_tvb != NULL) {
+            show_stub_data (decrypted_tvb, 0, dcerpc_tree, auth_info,
+                            FALSE);
+        } else
+            show_stub_data (tvb, 0, dcerpc_tree, auth_info, TRUE);
         return -1;
     }
 
@@ -1377,7 +1810,7 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
 
     if (tree) {
         proto_item *sub_item;
-        sub_item = proto_tree_add_item (tree, sub_proto->proto, tvb, offset,
+        sub_item = proto_tree_add_item (tree, sub_proto->proto_id, tvb, 0,
                                         -1, FALSE);
 
         if (sub_item) {
@@ -1390,98 +1823,129 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
          */
 
        if (sub_proto->opnum_hf != -1)
-               proto_tree_add_uint_format(sub_tree, sub_proto->opnum_hf,
-                                          tvb, 0, 0, info->call_data->opnum,
-                                          "Operation: %s (%u)",
-                                          name, info->call_data->opnum);
+            proto_tree_add_uint_format(sub_tree, sub_proto->opnum_hf,
+                                       tvb, 0, 0, info->call_data->opnum,
+                                       "Operation: %s (%u)",
+                                       name, info->call_data->opnum);
        else
-               proto_tree_add_uint_format(sub_tree, hf_dcerpc_op, tvb,
-                                          0, 0, info->call_data->opnum,
-                                          "Operation: %s (%u)",
-                                          name, info->call_data->opnum);
+            proto_tree_add_uint_format(sub_tree, hf_dcerpc_op, tvb,
+                                       0, 0, info->call_data->opnum,
+                                       "Operation: %s (%u)",
+                                       name, info->call_data->opnum);
     }
 
-    /*
-     * If the authentication level is DCE_C_AUTHN_LEVEL_PKT_PRIVACY,
-     * the stub data is encrypted, and we'd have to decrypt it in
-     * order to dissect it.
-     */
-    if (auth_info != NULL &&
-          auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) {
-        length = tvb_length_remaining (tvb, offset);
-        if (length > 0) {
-            proto_tree_add_text(sub_tree, tvb, offset, length,
-                                "Encrypted stub data (%d byte%s)",
-                                length, plurality(length, "", "s"));
-
-           switch (auth_info->auth_type) {
-
-           case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
-               /* NTLMSSP */
-               tvbuff_t *ntlmssp_tvb;
-               ntlmssp_tvb = tvb_new_subset(tvb, offset, length, length);
-               pinfo->decrypted_data=NULL;
+    sub_dissect = info->request ? proc->dissect_rqst : proc->dissect_resp;
 
-               call_dissector(ntlmssp_enc_payload_handle, ntlmssp_tvb, pinfo,
-                              sub_tree);
-
-               if(pinfo->decrypted_data){
-                       ntlmssp_decrypted_info_t *ndi=pinfo->decrypted_data;
-
-       
-                       sub_dissect = info->request ? proc->dissect_rqst : proc->dissect_resp;
-                       if (sub_dissect) {
-                           saved_proto = pinfo->current_proto;
-                           saved_private_data = pinfo->private_data;
-                           pinfo->current_proto = sub_proto->name;
-                           pinfo->private_data = (void *)info;
-
-                           init_ndr_pointer_list(pinfo);
-
-                            /*
-                             * Catch ReportedBoundsError, as that could
-                             * be due to the decryption being bad,
-                             * and doesn't mean that the tvbuff we were
-                             * handed has a malformed packet.
-                             */
-                            TRY {
-                                offset = sub_dissect (ndi->decr_tvb, 0, pinfo, ndi->decr_tree, drep);
-                            } CATCH(BoundsError) {
-                                RETHROW;
-                            } CATCH(ReportedBoundsError) {
-                                show_reported_bounds_error(tvb, pinfo, tree);
-                            } ENDTRY;
-
-                           pinfo->current_proto = saved_proto;
-                           pinfo->private_data = saved_private_data;
-                       }
-                   }
-               break;
-               }
-           }
-        }
-    } else {
-        sub_dissect = info->request ? proc->dissect_rqst : proc->dissect_resp;
+    if (decrypted_tvb != NULL) {
+        /* Either there was no encryption or we successfully decrypted
+           the entrypted payload. */
         if (sub_dissect) {
+            /* We have a subdissector - call it. */
             saved_proto = pinfo->current_proto;
             saved_private_data = pinfo->private_data;
             pinfo->current_proto = sub_proto->name;
             pinfo->private_data = (void *)info;
-
+           
             init_ndr_pointer_list(pinfo);
-            offset = sub_dissect (tvb, offset, pinfo, sub_tree, drep);
+
+            /*
+             * Remove the authentication padding from the stub data.
+             */
+            if (auth_info != NULL && auth_info->auth_pad_len != 0) {
+                length = tvb_length(decrypted_tvb);
+                reported_length = tvb_reported_length(decrypted_tvb);
+                if (reported_length >= auth_info->auth_pad_len) {
+                    /*
+                     * OK, the padding length isn't so big that it
+                     * exceeds the stub length.  Trim the reported
+                     * length of the tvbuff.
+                     */
+                    reported_length -= auth_info->auth_pad_len;
+
+                    /*
+                     * If that exceeds the actual amount of data in
+                     * the tvbuff (which means we have at least one
+                     * byte of authentication padding in the tvbuff),
+                     * trim the actual amount.
+                     */
+                    if (length > reported_length)
+                        length = reported_length;
+
+                    stub_tvb = tvb_new_subset(tvb, 0, length, reported_length);
+                    auth_pad_len = auth_info->auth_pad_len;
+                    auth_pad_offset = reported_length;
+                } else {
+                    /*
+                     * The padding length exceeds the stub length.
+                     * Don't bother dissecting the stub, trim the padding
+                     * length to what's in the stub data, and show the
+                     * entire stub as authentication padding.
+                     */
+                    stub_tvb = NULL;
+                    auth_pad_len = reported_length;
+                    auth_pad_offset = 0;
+                }
+            } else {
+                /*
+                 * No authentication padding.
+                 */
+                stub_tvb = decrypted_tvb;
+                auth_pad_len = 0;
+                auth_pad_offset = 0;
+            }
+
+            if (stub_tvb != NULL) {
+                /*
+                 * Catch all exceptions other than BoundsError, so that even
+                 * if the stub data is bad, we still show the authentication
+                 * padding, if any.
+                 *
+                 * If we get BoundsError, it means the frame was cut short
+                 * by a snapshot length, so there's nothing more to
+                 * dissect; just re-throw that exception.
+                 */
+                TRY {
+                    offset = sub_dissect (decrypted_tvb, 0, pinfo, sub_tree,
+                                          drep);
+
+                    /* If we have a subdissector and it didn't dissect all
+                       data in the tvb, make a note of it. */
+
+                    if (tvb_reported_length_remaining(stub_tvb, offset) > 0) {
+                        if (check_col(pinfo->cinfo, COL_INFO))
+                            col_append_fstr(pinfo->cinfo, COL_INFO,
+                                            "[Long frame (%d bytes)]",
+                                            tvb_reported_length_remaining(stub_tvb, offset));
+                    }
+                } CATCH(BoundsError) {
+                    RETHROW;
+                } CATCH_ALL {
+                    show_exception(decrypted_tvb, pinfo, tree, EXCEPT_CODE);
+                } ENDTRY;
+            }
+
+            /* If there is auth padding at the end of the stub, display it */
+            if (auth_pad_len != 0) {
+                proto_tree_add_text (sub_tree, decrypted_tvb, auth_pad_offset,
+                                     auth_pad_len,
+                                     "Auth Padding (%u byte%s)",
+                                     auth_pad_len,
+                                     plurality(auth_pad_len, "", "s"));
+            }
 
             pinfo->current_proto = saved_proto;
             pinfo->private_data = saved_private_data;
         } else {
-            length = tvb_length_remaining (tvb, offset);
-            if (length > 0) {
-                proto_tree_add_text (sub_tree, tvb, offset, length,
-                                     "Stub data (%d byte%s)", length,
-                                     plurality(length, "", "s"));
+            /* No subdissector - show it as stub data. */
+            if(decrypted_tvb){
+               show_stub_data (decrypted_tvb, 0, sub_tree, auth_info, FALSE);
+            } else {
+               show_stub_data (tvb, 0, sub_tree, auth_info, TRUE);
             }
         }
-    }
+    } else
+        show_stub_data (tvb, 0, sub_tree, auth_info, TRUE);
+
     tap_queue_packet(dcerpc_tap, pinfo, info);
     return 0;
 }
@@ -1493,148 +1957,133 @@ dissect_dcerpc_verifier (tvbuff_t *tvb, packet_info *pinfo,
 {
     int auth_offset;
 
+    auth_info->auth_data = NULL;
+
     if (auth_info->auth_size != 0) {
-        auth_offset = hdr->frag_len - hdr->auth_len;
-        switch (auth_info->auth_type) {
-    
-        case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
-            /* NTLMSSP */
-            tvbuff_t *ntlmssp_tvb;
-    
-            ntlmssp_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len,
-                                         hdr->auth_len);
-    
-            call_dissector(ntlmssp_verf_handle, ntlmssp_tvb, pinfo,
-                          dcerpc_tree);
-    
-            break;
-            }
-    
-        case DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO: {
-            /* SPNEGO (rfc2478) */
-            tvbuff_t *gssapi_tvb;
-    
-            gssapi_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len,
-                                        hdr->auth_len);
-    
-            call_dissector(gssapi_verf_handle, gssapi_tvb, pinfo, dcerpc_tree);
-    
-            break;
-            }
-    
-        default:
-            proto_tree_add_text (dcerpc_tree, tvb, auth_offset, hdr->auth_len,
-                                 "Auth Verifier");
-        }
+       dcerpc_auth_subdissector_fns *auth_fns;
+       tvbuff_t *auth_tvb;
+
+       auth_offset = hdr->frag_len - hdr->auth_len;
+
+       auth_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len,
+                                 hdr->auth_len);
+
+       auth_info->auth_data = auth_tvb;
+
+       if ((auth_fns = get_auth_subdissector_fns(auth_info->auth_level,
+                                                 auth_info->auth_type))) {
+           /*
+            * Catch all exceptions, so that even if the verifier is bad
+            * or we don't have all of it, we still show the stub data.
+            */
+           TRY {
+               dissect_auth_verf(auth_tvb, pinfo, dcerpc_tree, auth_fns,
+                                 hdr, auth_info);
+           } CATCH_ALL {
+               show_exception(auth_tvb, pinfo, dcerpc_tree, EXCEPT_CODE);
+           } ENDTRY;
+       } else {
+           proto_tree_add_text (dcerpc_tree, auth_tvb, 0, hdr->auth_len,
+                                "Auth Verifier");
+       }
     }
 
     return hdr->auth_len;
 }
 
 static void
-dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tree,
-                        e_dce_cn_common_hdr_t *hdr, gboolean are_credentials, 
-                       dcerpc_auth_info *auth_info)
+dissect_dcerpc_cn_auth (tvbuff_t *tvb, int stub_offset, packet_info *pinfo,
+                        proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr,
+                        gboolean are_credentials, dcerpc_auth_info *auth_info)
 {
-    int offset;
+    volatile int offset;
 
     /*
-     * Initially set auth_level to -1 to indicate that we haven't
-     * yet seen any authentication level information.
+     * Initially set auth_level and auth_type to zero to indicate that we 
+     * haven't yet seen any authentication level information.
      */
-    auth_info->auth_level = -1;
-
+    auth_info->auth_level = 0;
+    auth_info->auth_type = 0;
+    auth_info->auth_size = 0;
+    auth_info->auth_pad_len = 0;
+    
     /*
      * The authentication information is at the *end* of the PDU; in
      * request and response PDUs, the request and response stub data
      * come before it.
      *
-     * If the full packet is here, and we've got an auth len, and it's
-     * valid, then dissect the auth info.
+     * Is there any authentication data (i.e., is the authentication length
+     * non-zero), and is the authentication length valid (i.e., is it, plus
+     * 8 bytes for the type/level/pad length/reserved/context id, less than
+     * or equal to the fragment length minus the starting offset of the
+     * stub data?)
      */
-    if (tvb_length (tvb) >= hdr->frag_len
-        && hdr->auth_len
-        && (hdr->auth_len + 8 <= hdr->frag_len)) {
-
-        offset = hdr->frag_len - (hdr->auth_len + 8);
-
-        offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
-                                       hf_dcerpc_auth_type, 
-                                      &auth_info->auth_type);
-        offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
-                                       hf_dcerpc_auth_level, 
-                                      &auth_info->auth_level);
-
-        offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
-                                       hf_dcerpc_auth_pad_len, 
-                                      &auth_info->auth_pad_len);
-        offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
-                                       hf_dcerpc_auth_rsrvd, NULL);
-        offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
-                                        hf_dcerpc_auth_ctx_id, NULL);
-
-       /*
-        * Dissect the authentication data.
-        */
-       if (are_credentials) {
-           /*
-            * The authentication data are credentials.
-            */
-           switch (auth_info->auth_type) {
-
-           case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
-               /* NTLMSSP */
-               tvbuff_t *ntlmssp_tvb;
-
-               ntlmssp_tvb = tvb_new_subset(tvb, offset, hdr->auth_len,
-                                            hdr->auth_len);
-
-               call_dissector(ntlmssp_handle, ntlmssp_tvb, pinfo,
-                              dcerpc_tree);
-
-               break;
-           }
-
-           case DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO: {
-               /* SPNEGO (rfc2478) */
-               tvbuff_t *gssapi_tvb;
-
-               gssapi_tvb = tvb_new_subset(tvb, offset, hdr->auth_len,
-                                           hdr->auth_len);
-
-               call_dissector(gssapi_handle, gssapi_tvb, pinfo, dcerpc_tree);
-
-               break;
-           }
 
-           default:
-               proto_tree_add_text (dcerpc_tree, tvb, offset, hdr->auth_len,
-                                    "Auth Credentials");
-           }
-       }
+    if (hdr->auth_len
+        && (hdr->auth_len + 8 <= hdr->frag_len - stub_offset)) {
 
-       /*
-        * XXX - sometimes the padding is a multiple of 4 and greater
-        * than 3, meaning it's not padding to put the authentication
-        * data on a 4-byte boundary.
-        *
-        * For now, we take its value mod 4.
-        *
-        * XXX - what is going on there?
-        */
-       auth_info->auth_pad_len %= 4;
-
-        /* figure out where the auth padding starts */
-        offset = hdr->frag_len - (hdr->auth_len + 8 + auth_info->auth_pad_len);
-        if (offset > 0 && auth_info->auth_pad_len) {
-            proto_tree_add_text (dcerpc_tree, tvb, offset,
-                                 auth_info->auth_pad_len, "Auth padding");
-           auth_info->auth_size = hdr->auth_len + 8 + auth_info->auth_pad_len;
-        } else {
-           auth_info->auth_size = hdr->auth_len + 8;
+        /*
+         * Yes, there is authentication data, and the length is valid.
+         * Do we have all the bytes of stub data?
+         * (If not, we'd throw an exception dissecting *that*, so don't
+         * bother trying to dissect the authentication information and
+         * throwing another exception there.)
+         */
+        offset = hdr->frag_len - (hdr->auth_len + 8);
+        if (offset == 0 || tvb_offset_exists(tvb, offset - 1)) {
+            /*
+             * Either there's no stub data, or the last byte of the stub
+             * data is present in the captured data, so we shouldn't
+             * get a BoundsError dissecting the stub data.
+             *
+             * Try dissecting the authentication data.
+             * Catch all exceptions, so that even if the auth info is bad
+             * or we don't have all of it, we still show the stuff we
+             * dissect after this, such as stub data.
+             */
+            TRY {
+                offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+                                               hf_dcerpc_auth_type, 
+                                               &auth_info->auth_type);
+                offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+                                               hf_dcerpc_auth_level, 
+                                               &auth_info->auth_level);
+
+                offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+                                               hf_dcerpc_auth_pad_len, 
+                                               &auth_info->auth_pad_len);
+                offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+                                               hf_dcerpc_auth_rsrvd, NULL);
+                offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+                                                hf_dcerpc_auth_ctx_id, NULL);
+
+                /*
+                 * Dissect the authentication data.
+                 */
+                if (are_credentials) {
+                    tvbuff_t *auth_tvb;
+                    dcerpc_auth_subdissector_fns *auth_fns;
+
+                    auth_tvb = tvb_new_subset(tvb, offset, hdr->auth_len,
+                                              hdr->auth_len);
+
+                    if ((auth_fns = get_auth_subdissector_fns(auth_info->auth_level,
+                                                              auth_info->auth_type)))
+                        dissect_auth_verf(auth_tvb, pinfo, dcerpc_tree, auth_fns, 
+                                          hdr, auth_info);
+                    else
+                        proto_tree_add_text (dcerpc_tree, tvb, offset, hdr->auth_len,
+                                             "Auth Credentials");
+                }
+       
+                /* Compute the size of the auth block.  Note that this should not 
+                   include auth padding, since when NTLMSSP encryption is used, the
+                   padding is actually inside the encrypted stub */
+                   auth_info->auth_size = hdr->auth_len + 8;
+            } CATCH_ALL {
+                show_exception(tvb, pinfo, dcerpc_tree, EXCEPT_CODE);
+            } ENDTRY;
         }
-    } else {
-        auth_info->auth_size = 0;
     }
 }
 
@@ -1669,7 +2118,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr)
 {
     conversation_t *conv = NULL;
-    guint8 num_ctx_items;
+    guint8 num_ctx_items = 0;
     guint i;
     gboolean saw_ctx_item = FALSE;
     guint16 ctx_id;
@@ -1699,15 +2148,29 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
     offset += 3;
 
     for (i = 0; i < num_ctx_items; i++) {
-      offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+           proto_tree *ctx_tree = NULL, *iface_tree = NULL;
+
+      offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, NULL, hdr->drep,
                                       hf_dcerpc_cn_ctx_id, &ctx_id);
 
-      offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+      if (dcerpc_tree) {
+             proto_item *ctx_item;
+
+             ctx_item = proto_tree_add_item(dcerpc_tree, hf_dcerpc_cn_ctx_id,
+                                            tvb, offset - 2, 2, 
+                                            hdr->drep[0] & 0x10);
+
+             ctx_tree = proto_item_add_subtree(ctx_item, ett_dcerpc_cn_ctx);
+      }
+
+      offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, ctx_tree, hdr->drep,
                                       hf_dcerpc_cn_num_trans_items, &num_trans_items);
 
       /* XXX - use "dissect_ndr_uuid_t()"? */
       dcerpc_tvb_get_uuid (tvb, offset, hdr->drep, &if_id);
-      if (dcerpc_tree) {
+      if (ctx_tree) {
+         proto_item *iface_item;
+
          uuid_str_len = snprintf(uuid_str, DCERPC_UUID_STR_LEN, 
                                  "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                                   if_id.Data1, if_id.Data2, if_id.Data3,
@@ -1715,22 +2178,25 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                                   if_id.Data4[2], if_id.Data4[3],
                                   if_id.Data4[4], if_id.Data4[5],
                                   if_id.Data4[6], if_id.Data4[7]);
+
          if (uuid_str_len >= DCERPC_UUID_STR_LEN)
                  memset(uuid_str, 0, DCERPC_UUID_STR_LEN);
-          proto_tree_add_string_format (dcerpc_tree, hf_dcerpc_cn_bind_if_id, tvb,
+
+          iface_item = proto_tree_add_string_format (ctx_tree, hf_dcerpc_cn_bind_if_id, tvb,
                                         offset, 16, uuid_str, "Interface UUID: %s", uuid_str);
+         iface_tree = proto_item_add_subtree(iface_item, ett_dcerpc_cn_iface);
       }
       offset += 16;
 
       if (hdr->drep[0] & 0x10) {
-          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, iface_tree, hdr->drep,
                                           hf_dcerpc_cn_bind_if_ver, &if_ver);
-          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, iface_tree, hdr->drep,
                                           hf_dcerpc_cn_bind_if_ver_minor, &if_ver_minor);
       } else {
-          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, iface_tree, hdr->drep,
                                           hf_dcerpc_cn_bind_if_ver_minor, &if_ver_minor);
-          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+          offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, iface_tree, hdr->drep,
                                           hf_dcerpc_cn_bind_if_ver, &if_ver);
       }
 
@@ -1776,6 +2242,9 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
          key.uuid = if_id;
          key.ver = if_ver;
 
+         if (num_ctx_items > 1)
+                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %u context items, 1st", num_ctx_items);
+               
          if ((value = g_hash_table_lookup(dcerpc_uuids, &key)))
                  col_append_fstr(pinfo->cinfo, COL_INFO, " UUID: %s", value->name);
          else
@@ -1793,7 +2262,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
       for (j = 0; j < num_trans_items; j++) {
         /* XXX - use "dissect_ndr_uuid_t()"? */
         dcerpc_tvb_get_uuid (tvb, offset, hdr->drep, &trans_id);
-        if (dcerpc_tree) {
+        if (iface_tree) {
            uuid_str_len = snprintf(uuid_str, DCERPC_UUID_STR_LEN, 
                                   "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                                   trans_id.Data1, trans_id.Data2, trans_id.Data3,
@@ -1803,12 +2272,12 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                                   trans_id.Data4[6], trans_id.Data4[7]);
             if (uuid_str_len >= DCERPC_UUID_STR_LEN)
                 memset(uuid_str, 0, DCERPC_UUID_STR_LEN);
-            proto_tree_add_string_format (dcerpc_tree, hf_dcerpc_cn_bind_trans_id, tvb,
+            proto_tree_add_string_format (iface_tree, hf_dcerpc_cn_bind_trans_id, tvb,
                                           offset, 16, uuid_str, "Transfer Syntax: %s", uuid_str);
         }
         offset += 16;
 
-        offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
+        offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, iface_tree, hdr->drep,
                                         hf_dcerpc_cn_bind_trans_ver, &trans_ver);
       }
     }
@@ -1818,7 +2287,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
      * an authentication header, and associate it with an authentication
      * context, so subsequent PDUs can use that context.
      */
-    dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, TRUE, &auth_info);
+    dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, hdr, TRUE, &auth_info);
 }
 
 static void
@@ -1905,7 +2374,7 @@ dissect_dcerpc_cn_bind_ack (tvbuff_t *tvb, gint offset, packet_info *pinfo,
      * XXX - do we need to do anything with the authentication level
      * we get back from this?
      */
-    dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, TRUE, &auth_info);
+    dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, hdr, TRUE, &auth_info);
 
     if (check_col (pinfo->cinfo, COL_INFO)) {
         if (num_results != 0 && result == 0) {
@@ -1957,6 +2426,33 @@ dissect_dcerpc_cn_bind_nak (tvbuff_t *tvb, gint offset, packet_info *pinfo,
     }
 }
 
+/* Return a string describing a DCE/RPC fragment as first, middle, or end
+   fragment. */
+
+#define PFC_FRAG_MASK  0x03
+
+static char *
+fragment_type(guint8 flags)
+{
+       flags = flags & PFC_FRAG_MASK;
+
+       if (flags == PFC_FIRST_FRAG)
+               return "first";
+
+       if (flags == 0)
+               return "middle";
+
+       if (flags == PFC_LAST_FRAG)
+               return "last";
+
+       if (flags == (PFC_FIRST_FRAG | PFC_LAST_FRAG))
+               return "whole";
+
+       return "unknown";
+}
+
+/* Dissect stub data (payload) of a DCERPC packet. */
+
 static void
 dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
                         proto_tree *dcerpc_tree, proto_tree *tree,
@@ -1964,126 +2460,206 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
                         dcerpc_auth_info *auth_info, guint32 alloc_hint,
                         guint32 frame)
 {
-    int length, reported_length, stub_length;
     gboolean save_fragmented;
-
-    length = tvb_length_remaining(tvb, offset);
-    reported_length = tvb_reported_length_remaining(tvb, offset);
-    stub_length = hdr->frag_len - offset - auth_info->auth_size;
-    if (length > stub_length)
-      length = stub_length;
-    if (reported_length > stub_length)
-      reported_length = stub_length;
+    fragment_data *fd_head=NULL;
+    guint32 tot_len;
+    tvbuff_t *payload_tvb, *decrypted_tvb;
 
     save_fragmented = pinfo->fragmented;
 
-    /* If we don't have reassembly enabled, or this packet contains
-       the entire PDU, or if this is a short frame (or a frame
-       not reassembled at a lower layer) that doesn't include all
-       the data in the fragment, just call the handoff directly if
-       this is the first fragment or the PDU isn't fragmented. */
-    if( (!dcerpc_reassemble) || PFC_NOT_FRAGMENTED(hdr) ||
-               stub_length > length ){
-       if(hdr->flags&PFC_FIRST_FRAG){
-           /* First fragment, possibly the only fragment */
-            pinfo->fragmented = !PFC_NOT_FRAGMENTED(hdr);
-            dcerpc_try_handoff (pinfo, tree, dcerpc_tree,
-                                tvb_new_subset (tvb, offset, length,
-                                                reported_length),
-                                0, hdr->drep, di, auth_info);
+    payload_tvb = tvb_new_subset(
+           tvb, offset, tvb_length_remaining(tvb, offset) - 
+           auth_info->auth_size, tvb_length_remaining(tvb, offset) - 
+           auth_info->auth_size);    
+
+    /* Decrypt the PDU if it is encrypted */
+
+    if (auth_info->auth_type &&
+        auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) {
+           /*
+            * We know the authentication type, and the authentication
+            * level is "Packet privacy", meaning the payload is
+            * encrypted; attempt to decrypt it.
+            */
+           dcerpc_auth_subdissector_fns *auth_fns;
+           
+           /* Start out assuming we won't succeed in decrypting. */
+           decrypted_tvb = NULL;
+
+           if ((auth_fns = get_auth_subdissector_fns(
+                        auth_info->auth_level, auth_info->auth_type))) {
+                   tvbuff_t *result;
+                   
+                   result = decode_encrypted_data(
+                           payload_tvb, pinfo, auth_fns,
+                           hdr->ptype == PDU_REQ, auth_info);      
+                   
+                   if (result) {
+                           if (dcerpc_tree)
+                               proto_tree_add_text(
+                                           dcerpc_tree, payload_tvb, 0, -1,
+                                           "Encrypted stub data (%d byte%s)",
+                                           tvb_reported_length(payload_tvb),
+
+                           plurality(tvb_length(payload_tvb), "", "s"));
+
+                           add_new_data_source(
+                                   pinfo, result, "Decrypted stub data");
+                           
+                           /* We succeeded. */
+                           decrypted_tvb = result;
+                   }
+           }
+    } else
+           decrypted_tvb = payload_tvb;
+
+    /* if this packet is not fragmented, just dissect it and exit */
+    if(PFC_NOT_FRAGMENTED(hdr)){
+       pinfo->fragmented = FALSE;
+
+       dcerpc_try_handoff(
+               pinfo, tree, dcerpc_tree, payload_tvb, decrypted_tvb,
+               hdr->drep, di, auth_info);
+       
+       pinfo->fragmented = save_fragmented;
+       return;
+    }
+
+    /* The packet is fragmented. */
+    pinfo->fragmented = TRUE;
+
+    /* if we are not doing reassembly and this is the first fragment
+       then just dissect it and exit
+       XXX - if we're not doing reassembly, can we decrypt an
+       encrypted stub?
+    */
+    if( (!dcerpc_reassemble) && hdr->flags&PFC_FIRST_FRAG ){
+
+       dcerpc_try_handoff(
+               pinfo, tree, dcerpc_tree, payload_tvb, decrypted_tvb,
+               hdr->drep, di, auth_info);
+       
+        if (check_col(pinfo->cinfo, COL_INFO)) {
+            col_append_fstr(pinfo->cinfo, COL_INFO,
+                            " [DCE/RPC %s fragment]", fragment_type(hdr->flags));
+        }
+        pinfo->fragmented = save_fragmented;
+        return;
+    }
+
+    /* if we have already seen this packet, see if it was reassembled
+       and if so dissect the full pdu.
+       then exit 
+    */
+    if(pinfo->fd->flags.visited){
+       fd_head=fragment_get(pinfo, frame, dcerpc_co_reassemble_table);
+       goto end_cn_stub;
+    }
+
+    /* if we are not doing reassembly and it was neither a complete PDU
+       nor the first fragment then there is nothing more we can do
+       so we just have to exit
+    */
+    if( !dcerpc_reassemble )
+        goto end_cn_stub;
+
+    /* if we didnt get 'frame' we dont know where the PDU started and thus
+       it is pointless to continue 
+    */
+    if(!frame)
+        goto end_cn_stub;
+
+    /* from now on we must attempt to reassemble the PDU 
+    */
+
+    /* if we get here we know it is the first time we see the packet
+       and we also know it is only a fragment and not a full PDU,
+       thus we must reassemble it.
+    */
+
+    /* Do we have any non-encrypted data to reassemble? */
+    if (decrypted_tvb == NULL) {
+      /* No.  We can't even try to reassemble.  */
+      goto end_cn_stub;
+    }
+
+    /* if this is the first fragment we need to start reassembly
+    */
+    if(hdr->flags&PFC_FIRST_FRAG){
+       fragment_add(decrypted_tvb, 0, pinfo, frame, dcerpc_co_reassemble_table,
+                    0, tvb_length(decrypted_tvb), TRUE);
+       fragment_set_tot_len(pinfo, frame,
+                dcerpc_co_reassemble_table, alloc_hint);
+
+       goto end_cn_stub;
+    }
+
+    /* if this is a middle fragment, just add it and exit */
+    if(!(hdr->flags&PFC_LAST_FRAG)){
+       tot_len = fragment_get_tot_len(pinfo, frame,
+                dcerpc_co_reassemble_table);
+       fragment_add(decrypted_tvb, 0, pinfo, frame,
+                dcerpc_co_reassemble_table,
+                tot_len-alloc_hint, tvb_length(decrypted_tvb),
+                TRUE);
+
+       goto end_cn_stub;
+    }
+
+    /* this was the last fragment add it to reassembly
+    */
+    tot_len = fragment_get_tot_len(pinfo, frame,
+               dcerpc_co_reassemble_table);
+    fd_head = fragment_add(decrypted_tvb, 0, pinfo,
+               frame,
+               dcerpc_co_reassemble_table,
+               tot_len-alloc_hint, tvb_length(decrypted_tvb),
+               TRUE);
+
+end_cn_stub:
+
+    /* if reassembly is complete, dissect the full PDU
+    */
+    if(fd_head && (fd_head->flags&FD_DEFRAGMENTED) ){
+
+       if(pinfo->fd->num==fd_head->reassembled_in){
+           tvbuff_t *next_tvb;
+
+           next_tvb = tvb_new_real_data(fd_head->data, fd_head->datalen, fd_head->datalen);
+           tvb_set_child_real_data_tvbuff(decrypted_tvb, next_tvb);
+           add_new_data_source(pinfo, next_tvb, "Reassembled DCE/RPC");
+           show_fragment_tree(fd_head, &dcerpc_frag_items,
+               dcerpc_tree, pinfo, next_tvb);
+
+           pinfo->fragmented = FALSE;
+
+           dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb,
+               next_tvb, hdr->drep, di, auth_info);
+
        } else {
-           /* PDU is fragmented and this isn't the first fragment */
+           proto_tree_add_uint(dcerpc_tree, hf_dcerpc_reassembled_in,
+                               decrypted_tvb, 0, 0, fd_head->reassembled_in);
            if (check_col(pinfo->cinfo, COL_INFO)) {
                col_append_fstr(pinfo->cinfo, COL_INFO,
-                               " [DCE/RPC fragment]");
-           }
-           if (dcerpc_tree) {
-               if (length > 0) {
-                   proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                               "Fragment data (%d byte%s)", length,
-                               plurality(length, "", "s"));
-               }
+                       " [DCE/RPC %s fragment]", fragment_type(hdr->flags));
            }
        }
     } else {
-       /* Reassembly is enabled, the PDU is fragmented, and
-          we have all the data in the fragment; the first two
-          of those mean we should attempt reassembly, and the
-          third means we can attempt reassembly. */
-       if (dcerpc_tree) {
-           if (length > 0) {
-               proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                               "Fragment data (%d byte%s)", length,
-                               plurality(length, "", "s"));
-           }
+       /* Reassembly not complete - some fragments
+          are missing.  Just show the stub data. */
+
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+           col_append_fstr(pinfo->cinfo, COL_INFO,
+                       " [DCE/RPC %s fragment]", fragment_type(hdr->flags));
        }
-       if(hdr->flags&PFC_FIRST_FRAG){  /* FIRST fragment */
-           if( (!pinfo->fd->flags.visited) && frame){
-               fragment_add(tvb, offset, pinfo, frame,
-                            dcerpc_co_reassemble_table,
-                            0,
-                            length,
-                            TRUE);
-               fragment_set_tot_len(pinfo, frame,
-                            dcerpc_co_reassemble_table, alloc_hint);
-           }
-           if (check_col(pinfo->cinfo, COL_INFO)) {
-               col_append_fstr(pinfo->cinfo, COL_INFO,
-                               " [DCE/RPC fragment]");
-           }
-       } else if(hdr->flags&PFC_LAST_FRAG){  /* LAST fragment */
-           if( frame ){
-               fragment_data *fd_head;
-               guint32 tot_len;
-
-               tot_len = fragment_get_tot_len(pinfo, frame,
-                              dcerpc_co_reassemble_table);
-               fd_head = fragment_add(tvb, offset, pinfo,
-                    frame,
-                    dcerpc_co_reassemble_table,
-                    tot_len-alloc_hint,
-                    length,
-                    TRUE);
-
-               if(fd_head){
-                   /* We completed reassembly */
-                   tvbuff_t *next_tvb;
-
-                   next_tvb = tvb_new_real_data(fd_head->data, fd_head->datalen, fd_head->datalen);
-                   tvb_set_child_real_data_tvbuff(tvb, next_tvb);
-                   add_new_data_source(pinfo, next_tvb, "Reassembled DCE/RPC");
-                   show_fragment_tree(fd_head, &dcerpc_frag_items,
-                       dcerpc_tree, pinfo, next_tvb);
-
-                   pinfo->fragmented = FALSE;
-                   dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb,
-                                0, hdr->drep, di, auth_info);
-               } else {
-                   /* Reassembly not complete - some fragments
-                      are missing */
-                   if (check_col(pinfo->cinfo, COL_INFO)) {
-                       col_append_fstr(pinfo->cinfo, COL_INFO,
-                                   " [DCE/RPC fragment]");
-                   }
-               }
-           }
-       } else {  /* MIDDLE fragment(s) */
-           if( (!pinfo->fd->flags.visited) && frame ){
-               guint32 tot_len;
-               tot_len = fragment_get_tot_len(pinfo, frame,
-                                      dcerpc_co_reassemble_table);
-               fragment_add(tvb, offset, pinfo, frame,
-                            dcerpc_co_reassemble_table,
-                            tot_len-alloc_hint,
-                            length,
-                            TRUE);
-           }
-           if (check_col(pinfo->cinfo, COL_INFO)) {
-               col_append_fstr(pinfo->cinfo, COL_INFO,
-                               " [DCE/RPC fragment]");
-           }
+
+       if(decrypted_tvb){
+               show_stub_data (decrypted_tvb, 0, tree, auth_info, FALSE);
+       } else {
+               show_stub_data (payload_tvb, 0, tree, auth_info, TRUE);
        }
     }
+
     pinfo->fragmented = save_fragmented;
 }
 
@@ -2097,7 +2673,6 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
     e_uuid_t obj_id;
     dcerpc_auth_info auth_info;
     guint32 alloc_hint;
-    int length;
     char uuid_str[DCERPC_UUID_STR_LEN]; 
     int uuid_str_len;
 
@@ -2142,18 +2717,15 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
      * XXX - what if this was set when the connection was set up,
      * and we just have a security context?
      */
-    dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
+    dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
+    dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info);
 
     conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype,
                               pinfo->srcport, pinfo->destport, 0);
-    if (!conv) {
-        length = tvb_length_remaining (tvb, offset);
-        if (length > 0) {
-            proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                 "Stub data (%d byte%s)", length,
-                                 plurality(length, "", "s"));
-        }
-    } else {
+    if (!conv)
+        show_stub_data (tvb, offset, dcerpc_tree, &auth_info, TRUE);
+    else {
+        dcerpc_matched_key matched_key, *new_matched_key;
         dcerpc_call_value *value;
 
        /* !!! we can NOT check flags.visited here since this will interact
@@ -2161,7 +2733,10 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
           and desegmented pdu's .
           Instead we check if this pdu is already in the matched table or not
        */
-       if(!g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num)){
+       matched_key.frame = pinfo->fd->num;
+       matched_key.call_id = hdr->call_id;
+       value = g_hash_table_lookup(dcerpc_matched, &matched_key);
+       if(!value){
                dcerpc_bind_key bind_key;
                dcerpc_bind_value *bind_value;
 
@@ -2169,51 +2744,71 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                bind_key.ctx_id=ctx_id;
                bind_key.smb_fid=get_smb_fid(pinfo->private_data);
 
-               if((bind_value=g_hash_table_lookup(dcerpc_binds, &bind_key))){
-                       dcerpc_call_key *call_key;
-                       dcerpc_call_value *call_value;
-
-                       /* We found the binding so just add the call
-                          to both the call table and the matched table
-                       */
-                       call_key=g_mem_chunk_alloc (dcerpc_call_key_chunk);
-                       call_key->conv=conv;
-                       call_key->call_id=hdr->call_id;
-                       call_key->smb_fid=get_smb_fid(pinfo->private_data);
-
-                       /* if there is already a matching call in the table
-                          remove it so it is replaced with the new one */
-                       if(g_hash_table_lookup(dcerpc_calls, call_key)){
-                               g_hash_table_remove(dcerpc_calls, call_key);
-                       }
+               if((bind_value=g_hash_table_lookup(dcerpc_binds, &bind_key)) ){
+                       if(!(hdr->flags&PFC_FIRST_FRAG)){
+                               dcerpc_call_key call_key;
+                               dcerpc_call_value *call_value;
+
+                               call_key.conv=conv;
+                               call_key.call_id=hdr->call_id;
+                               call_key.smb_fid=get_smb_fid(pinfo->private_data);
+                               if((call_value=g_hash_table_lookup(dcerpc_calls, &call_key))){
+                                       new_matched_key = g_mem_chunk_alloc(dcerpc_matched_key_chunk);
+                                       *new_matched_key = matched_key;
+                                       g_hash_table_insert (dcerpc_matched, new_matched_key, call_value);
+                                       value = call_value;
+                               }
+                       } else {
+                               dcerpc_call_key *call_key;
+                               dcerpc_call_value *call_value;
+
+                               /* We found the binding and it is the first fragment 
+                                  (or a complete PDU) of a dcerpc pdu so just add 
+                                  the call to both the call table and the 
+                                  matched table
+                               */
+                               call_key=g_mem_chunk_alloc (dcerpc_call_key_chunk);
+                               call_key->conv=conv;
+                               call_key->call_id=hdr->call_id;
+                               call_key->smb_fid=get_smb_fid(pinfo->private_data);
+
+                               /* if there is already a matching call in the table
+                                  remove it so it is replaced with the new one */
+                               if(g_hash_table_lookup(dcerpc_calls, call_key)){
+                                       g_hash_table_remove(dcerpc_calls, call_key);
+                               }
 
-                       call_value=g_mem_chunk_alloc (dcerpc_call_value_chunk);
-                       call_value->uuid = bind_value->uuid;
-                       call_value->ver = bind_value->ver;
-                       call_value->opnum = opnum;
-                       call_value->req_frame=pinfo->fd->num;
-                       call_value->req_time.secs=pinfo->fd->abs_secs;
-                       call_value->req_time.nsecs=pinfo->fd->abs_usecs*1000;
-                       call_value->rep_frame=0;
-                       call_value->max_ptr=0;
-                       call_value->private_data = NULL;
-                       g_hash_table_insert (dcerpc_calls, call_key, call_value);
-
-                       g_hash_table_insert (dcerpc_matched, (void *)pinfo->fd->num, call_value);
+                               call_value=g_mem_chunk_alloc (dcerpc_call_value_chunk);
+                               call_value->uuid = bind_value->uuid;
+                               call_value->ver = bind_value->ver;
+                               call_value->opnum = opnum;
+                               call_value->req_frame=pinfo->fd->num;
+                               call_value->req_time.secs=pinfo->fd->abs_secs;
+                               call_value->req_time.nsecs=pinfo->fd->abs_usecs*1000;
+                               call_value->rep_frame=0;
+                               call_value->max_ptr=0;
+                               call_value->private_data = NULL;
+                               g_hash_table_insert (dcerpc_calls, call_key, call_value);
+
+                               new_matched_key = g_mem_chunk_alloc(dcerpc_matched_key_chunk);
+                               *new_matched_key = matched_key;
+                               g_hash_table_insert (dcerpc_matched, new_matched_key, call_value);
+                               value = call_value;
+                       }
                }
        }
 
-       value=g_hash_table_lookup (dcerpc_matched, (void *)pinfo->fd->num);
-
         if (value) {
-            dcerpc_info di;
+            dcerpc_info *di;
 
+            di=get_next_di();
             /* handoff this call */
-           di.conv = conv;
-           di.call_id = hdr->call_id;
-           di.smb_fid = get_smb_fid(pinfo->private_data);
-           di.request = TRUE;
-           di.call_data = value;
+           di->conv = conv;
+           di->call_id = hdr->call_id;
+           di->smb_fid = get_smb_fid(pinfo->private_data);
+           di->request = TRUE;
+           di->call_data = value;
+               di->hf_index = -1;
 
            if(value->rep_frame!=0){
                proto_tree_add_uint(dcerpc_tree, hf_dcerpc_response_in,
@@ -2221,20 +2816,11 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
            }
 
            dissect_dcerpc_cn_stub (tvb, offset, pinfo, dcerpc_tree, tree,
-                                   hdr, &di, &auth_info, alloc_hint,
+                                   hdr, di, &auth_info, alloc_hint,
                                    value->req_frame);
-       } else {
-            length = tvb_length_remaining (tvb, offset);
-            if (length > 0) {
-                proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                     "Stub data (%d byte%s)", length,
-                                     plurality(length, "", "s"));
-            }
-       }
+       } else
+           show_stub_data (tvb, offset, dcerpc_tree, &auth_info, TRUE);
     }
-
-    /* Decrypt the verifier, if present */
-    dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info);
 }
 
 static void
@@ -2246,7 +2832,6 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
     guint16 ctx_id;
     dcerpc_auth_info auth_info;
     guint32 alloc_hint;
-    int length;
 
     offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
                                     hf_dcerpc_cn_alloc_hint, &alloc_hint);
@@ -2267,26 +2852,27 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
      * XXX - what if this was set when the connection was set up,
      * and we just have a security context?
      */
-    dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
+    dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
+    dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info);
 
     conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype,
                               pinfo->srcport, pinfo->destport, 0);
+
     if (!conv) {
         /* no point in creating one here, really */
-        length = tvb_length_remaining (tvb, offset);
-        if (length > 0) {
-            proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                 "Stub data (%d byte%s)", length,
-                                 plurality(length, "", "s"));
-        }
+        show_stub_data (tvb, offset, dcerpc_tree, &auth_info, TRUE);
     } else {
+       dcerpc_matched_key matched_key, *new_matched_key;
 
        /* !!! we can NOT check flags.visited here since this will interact
           badly with when SMB handles (i.e. calls the subdissector)
           and desegmented pdu's .
           Instead we check if this pdu is already in the matched table or not
        */
-       if(!g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num)){
+       matched_key.frame = pinfo->fd->num;
+       matched_key.call_id = hdr->call_id;
+       value=g_hash_table_lookup(dcerpc_matched, &matched_key);
+       if(!value){
                dcerpc_call_key call_key;
                dcerpc_call_value *call_value;
 
@@ -2295,25 +2881,26 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                call_key.smb_fid=get_smb_fid(pinfo->private_data);
 
                if((call_value=g_hash_table_lookup(dcerpc_calls, &call_key))){
-                       g_hash_table_insert (dcerpc_matched, (void *)pinfo->fd->num, call_value);
+                       new_matched_key = g_mem_chunk_alloc(dcerpc_matched_key_chunk);
+                       *new_matched_key = matched_key;
+                       g_hash_table_insert (dcerpc_matched, new_matched_key, call_value);
+                       value = call_value;
                        if(call_value->rep_frame==0){
                                call_value->rep_frame=pinfo->fd->num;
                        }
-
                }
        }
 
-       value=g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num);
-
         if (value) {
-            dcerpc_info di;
+            dcerpc_info *di;
 
+            di=get_next_di();
             /* handoff this call */
-           di.conv = conv;
-           di.call_id = hdr->call_id;
-           di.smb_fid = get_smb_fid(pinfo->private_data);
-           di.request = FALSE;
-           di.call_data = value;
+           di->conv = conv;
+           di->call_id = hdr->call_id;
+           di->smb_fid = get_smb_fid(pinfo->private_data);
+           di->request = FALSE;
+           di->call_data = value;
 
            proto_tree_add_uint (dcerpc_tree, hf_dcerpc_opnum, tvb, 0, 0, value->opnum);
            if(value->req_frame!=0){
@@ -2330,20 +2917,11 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
            }
 
            dissect_dcerpc_cn_stub (tvb, offset, pinfo, dcerpc_tree, tree,
-                                   hdr, &di, &auth_info, alloc_hint,
+                                   hdr, di, &auth_info, alloc_hint,
                                    value->rep_frame);
-        } else {
-            length = tvb_length_remaining (tvb, offset);
-            if (length > 0) {
-                proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                     "Stub data (%d byte%s)", length,
-                                     plurality(length, "", "s"));
-            }
-       }
+        } else
+            show_stub_data (tvb, offset, dcerpc_tree, &auth_info, TRUE);
     }
-
-    /* Decrypt the verifier, if present */
-    dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info);
 }
 
 static void
@@ -2385,20 +2963,24 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
      * XXX - what if this was set when the connection was set up,
      * and we just have a security context?
      */
-    dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
+    dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
 
     conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype,
                               pinfo->srcport, pinfo->destport, 0);
     if (!conv) {
         /* no point in creating one here, really */
     } else {
+       dcerpc_matched_key matched_key, *new_matched_key;
 
        /* !!! we can NOT check flags.visited here since this will interact
           badly with when SMB handles (i.e. calls the subdissector)
           and desegmented pdu's .
           Instead we check if this pdu is already in the matched table or not
        */
-       if(!g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num)){
+       matched_key.frame = pinfo->fd->num;
+       matched_key.call_id = hdr->call_id;
+       value=g_hash_table_lookup(dcerpc_matched, &matched_key);
+       if(!value){
                dcerpc_call_key call_key;
                dcerpc_call_value *call_value;
 
@@ -2407,7 +2989,10 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                call_key.smb_fid=get_smb_fid(pinfo->private_data);
 
                if((call_value=g_hash_table_lookup(dcerpc_calls, &call_key))){
-                       g_hash_table_insert (dcerpc_matched, (void *)pinfo->fd->num, call_value);
+                       new_matched_key = g_mem_chunk_alloc(dcerpc_matched_key_chunk);
+                       *new_matched_key = matched_key;
+                       g_hash_table_insert (dcerpc_matched, new_matched_key, call_value);
+                       value = call_value;
                        if(call_value->rep_frame==0){
                                call_value->rep_frame=pinfo->fd->num;
                        }
@@ -2415,18 +3000,17 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                }
        }
 
-       value=g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num);
-
         if (value) {
             int length, reported_length, stub_length;
-            dcerpc_info di;
+            dcerpc_info *di;
 
+            di=get_next_di();
             /* handoff this call */
-           di.conv = conv;
-           di.call_id = hdr->call_id;
-           di.smb_fid = get_smb_fid(pinfo->private_data);
-           di.request = FALSE;
-           di.call_data = value;
+           di->conv = conv;
+           di->call_id = hdr->call_id;
+           di->smb_fid = get_smb_fid(pinfo->private_data);
+           di->request = FALSE;
+           di->call_data = value;
 
            proto_tree_add_uint (dcerpc_tree, hf_dcerpc_opnum, tvb, 0, 0, value->opnum);
            if(value->req_frame!=0){
@@ -2451,12 +3035,11 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
              reported_length = stub_length;
 
            /* If we don't have reassembly enabled, or this packet contains
-              the entire PDU, or if this is a short frame (or a frame
-              not reassembled at a lower layer) that doesn't include all
-              the data in the fragment, just call the handoff directly if
-              this is the first fragment or the PDU isn't fragmented. */
+              the entire PDU, or if we don't have all the data in this
+              fragment, just call the handoff directly if this is the
+              first fragment or the PDU isn't fragmented. */
            if( (!dcerpc_reassemble) || PFC_NOT_FRAGMENTED(hdr) ||
-                       stub_length > length ){
+                       !tvb_bytes_exist(tvb, offset, stub_length) ){
                if(hdr->flags&PFC_FIRST_FRAG){
                    /* First fragment, possibly the only fragment */
                    /*
@@ -2470,10 +3053,11 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                     * as well, as that might be protocol-specific.
                     */
                    if (dcerpc_tree) {
-                       if (length > 0) {
-                           proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                       "Fault stub data (%d byte%s)", length,
-                                       plurality(length, "", "s"));
+                       if (stub_length > 0) {
+                           proto_tree_add_text (dcerpc_tree, tvb, offset, stub_length,
+                                                "Fault stub data (%d byte%s)",
+                                                stub_length,
+                                                plurality(stub_length, "", "s"));
                        }
                    }
                } else {
@@ -2483,10 +3067,11 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                                        " [DCE/RPC fragment]");
                    }
                    if (dcerpc_tree) {
-                       if (length > 0) {
-                           proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                       "Fragment data (%d byte%s)", length,
-                                       plurality(length, "", "s"));
+                       if (stub_length > 0) {
+                           proto_tree_add_text (dcerpc_tree, tvb, offset, stub_length,
+                                                "Fragment data (%d byte%s)",
+                                                stub_length,
+                                                plurality(stub_length, "", "s"));
                        }
                    }
                }
@@ -2497,9 +3082,10 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                   third means we can attempt reassembly. */
                if (dcerpc_tree) {
                    if (length > 0) {
-                       proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                       "Fragment data (%d byte%s)", length,
-                                       plurality(length, "", "s"));
+                       proto_tree_add_text (dcerpc_tree, tvb, offset, stub_length,
+                                            "Fragment data (%d byte%s)",
+                                            stub_length,
+                                            plurality(stub_length, "", "s"));
                    }
                }
                if(hdr->flags&PFC_FIRST_FRAG){  /* FIRST fragment */
@@ -2507,7 +3093,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        fragment_add(tvb, offset, pinfo, value->rep_frame,
                             dcerpc_co_reassemble_table,
                             0,
-                            length,
+                            stub_length,
                             TRUE);
                        fragment_set_tot_len(pinfo, value->rep_frame,
                             dcerpc_co_reassemble_table, alloc_hint);
@@ -2527,7 +3113,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                             value->rep_frame,
                             dcerpc_co_reassemble_table,
                             tot_len-alloc_hint,
-                            length,
+                            stub_length,
                             TRUE);
 
                        if(fd_head){
@@ -2552,9 +3138,10 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                             */
                            if (dcerpc_tree) {
                                if (length > 0) {
-                                   proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                               "Fault stub data (%d byte%s)", length,
-                                               plurality(length, "", "s"));
+                                    proto_tree_add_text (dcerpc_tree, tvb, offset, stub_length,
+                                                         "Fault stub data (%d byte%s)",
+                                                         stub_length,
+                                                         plurality(stub_length, "", "s"));
                                }
                            }
                        } else {
@@ -2574,7 +3161,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
                        fragment_add(tvb, offset, pinfo, value->rep_frame,
                             dcerpc_co_reassemble_table,
                             tot_len-alloc_hint,
-                            length,
+                            stub_length,
                             TRUE);
                    }
                    if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -2590,9 +3177,9 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
 /*
  * DCERPC dissector for connection oriented calls
  */
-static int
+static gboolean
 dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
-                   proto_tree *tree, gboolean can_desegment)
+                   proto_tree *tree, gboolean can_desegment, int *pkt_len)
 {
     static char nulls[4] = { 0 };
     int start_offset;
@@ -2614,8 +3201,7 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
      * the 4 bytes of null padding, and make that the dissector
      * used for "netbios".
      */
-    if (tvb_bytes_exist (tvb, offset, 4) &&
-       tvb_memeql (tvb, offset, nulls, 4) == 0) {
+    if (tvb_memeql (tvb, offset, nulls, 4) == 0) {
 
         /*
          * Skip the padding.
@@ -2628,18 +3214,18 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
      * Check if this looks like a C/O DCERPC call
      */
     if (!tvb_bytes_exist (tvb, offset, sizeof (hdr))) {
-        return -1;
+        return FALSE;  /* not enough information to check */
     }
     start_offset = offset;
     hdr.rpc_ver = tvb_get_guint8 (tvb, offset++);
     if (hdr.rpc_ver != 5)
-        return -1;
+        return FALSE;
     hdr.rpc_ver_minor = tvb_get_guint8 (tvb, offset++);
     if (hdr.rpc_ver_minor != 0 && hdr.rpc_ver_minor != 1)
-        return -1;
+        return FALSE;
     hdr.ptype = tvb_get_guint8 (tvb, offset++);
     if (hdr.ptype > 19)
-        return -1;
+        return FALSE;
 
     hdr.flags = tvb_get_guint8 (tvb, offset++);
     tvb_memcpy (tvb, (guint8 *)hdr.drep, offset, sizeof (hdr.drep));
@@ -2653,10 +3239,11 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
     offset += 4;
 
     if (can_desegment && pinfo->can_desegment
-        && hdr.frag_len > tvb_length_remaining (tvb, start_offset)) {
+        && !tvb_bytes_exist(tvb, start_offset, hdr.frag_len)) {
         pinfo->desegment_offset = start_offset;
         pinfo->desegment_len = hdr.frag_len - tvb_length_remaining (tvb, start_offset);
-        return 0;      /* desegmentation required */
+        *pkt_len = 0;  /* desegmentation required */
+        return TRUE;
     }
 
     if (check_col (pinfo->cinfo, COL_PROTOCOL))
@@ -2666,7 +3253,7 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
            pckt_vals[hdr.ptype].strptr, hdr.call_id);
 
     if (tree) {
-      offset = start_offset;
+        offset = start_offset;
         ti = proto_tree_add_item (tree, proto_dcerpc, tvb, offset, hdr.frag_len, FALSE);
         if (ti) {
             dcerpc_tree = proto_item_add_subtree (ti, ett_dcerpc);
@@ -2707,6 +3294,23 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
         offset += 4;
     }
 
+    /*
+     * None of the stuff done above should throw an exception, because
+     * we would have rejected this as "not DCE RPC" if we didn't have all
+     * of it.  (XXX - perhaps we should request reassembly if we have
+     * enough of the header to consider it DCE RPC but not enough to
+     * get the fragment length; in that case the stuff still wouldn't
+     * throw an exception.)
+     *
+     * The rest of the stuff might, so return the PDU length to our caller.
+     * XXX - should we construct a tvbuff containing only the PDU and
+     * use that?  Or should we have separate "is this a DCE RPC PDU",
+     * "how long is it", and "dissect it" routines - which might let us
+     * do most of the work in "tcp_dissect_pdus()"?
+     */
+    if (pkt_len != NULL)
+        *pkt_len = hdr.frag_len + padding;
+
     /*
      * Packet type specific stuff is next.
      */
@@ -2725,7 +3329,7 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
         /*
          * Nothing after the common header other than credentials.
          */
-        dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, TRUE, 
+        dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, &hdr, TRUE, 
                                &auth_info);
         break;
 
@@ -2751,7 +3355,7 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
          * Nothing after the common header other than an authentication
          * verifier.
          */
-        dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, FALSE, 
+        dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, &hdr, FALSE, 
                                &auth_info);
         break;
 
@@ -2764,11 +3368,11 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
 
     default:
         /* might as well dissect the auth info */
-        dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, FALSE, 
+        dissect_dcerpc_cn_auth (tvb, offset, pinfo, dcerpc_tree, &hdr, FALSE, 
                                &auth_info);
         break;
     }
-    return hdr.frag_len + padding;
+    return TRUE;
 }
 
 /*
@@ -2782,7 +3386,7 @@ dissect_dcerpc_cn_pk (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      * Only one PDU per transport packet, and only one transport
      * packet per PDU.
      */
-    if (dissect_dcerpc_cn (tvb, 0, pinfo, tree, FALSE) == -1) {
+    if (!dissect_dcerpc_cn (tvb, 0, pinfo, tree, FALSE, NULL)) {
         /*
          * It wasn't a DCERPC PDU.
          */
@@ -2802,18 +3406,36 @@ dissect_dcerpc_cn_pk (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 static gboolean
 dissect_dcerpc_cn_bs (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-    int offset = 0;
+    volatile int offset = 0;
     int pdu_len;
-    gboolean ret = FALSE;
+    volatile gboolean is_dcerpc_pdu;
+    volatile gboolean ret = FALSE;
 
     /*
      * There may be multiple PDUs per transport packet; keep
      * processing them.
      */
     while (tvb_reported_length_remaining(tvb, offset) != 0) {
-        pdu_len = dissect_dcerpc_cn (tvb, offset, pinfo, tree,
-                                     dcerpc_cn_desegment);
-        if (pdu_len == -1) {
+        /*
+         * Catch ReportedBoundsError, so that even if the stub data is bad,
+         * we don't abort the full DCE RPC dissection - there might be more
+         * than one DCE RPC PDU in the data being dissected.
+         *
+         * If we get BoundsError, it means the frame was cut short by a
+         * snapshot length, so there's nothing more to dissect; just
+         * re-throw that exception.
+         */
+        is_dcerpc_pdu = FALSE;
+        TRY {
+            is_dcerpc_pdu = dissect_dcerpc_cn (tvb, offset, pinfo, tree,
+                                               dcerpc_cn_desegment, &pdu_len);
+        } CATCH(BoundsError) {
+            RETHROW;
+        } CATCH(ReportedBoundsError) {
+            show_reported_bounds_error(tvb, pinfo, tree);
+        } ENDTRY;
+
+        if (!is_dcerpc_pdu) {
             /*
              * Not a DCERPC PDU.
              */
@@ -2869,7 +3491,7 @@ dissect_dcerpc_dg_auth (tvbuff_t *tvb, int offset, proto_tree *dcerpc_tree,
 
         case DCE_C_RPC_AUTHN_PROTOCOL_KRB5:
             ti = proto_tree_add_text (dcerpc_tree, tvb, offset, -1, "Kerberos authentication verifier");
-            auth_tree = proto_item_add_subtree (ti, ett_decrpc_krb5_auth_verf);
+            auth_tree = proto_item_add_subtree (ti, ett_dcerpc_krb5_auth_verf);
             protection_level = tvb_get_guint8 (tvb, offset);
             if (auth_level_p != NULL)
                 *auth_level_p = protection_level;
@@ -3018,11 +3640,10 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
     int length, reported_length, stub_length;
     gboolean save_fragmented;
     fragment_data *fd_head;
+    tvbuff_t *next_tvb;
 
-    if (check_col (pinfo->cinfo, COL_INFO)) {
-        col_append_fstr (pinfo->cinfo, COL_INFO, " opnum: %u",
-                         di->call_data->opnum);
-    }
+    if (check_col (pinfo->cinfo, COL_INFO))
+        col_append_fstr (pinfo->cinfo, COL_INFO, " opnum: %u", di->call_data->opnum );
 
     length = tvb_length_remaining (tvb, offset);
     reported_length = tvb_reported_length_remaining (tvb, offset);
@@ -3040,18 +3661,28 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
        the data in the fragment, just call the handoff directly if
        this is the first fragment or the PDU isn't fragmented. */
     if( (!dcerpc_reassemble) || !(hdr->flags1 & PFCL1_FRAG) ||
-               stub_length > length ) {
+               !tvb_bytes_exist(tvb, offset, stub_length) ){
        if(hdr->frag_num == 0) {
+
+
+    if (check_col (pinfo->cinfo, COL_INFO))
+        col_append_fstr (pinfo->cinfo, COL_INFO, " UNKUUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x rpcver: %u",
+                     di->call_data->uuid.Data1, di->call_data->uuid.Data2, di->call_data->uuid.Data3, di->call_data->uuid.Data4[0],
+                     di->call_data->uuid.Data4[1], di->call_data->uuid.Data4[2], di->call_data->uuid.Data4[3],
+                     di->call_data->uuid.Data4[4], di->call_data->uuid.Data4[5], di->call_data->uuid.Data4[6],
+                     di->call_data->uuid.Data4[7], di->call_data->ver);
+
+
            /* First fragment, possibly the only fragment */
 
            /*
             * XXX - authentication info?
             */
            pinfo->fragmented = (hdr->flags1 & PFCL1_FRAG);
-           dcerpc_try_handoff (pinfo, tree, dcerpc_tree,
-                               tvb_new_subset (tvb, offset, length,
-                                               reported_length),
-                               0, hdr->drep, di, NULL);
+           next_tvb = tvb_new_subset (tvb, offset, length,
+                                      reported_length);
+           dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb,
+                               next_tvb, hdr->drep, di, NULL);
        } else {
            /* PDU is fragmented and this isn't the first fragment */
            if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -3059,10 +3690,11 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
            }
            if (dcerpc_tree) {
                if (length > 0) {
-                   proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                        "Fragment data (%d byte%s)", length,
-                                        plurality(length, "", "s"));
-               }
+                   proto_tree_add_text (dcerpc_tree, tvb, offset, stub_length,
+                                        "Fragment data (%d byte%s)",
+                                        stub_length,
+                                        plurality(stub_length, "", "s"));
+               }
            }
         }
     } else {
@@ -3072,19 +3704,18 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
           third means we can attempt reassembly. */
        if (dcerpc_tree) {
            if (length > 0) {
-               proto_tree_add_text (dcerpc_tree, tvb, offset, length,
-                                    "Fragment data (%d byte%s)", length,
-                                    plurality(length, "", "s"));
+               proto_tree_add_text (dcerpc_tree, tvb, offset, stub_length,
+                                    "Fragment data (%d byte%s)", stub_length,
+                                    plurality(stub_length, "", "s"));
            }
        }
 
        fd_head = fragment_add_seq(tvb, offset, pinfo,
                        hdr->seqnum, dcerpc_cl_reassemble_table,
-                       hdr->frag_num, length, !(hdr->flags1 & PFCL1_LASTFRAG));
+                       hdr->frag_num, stub_length,
+                       !(hdr->flags1 & PFCL1_LASTFRAG));
        if (fd_head != NULL) {
            /* We completed reassembly */
-           tvbuff_t *next_tvb;
-
            next_tvb = tvb_new_real_data(fd_head->data, fd_head->len, fd_head->len);
            tvb_set_child_real_data_tvbuff(tvb, next_tvb);
            add_new_data_source(pinfo, next_tvb, "Reassembled DCE/RPC");
@@ -3096,7 +3727,7 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
             */
            pinfo->fragmented = FALSE;
            dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb,
-                               0, hdr->drep, di, NULL);
+                               next_tvb, hdr->drep, di, NULL);
        } else {
            /* Reassembly isn't completed yet */
            if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -3112,9 +3743,11 @@ dissect_dcerpc_dg_rqst (tvbuff_t *tvb, int offset, packet_info *pinfo,
                         proto_tree *dcerpc_tree, proto_tree *tree,
                         e_dce_dg_common_hdr_t *hdr, conversation_t *conv)
 {
-    dcerpc_info di;
+    dcerpc_info *di;
     dcerpc_call_value *value, v;
+    dcerpc_matched_key matched_key, *new_matched_key;
 
+    di=get_next_di();
     if(!(pinfo->fd->flags.visited)){
        dcerpc_call_value *call_value;
        dcerpc_call_key *call_key;
@@ -3136,10 +3769,15 @@ dissect_dcerpc_dg_rqst (tvbuff_t *tvb, int offset, packet_info *pinfo,
        call_value->private_data = NULL;
        g_hash_table_insert (dcerpc_calls, call_key, call_value);
 
-       g_hash_table_insert (dcerpc_matched, (void *)pinfo->fd->num, call_value);
+       new_matched_key = g_mem_chunk_alloc(dcerpc_matched_key_chunk);
+       new_matched_key->frame = pinfo->fd->num;
+       new_matched_key->call_id = hdr->seqnum;
+       g_hash_table_insert (dcerpc_matched, new_matched_key, call_value);
     }
 
-    value=g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num);
+    matched_key.frame = pinfo->fd->num;
+    matched_key.call_id = hdr->seqnum;
+    value=g_hash_table_lookup(dcerpc_matched, &matched_key);
     if (!value) {
         v.uuid = hdr->if_id;
         v.ver = hdr->if_ver;
@@ -3151,17 +3789,17 @@ dissect_dcerpc_dg_rqst (tvbuff_t *tvb, int offset, packet_info *pinfo,
         value = &v;
     }
 
-    di.conv = conv;
-    di.call_id = hdr->seqnum;
-    di.smb_fid = -1;
-    di.request = TRUE;
-    di.call_data = value;
+    di->conv = conv;
+    di->call_id = hdr->seqnum;
+    di->smb_fid = -1;
+    di->request = TRUE;
+    di->call_data = value;
 
     if(value->rep_frame!=0){
        proto_tree_add_uint(dcerpc_tree, hf_dcerpc_response_in,
                            tvb, 0, 0, value->rep_frame);
     }
-    dissect_dcerpc_dg_stub (tvb, offset, pinfo, dcerpc_tree, tree, hdr, &di);
+    dissect_dcerpc_dg_stub (tvb, offset, pinfo, dcerpc_tree, tree, hdr, di);
 }
 
 static void
@@ -3169,9 +3807,11 @@ dissect_dcerpc_dg_resp (tvbuff_t *tvb, int offset, packet_info *pinfo,
                         proto_tree *dcerpc_tree, proto_tree *tree,
                         e_dce_dg_common_hdr_t *hdr, conversation_t *conv)
 {
-    dcerpc_info di;
+    dcerpc_info *di;
     dcerpc_call_value *value, v;
+    dcerpc_matched_key matched_key, *new_matched_key;
 
+    di=get_next_di();
     if(!(pinfo->fd->flags.visited)){
        dcerpc_call_value *call_value;
        dcerpc_call_key call_key;
@@ -3181,14 +3821,19 @@ dissect_dcerpc_dg_resp (tvbuff_t *tvb, int offset, packet_info *pinfo,
        call_key.smb_fid=get_smb_fid(pinfo->private_data);
 
        if((call_value=g_hash_table_lookup(dcerpc_calls, &call_key))){
-           g_hash_table_insert (dcerpc_matched, (void *)pinfo->fd->num, call_value);
+           new_matched_key = g_mem_chunk_alloc(dcerpc_matched_key_chunk);
+           new_matched_key->frame = pinfo->fd->num;
+           new_matched_key->call_id = hdr->seqnum;
+           g_hash_table_insert (dcerpc_matched, new_matched_key, call_value);
            if(call_value->rep_frame==0){
                call_value->rep_frame=pinfo->fd->num;
            }
        }
     }
 
-    value=g_hash_table_lookup(dcerpc_matched, (void *)pinfo->fd->num);
+    matched_key.frame = pinfo->fd->num;
+    matched_key.call_id = hdr->seqnum;
+    value=g_hash_table_lookup(dcerpc_matched, &matched_key);
     if (!value) {
         v.uuid = hdr->if_id;
         v.ver = hdr->if_ver;
@@ -3199,11 +3844,11 @@ dissect_dcerpc_dg_resp (tvbuff_t *tvb, int offset, packet_info *pinfo,
         value = &v;
     }
 
-    di.conv = conv;
-    di.call_id = 0;
-    di.smb_fid = -1;
-    di.request = FALSE;
-    di.call_data = value;
+    di->conv = conv;
+    di->call_id = 0;
+    di->smb_fid = -1;
+    di->request = FALSE;
+    di->call_data = value;
 
     if(value->req_frame!=0){
        nstime_t ns;
@@ -3217,7 +3862,7 @@ dissect_dcerpc_dg_resp (tvbuff_t *tvb, int offset, packet_info *pinfo,
        }
        proto_tree_add_time(dcerpc_tree, hf_dcerpc_time, tvb, offset, 0, &ns);
     }
-    dissect_dcerpc_dg_stub (tvb, offset, pinfo, dcerpc_tree, tree, hdr, &di);
+    dissect_dcerpc_dg_stub (tvb, offset, pinfo, dcerpc_tree, tree, hdr, di);
 }
 
 /*
@@ -3604,7 +4249,13 @@ dcerpc_init_protocol (void)
                g_hash_table_destroy (dcerpc_matched);
        }
        dcerpc_matched = g_hash_table_new (dcerpc_matched_hash, dcerpc_matched_equal);
-
+       if (dcerpc_matched_key_chunk){
+               g_mem_chunk_destroy (dcerpc_matched_key_chunk);
+       }
+       dcerpc_matched_key_chunk = g_mem_chunk_new ("dcerpc_matched_key_chunk",
+                                             sizeof (dcerpc_matched_key),
+                                             200 * sizeof (dcerpc_matched_key),
+                                             G_ALLOC_ONLY);
 }
 
 void
@@ -3612,10 +4263,10 @@ proto_register_dcerpc (void)
 {
     static hf_register_info hf[] = {
        { &hf_dcerpc_request_in,
-               { "Request in", "dcerpc.request_in", FT_UINT32, BASE_DEC,
+               { "Request in", "dcerpc.request_in", FT_FRAMENUM, BASE_NONE,
                NULL, 0, "This packet is a response to the packet in this frame", HFILL }},
        { &hf_dcerpc_response_in,
-               { "Response in", "dcerpc.response_in", FT_UINT32, BASE_DEC,
+               { "Response in", "dcerpc.response_in", FT_FRAMENUM, BASE_NONE,
                NULL, 0, "The response to this packet is in this packet", HFILL }},
        { &hf_dcerpc_referent_id,
                { "Referent ID", "dcerpc.referent_id", FT_UINT32, BASE_HEX,
@@ -3802,7 +4453,7 @@ proto_register_dcerpc (void)
           { "FACK Version", "dcerpc.fack_vers", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
         { &hf_dcerpc_dg_fack_window_size,
-          { "Window Size", "dcerpc.fack_window size", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
+          { "Window Size", "dcerpc.fack_window_size", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
 
         { &hf_dcerpc_dg_fack_max_tsdu,
           { "Max TSDU", "dcerpc.fack_max_tsdu", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
@@ -3831,6 +4482,9 @@ proto_register_dcerpc (void)
         { &hf_dcerpc_array_actual_count,
           { "Actual Count", "dcerpc.array.actual_count", FT_UINT32, BASE_DEC, NULL, 0x0, "Actual Count: Actual number of elements in the array", HFILL }},
 
+       { &hf_dcerpc_array_buffer,
+         { "Buffer", "dcerpc.array.buffer", FT_BYTES, BASE_NONE, NULL, 0x0, "Buffer: Buffer containing elements of the array", HFILL }},
+               
         { &hf_dcerpc_op,
           { "Operation", "dcerpc.op", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
 
@@ -3858,19 +4512,23 @@ proto_register_dcerpc (void)
          { "Defragmentation error", "dcerpc.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "Defragmentation error due to illegal fragments", HFILL }},
 
        { &hf_dcerpc_time, 
-         { "Time from request", "dcerpc.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "Time between Request and Reply for DCE-RPC calls", HFILL }}
-
+         { "Time from request", "dcerpc.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "Time between Request and Reply for DCE-RPC calls", HFILL }},
+       { &hf_dcerpc_reassembled_in,
+         { "This PDU is reassembled in", "dcerpc.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "The DCE/RPC PDU is completely reassembled in this frame", HFILL }},
    };
     static gint *ett[] = {
         &ett_dcerpc,
         &ett_dcerpc_cn_flags,
+        &ett_dcerpc_cn_ctx,
+        &ett_dcerpc_cn_iface,
         &ett_dcerpc_drep,
         &ett_dcerpc_dg_flags1,
         &ett_dcerpc_dg_flags2,
         &ett_dcerpc_pointer_data,
+        &ett_dcerpc_string,
         &ett_dcerpc_fragments,
         &ett_dcerpc_fragment,
-        &ett_decrpc_krb5_auth_verf,
+        &ett_dcerpc_krb5_auth_verf,
     };
     module_t *dcerpc_module;
 
@@ -3901,9 +4559,5 @@ proto_reg_handoff_dcerpc (void)
     heur_dissector_add ("netbios", dissect_dcerpc_cn_pk, proto_dcerpc);
     heur_dissector_add ("udp", dissect_dcerpc_dg, proto_dcerpc);
     heur_dissector_add ("smb_transact", dissect_dcerpc_cn_bs, proto_dcerpc);
-    ntlmssp_handle = find_dissector("ntlmssp");
-    ntlmssp_verf_handle = find_dissector("ntlmssp_verf");
-    ntlmssp_enc_payload_handle = find_dissector("ntlmssp_encrypted_payload");
-    gssapi_handle = find_dissector("gssapi");
-    gssapi_verf_handle = find_dissector("gssapi_verf");
+    dcerpc_smb_init(proto_dcerpc);
 }