Fix for bug 5422:
[obnox/wireshark/wip.git] / epan / dissectors / packet-rpc.c
index 9999b357509ba833166b3e77dca4ebbb66bc7ae5..f8710f321f5a04ed52bced68ae41b8c543f7db71 100644 (file)
@@ -4,8 +4,8 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * Copied from packet-smb.c
 #endif
 
 #include <glib.h>
-#include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <epan/packet.h>
 #include <epan/conversation.h>
+#include <epan/emem.h>
 #include "packet-rpc.h"
 #include "packet-frame.h"
 #include "packet-tcp.h"
 #include <epan/prefs.h>
 #include <epan/reassemble.h>
-#include "rpc_defrag.h"
+#include <epan/dissectors/rpc_defrag.h>
 #include "packet-nfs.h"
 #include <epan/tap.h>
-#include <epan/emem.h>
+#include <epan/strutil.h>
+#include <epan/garrayfix.h>
 
 /*
  * See:
 static gboolean rpc_desegment = TRUE;
 
 /* defragmentation of fragmented RPC over TCP records */
-static gboolean rpc_defragment = FALSE;
+static gboolean rpc_defragment = TRUE;
 
 /* try to dissect RPC packets for programs that are not known
- * (proprietary ones) by ethereal.
+ * (proprietary ones) by wireshark.
  */
 static gboolean rpc_dissect_unknown_programs = FALSE;
 
-
-static struct true_false_string yesno = { "Yes", "No" };
+/* try to find RPC fragment start if normal decode fails
+ * (good when starting decode of mid-stream capture)
+ */
+static gboolean rpc_find_fragment_start = FALSE;
 
 static int rpc_tap = -1;
 
@@ -97,6 +100,15 @@ const value_string rpc_auth_flavor[] = {
        { AUTH_DES, "AUTH_DES" },
        { RPCSEC_GSS, "RPCSEC_GSS" },
        { AUTH_GSSAPI, "AUTH_GSSAPI" },
+       { RPCSEC_GSS_KRB5, "RPCSEC_GSS_KRB5" },
+       { RPCSEC_GSS_KRB5I, "RPCSEC_GSS_KRB5I" },
+       { RPCSEC_GSS_KRB5P, "RPCSEC_GSS_KRB5P" },
+       { RPCSEC_GSS_LIPKEY, "RPCSEC_GSS_LIPKEY" },
+       { RPCSEC_GSS_LIPKEY_I, "RPCSEC_GSS_LIPKEY_I" },
+       { RPCSEC_GSS_LIPKEY_P, "RPCSEC_GSS_LIPKEY_P" },
+       { RPCSEC_GSS_SPKM3, "RPCSEC_GSS_SPKM3" },
+       { RPCSEC_GSS_SPKM3I, "RPCSEC_GSS_SPKM3I" },
+       { RPCSEC_GSS_SPKM3P, "RPCSEC_GSS_SPKM3P" },
        { 0, NULL }
 };
 
@@ -117,7 +129,7 @@ static const value_string rpc_authgssapi_proc[] = {
        { 0, NULL }
 };
 
-value_string rpc_authgss_svc[] = {
+const value_string rpc_authgss_svc[] = {
        { RPCSEC_GSS_SVC_NONE, "rpcsec_gss_svc_none" },
        { RPCSEC_GSS_SVC_INTEGRITY, "rpcsec_gss_svc_integrity" },
        { RPCSEC_GSS_SVC_PRIVACY, "rpcsec_gss_svc_privacy" },
@@ -190,6 +202,7 @@ static int hf_rpc_authgss_window = -1;
 static int hf_rpc_authgss_token_length = -1;
 static int hf_rpc_authgss_data_length = -1;
 static int hf_rpc_authgss_data = -1;
+static int hf_rpc_authgss_token = -1;
 static int hf_rpc_authgss_checksum = -1;
 static int hf_rpc_authgssapi_v = -1;
 static int hf_rpc_authgssapi_msg = -1;
@@ -221,6 +234,7 @@ static int hf_rpc_fragment_overlap_conflict = -1;
 static int hf_rpc_fragment_multiple_tails = -1;
 static int hf_rpc_fragment_too_long_fragment = -1;
 static int hf_rpc_fragment_error = -1;
+static int hf_rpc_reassembled_length = -1;
 
 static gint ett_rpc = -1;
 static gint ett_rpc_unknown_program = -1;
@@ -241,7 +255,7 @@ static dissector_handle_t rpc_handle;
 static dissector_handle_t gssapi_handle;
 static dissector_handle_t data_handle;
 
-static guint max_rpc_tcp_pdu_size = 262144;
+static guint max_rpc_tcp_pdu_size = 4 * 1024 * 1024;
 
 static const fragment_items rpc_frag_items = {
        &ett_rpc_fragment,
@@ -254,14 +268,15 @@ static const fragment_items rpc_frag_items = {
        &hf_rpc_fragment_too_long_fragment,
        &hf_rpc_fragment_error,
        NULL,
+       &hf_rpc_reassembled_length,
        "fragments"
 };
 
 /* Hash table with info on RPC program numbers */
-GHashTable *rpc_progs;
+GHashTable *rpc_progs = NULL;
 
 /* Hash table with info on RPC procedure numbers */
-GHashTable *rpc_procs;
+GHashTable *rpc_procs = NULL;
 
 static void dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
 static void dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
@@ -333,24 +348,23 @@ rpc_init_proc_table(guint prog, guint vers, const vsff *proc_table,
 
 
 /*     return the name associated with a previously registered procedure. */
-char *rpc_proc_name(guint32 prog, guint32 vers, guint32 proc)
+const char *
+rpc_proc_name(guint32 prog, guint32 vers, guint32 proc)
 {
        rpc_proc_info_key key;
        rpc_proc_info_value *value;
        char *procname;
-       static char procname_static[20];
 
        key.prog = prog;
        key.vers = vers;
        key.proc = proc;
 
        if ((value = g_hash_table_lookup(rpc_procs,&key)) != NULL)
-               procname = value->name;
+               procname = (char *)value->name;
        else {
                /* happens only with strange program versions or
                   non-existing dissectors */
-               sprintf(procname_static, "proc-%u", key.proc);
-               procname = procname_static;
+               procname = ep_strdup_printf("proc-%u", key.proc);
        }
        return procname;
 }
@@ -409,7 +423,8 @@ rpc_init_prog(int proto, guint32 prog, int ett)
 
 /*     return the hf_field associated with a previously registered program.
 */
-int rpc_prog_hf(guint32 prog, guint32 vers)
+int
+rpc_prog_hf(guint32 prog, guint32 vers)
 {
        rpc_prog_info_key       rpc_prog_key;
        rpc_prog_info_value     *rpc_prog;
@@ -423,8 +438,9 @@ int rpc_prog_hf(guint32 prog, guint32 vers)
 
 /*     return the name associated with a previously registered program. This
        should probably eventually be expanded to use the rpc YP/NIS map
-       so that it can give names for programs not handled by ethereal */
-const char *rpc_prog_name(guint32 prog)
+       so that it can give names for programs not handled by wireshark */
+const char *
+rpc_prog_name(guint32 prog)
 {
        const char *progname = NULL;
        rpc_prog_info_key       rpc_prog_key;
@@ -445,52 +461,30 @@ const char *rpc_prog_name(guint32 prog)
 /* end of Hash array with program names */
 /*--------------------------------------*/
 
-typedef struct _rpc_call_info_key {
-       guint32 xid;
-       conversation_t *conversation;
-} rpc_call_info_key;
-
-static GMemChunk *rpc_call_info_key_chunk;
-
-static GMemChunk *rpc_call_info_value_chunk;
-
-static GHashTable *rpc_calls;
-
-static GHashTable *rpc_indir_calls;
-
-/* compare 2 keys */
-static gint
-rpc_call_equal(gconstpointer k1, gconstpointer k2)
-{
-       const rpc_call_info_key* key1 = (const rpc_call_info_key*) k1;
-       const rpc_call_info_key* key2 = (const rpc_call_info_key*) k2;
-
-       return (key1->xid == key2->xid &&
-           key1->conversation == key2->conversation);
-}
-
-
-/* calculate a hash key */
-static guint
-rpc_call_hash(gconstpointer k)
-{
-       const rpc_call_info_key* key = (const rpc_call_info_key*) k;
-
-       return key->xid + GPOINTER_TO_UINT(key->conversation);
-}
+/* One of these structures are created for each conversation that contains
+ * RPC and contains the state we need to maintain for the conversation.
+ */
+typedef struct _rpc_conv_info_t {
+        emem_tree_t *xids;
+} rpc_conv_info_t;
 
 
 unsigned int
 rpc_roundup(unsigned int a)
 {
        unsigned int mod = a % 4;
-       return a + ((mod)? 4-mod : 0);
+        unsigned int ret;
+       ret = a + ((mod)? 4-mod : 0);
+        /* Check for overflow */
+        if (ret < a)
+               THROW(ReportedBoundsError);
+       return ret;
 }
 
 
 int
 dissect_rpc_bool(tvbuff_t *tvb, proto_tree *tree,
-int hfindex, int offset)
+                int hfindex, int offset)
 {
        if (tree)
                proto_tree_add_item(tree, hfindex, tvb, offset, 4, FALSE);
@@ -500,7 +494,7 @@ int hfindex, int offset)
 
 int
 dissect_rpc_uint32(tvbuff_t *tvb, proto_tree *tree,
-int hfindex, int offset)
+                  int hfindex, int offset)
 {
        if (tree)
                proto_tree_add_item(tree, hfindex, tvb, offset, 4, FALSE);
@@ -510,7 +504,7 @@ int hfindex, int offset)
 
 int
 dissect_rpc_uint64(tvbuff_t *tvb, proto_tree *tree,
-int hfindex, int offset)
+                  int hfindex, int offset)
 {
        header_field_info       *hfinfo;
 
@@ -523,9 +517,9 @@ int hfindex, int offset)
 }
 
 /*
- * We want to make this function available outside this file and 
+ * We want to make this function available outside this file and
  * allow callers to pass a dissection function for the opaque data
- */ 
+ */
 int
 dissect_rpc_opaque_data(tvbuff_t *tvb, int offset,
     proto_tree *tree,
@@ -618,44 +612,33 @@ dissect_rpc_opaque_data(tvbuff_t *tvb, int offset,
         }
 
        if (string_data) {
-               char *tmpstr;
-               tmpstr = tvb_get_string(tvb, data_offset, string_length_copy);
-               string_buffer = memcpy(ep_alloc(string_length_copy), tmpstr, string_length_copy);
-               g_free(tmpstr);
+               string_buffer = tvb_get_ephemeral_string(tvb, data_offset, string_length_copy);
        } else {
-               string_buffer = tvb_memcpy(tvb, ep_alloc(string_length_copy), data_offset, string_length_copy);
+               string_buffer = tvb_memcpy(tvb, ep_alloc(string_length_copy+1), data_offset, string_length_copy);
        }
+       string_buffer[string_length_copy] = '\0';
        /* calculate a nice printable string */
        if (string_length) {
                if (string_length != string_length_copy) {
                        if (string_data) {
-                               /* alloc maximum data area */
-                               string_buffer_print = (char*)ep_alloc(string_length_copy + 12 + 1);
-                               /* copy over the data */
-                               memcpy(string_buffer_print,string_buffer,string_length_copy);
-                               /* append a 0 byte for sure printing */
-                               string_buffer_print[string_length_copy] = '\0';
-                               /* append <TRUNCATED> */
-                               /* This way, we get the TRUNCATED even
-                                  in the case of totally wrong packets,
-                                  where \0 are inside the string.
-                                  TRUNCATED will appear at the
-                                  first \0 or at the end (where we
-                                  put the securing \0).
-                               */
-                               strcat(string_buffer_print,"<TRUNCATED>");
+                               char *formatted;
+
+                               formatted = format_text(string_buffer, strlen(string_buffer));
+                               /* copy over the data and append <TRUNCATED> */
+                               string_buffer_print=ep_strdup_printf("%s%s", formatted, RPC_STRING_TRUNCATED);
                        } else {
-                               string_buffer_print="<DATA><TRUNCATED>";
+                               string_buffer_print=RPC_STRING_DATA RPC_STRING_TRUNCATED;
                        }
                } else {
                        if (string_data) {
-                               string_buffer_print = string_buffer;
+                               string_buffer_print =
+                                   ep_strdup(format_text(string_buffer, strlen(string_buffer)));
                        } else {
-                               string_buffer_print="<DATA>";
+                               string_buffer_print=RPC_STRING_DATA;
                        }
                }
        } else {
-               string_buffer_print="<EMPTY>";
+               string_buffer_print=RPC_STRING_EMPTY;
        }
 
        if (tree) {
@@ -724,7 +707,7 @@ int
 dissect_rpc_string(tvbuff_t *tvb, proto_tree *tree,
     int hfindex, int offset, char **string_buffer_ret)
 {
-        offset = dissect_rpc_opaque_data(tvb, offset, tree, NULL, 
+        offset = dissect_rpc_opaque_data(tvb, offset, tree, NULL,
            hfindex, FALSE, 0, TRUE, string_buffer_ret, NULL);
        return offset;
 }
@@ -734,7 +717,7 @@ int
 dissect_rpc_data(tvbuff_t *tvb, proto_tree *tree,
     int hfindex, int offset)
 {
-        offset = dissect_rpc_opaque_data(tvb, offset, tree, NULL, 
+        offset = dissect_rpc_opaque_data(tvb, offset, tree, NULL,
                                         hfindex, FALSE, 0, FALSE, NULL, NULL);
        return offset;
 }
@@ -901,7 +884,7 @@ dissect_rpc_authgss_cred(tvbuff_t* tvb, proto_tree* tree, int offset)
 
 static int
 dissect_rpc_authdes_desblock(tvbuff_t *tvb, proto_tree *tree,
-int hfindex, int offset)
+                            int hfindex, int offset)
 {
        guint32 value_low;
        guint32 value_high;
@@ -1040,7 +1023,7 @@ dissect_rpc_cred(tvbuff_t* tvb, proto_tree* tree, int offset)
  */
 static int
 dissect_rpc_authgss_token(tvbuff_t* tvb, proto_tree* tree, int offset,
-    packet_info *pinfo)
+    packet_info *pinfo, int hfindex)
 {
        guint32 opaque_length, rounded_length;
        gint len_consumed, length, reported_length;
@@ -1052,26 +1035,27 @@ dissect_rpc_authgss_token(tvbuff_t* tvb, proto_tree* tree, int offset,
        opaque_length = tvb_get_ntohl(tvb, offset+0);
        rounded_length = rpc_roundup(opaque_length);
        if (tree) {
-               gitem = proto_tree_add_text(tree, tvb, offset,
-                                           4+rounded_length, "GSS Token");
+               gitem = proto_tree_add_item(tree, hfindex, tvb, offset, 4+rounded_length, FALSE);
                gtree = proto_item_add_subtree(gitem, ett_rpc_gss_token);
                proto_tree_add_uint(gtree, hf_rpc_authgss_token_length,
                                    tvb, offset+0, 4, opaque_length);
        }
        offset += 4;
-       length = tvb_length_remaining(tvb, offset);
-       reported_length = tvb_reported_length_remaining(tvb, offset);
-       DISSECTOR_ASSERT(length >= 0);
-       DISSECTOR_ASSERT(reported_length >= 0);
-       if (length > reported_length)
-               length = reported_length;
-       if ((guint32)length > opaque_length)
-               length = opaque_length;
-       if ((guint32)reported_length > opaque_length)
-               reported_length = opaque_length;
-       new_tvb = tvb_new_subset(tvb, offset, length, reported_length);
-       len_consumed = call_dissector(gssapi_handle, new_tvb, pinfo, gtree);
-       offset += len_consumed;
+       if (opaque_length != 0) {
+               length = tvb_length_remaining(tvb, offset);
+               reported_length = tvb_reported_length_remaining(tvb, offset);
+               DISSECTOR_ASSERT(length >= 0);
+               DISSECTOR_ASSERT(reported_length >= 0);
+               if (length > reported_length)
+                       length = reported_length;
+               if ((guint32)length > opaque_length)
+                       length = opaque_length;
+               if ((guint32)reported_length > opaque_length)
+                       reported_length = opaque_length;
+               new_tvb = tvb_new_subset(tvb, offset, length, reported_length);
+               len_consumed = call_dissector(gssapi_handle, new_tvb, pinfo, gtree);
+               offset += len_consumed;
+       }
        offset = rpc_roundup(offset);
        return offset;
 }
@@ -1133,7 +1117,7 @@ dissect_rpc_verf(tvbuff_t* tvb, proto_tree* tree, int offset, int msg_type,
                        }
                        break;
                case RPCSEC_GSS:
-                       dissect_rpc_authgss_token(tvb, vtree, offset+4, pinfo);
+                       dissect_rpc_authgss_token(tvb, vtree, offset+4, pinfo, hf_rpc_authgss_token);
                        break;
                default:
                        proto_tree_add_uint(vtree, hf_rpc_auth_length, tvb,
@@ -1153,7 +1137,7 @@ static int
 dissect_rpc_authgss_initarg(tvbuff_t* tvb, proto_tree* tree, int offset,
     packet_info *pinfo)
 {
-       return dissect_rpc_authgss_token(tvb, tree, offset, pinfo);
+       return dissect_rpc_authgss_token(tvb, tree, offset, pinfo, hf_rpc_authgss_token);
 }
 
 static int
@@ -1183,7 +1167,7 @@ dissect_rpc_authgss_initres(tvbuff_t* tvb, proto_tree* tree, int offset,
                                    offset+0, 4, window);
        offset += 4;
 
-       offset = dissect_rpc_authgss_token(tvb, tree, offset, pinfo);
+       offset = dissect_rpc_authgss_token(tvb, tree, offset, pinfo, hf_rpc_authgss_token);
 
        return offset;
 }
@@ -1208,7 +1192,7 @@ dissect_rpc_authgssapi_initarg(tvbuff_t* tvb, proto_tree* tree, int offset,
        }
        offset += 4;
 
-       offset = dissect_rpc_authgss_token(tvb, mtree, offset, pinfo);
+       offset = dissect_rpc_authgss_token(tvb, mtree, offset, pinfo, hf_rpc_authgss_token);
 
        return offset;
 }
@@ -1252,7 +1236,7 @@ dissect_rpc_authgssapi_initres(tvbuff_t* tvb, proto_tree* tree, int offset,
        }
        offset += 4;
 
-       offset = dissect_rpc_authgss_token(tvb, mtree, offset, pinfo);
+       offset = dissect_rpc_authgss_token(tvb, mtree, offset, pinfo, hf_rpc_authgss_token);
 
        offset = dissect_rpc_data(tvb, mtree, hf_rpc_authgssapi_isn, offset);
 
@@ -1273,6 +1257,7 @@ call_dissect_function(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 {
        const char *saved_proto;
 
+       tvb_ensure_length_remaining(tvb, offset);
        if (dissect_function != NULL) {
                /* set the current protocol name */
                saved_proto = pinfo->current_proto;
@@ -1322,8 +1307,8 @@ dissect_rpc_authgss_integ_data(tvbuff_t *tvb, packet_info *pinfo,
                                      dissect_function, progname);
        }
        offset += rounded_length - 4;
-       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_checksum,
-                       offset);
+       offset = dissect_rpc_authgss_token(tvb, tree, offset, pinfo, hf_rpc_authgss_checksum);
+
        return offset;
 }
 
@@ -1338,10 +1323,12 @@ dissect_rpc_authgss_priv_data(tvbuff_t *tvb, proto_tree *tree, int offset)
 
 /*
  * Dissect the arguments to an indirect call; used by the portmapper/RPCBIND
- * dissector.
+ * dissector for the CALLIT procedure.
  *
- * Record this call in a hash table, similar to the hash table for
- * direct calls, so we can find it when dissecting an indirect call reply.
+ * Record these in the same table as the direct calls
+ * so we can find it when dissecting an indirect call reply.
+ * (There should not be collissions between xid between direct and
+ *  indirect calls.)
  */
 int
 dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
@@ -1352,9 +1339,9 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        rpc_proc_info_key key;
        rpc_proc_info_value *value;
        rpc_call_info_value *rpc_call;
-       rpc_call_info_key rpc_call_key;
-       rpc_call_info_key *new_rpc_call_key;
        dissect_function_t *dissect_function = NULL;
+       rpc_conv_info_t *rpc_conv_info=NULL;
+       guint32 xid;
 
        key.prog = prog;
        key.vers = vers;
@@ -1414,31 +1401,38 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                    0, NO_ADDR2|NO_PORT2);
                        }
                }
+               /*
+                * Do we already have a state structure for this conv
+                */
+               rpc_conv_info = conversation_get_proto_data(conversation, proto_rpc);
+               if (!rpc_conv_info) {
+                       /* No.  Attach that information to the conversation, and add
+                        * it to the list of information structures.
+                        */
+                       rpc_conv_info = se_alloc(sizeof(rpc_conv_info_t));
+                       rpc_conv_info->xids=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rpc_xids");
+
+                       conversation_add_proto_data(conversation, proto_rpc, rpc_conv_info);
+               }
 
                /* Make the dissector for this conversation the non-heuristic
                   RPC dissector. */
                conversation_set_dissector(conversation,
                    (pinfo->ptype == PT_TCP) ? rpc_tcp_handle : rpc_handle);
 
-               /* Prepare the key data.
-
-                  Dissectors for RPC procedure calls and replies shouldn't
+               /* Dissectors for RPC procedure calls and replies shouldn't
                   create new tvbuffs, and we don't create one ourselves,
                   so we should have been handed the tvbuff for this RPC call;
                   as such, the XID is at offset 0 in this tvbuff. */
-               rpc_call_key.xid = tvb_get_ntohl(tvb, 0);
-               rpc_call_key.conversation = conversation;
-
                /* look up the request */
-               rpc_call = g_hash_table_lookup(rpc_indir_calls, &rpc_call_key);
+               xid = tvb_get_ntohl(tvb, offset + 0);
+               rpc_call = se_tree_lookup32(rpc_conv_info->xids, xid);
                if (rpc_call == NULL) {
                        /* We didn't find it; create a new entry.
                           Prepare the value data.
                           Not all of it is needed for handling indirect
                           calls, so we set a bunch of items to 0. */
-                       new_rpc_call_key = g_mem_chunk_alloc(rpc_call_info_key_chunk);
-                       *new_rpc_call_key = rpc_call_key;
-                       rpc_call = g_mem_chunk_alloc(rpc_call_info_value_chunk);
+                       rpc_call = se_alloc(sizeof(rpc_call_info_value));
                        rpc_call->req_num = 0;
                        rpc_call->rep_num = 0;
                        rpc_call->prog = prog;
@@ -1455,8 +1449,7 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        rpc_call->gss_svc = 0;
                        rpc_call->proc_info = value;
                        /* store it */
-                       g_hash_table_insert(rpc_indir_calls, new_rpc_call_key,
-                           rpc_call);
+                       se_tree_insert32(rpc_conv_info->xids, xid, (void *)rpc_call);
                }
        }
        else {
@@ -1493,14 +1486,14 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 {
        conversation_t* conversation;
        static address null_address = { AT_NONE, 0, NULL };
-       rpc_call_info_key rpc_call_key;
        rpc_call_info_value *rpc_call;
-       char *procname = NULL;
-       char procname_static[20];
+       char *procname=NULL;
        dissect_function_t *dissect_function = NULL;
+       rpc_conv_info_t *rpc_conv_info=NULL;
+       guint32 xid;
 
-       /* Look for the matching call in the hash table of indirect
-          calls.  A reply must match a call that we've seen, and the
+       /* Look for the matching call in the xid table.
+          A reply must match a call that we've seen, and the
           reply must be sent to the same address that the call came
           from, and must come from the port to which the call was sent.
 
@@ -1540,11 +1533,22 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                    offset);
                return offset;
        }
+       /*
+        * Do we already have a state structure for this conv
+        */
+       rpc_conv_info = conversation_get_proto_data(conversation, proto_rpc);
+       if (!rpc_conv_info) {
+               /* No.  Attach that information to the conversation, and add
+                * it to the list of information structures.
+                */
+               rpc_conv_info = se_alloc(sizeof(rpc_conv_info_t));
+               rpc_conv_info->xids=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rpc_xids");
+               conversation_add_proto_data(conversation, proto_rpc, rpc_conv_info);
+       }
 
        /* The XIDs of the call and reply must match. */
-       rpc_call_key.xid = tvb_get_ntohl(tvb, 0);
-       rpc_call_key.conversation = conversation;
-       rpc_call = g_hash_table_lookup(rpc_indir_calls, &rpc_call_key);
+       xid = tvb_get_ntohl(tvb, 0);
+       rpc_call = se_tree_lookup32(rpc_conv_info->xids, xid);
        if (rpc_call == NULL) {
                /* The XID doesn't match a call from that
                   conversation, so it's probably not an RPC reply.
@@ -1557,31 +1561,36 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        if (rpc_call->proc_info != NULL) {
                dissect_function = rpc_call->proc_info->dissect_reply;
                if (rpc_call->proc_info->name != NULL) {
-                       procname = rpc_call->proc_info->name;
+                       procname = (char *)rpc_call->proc_info->name;
                }
                else {
-                       sprintf(procname_static, "proc-%u", rpc_call->proc);
-                       procname = procname_static;
+                       procname=ep_strdup_printf("proc-%u", rpc_call->proc);
                }
        }
        else {
 #if 0
                dissect_function = NULL;
 #endif
-               sprintf(procname_static, "proc-%u", rpc_call->proc);
-               procname = procname_static;
+               procname=ep_strdup_printf("proc-%u", rpc_call->proc);
        }
 
        if ( tree )
        {
+               proto_item *tmp_item;
+
                /* Put the program, version, and procedure into the tree. */
-               proto_tree_add_uint_format(tree, prog_id, tvb,
+               tmp_item=proto_tree_add_uint_format(tree, prog_id, tvb,
                        0, 0, rpc_call->prog, "Program: %s (%u)",
                        rpc_prog_name(rpc_call->prog), rpc_call->prog);
-               proto_tree_add_uint(tree, vers_id, tvb, 0, 0, rpc_call->vers);
-               proto_tree_add_uint_format(tree, proc_id, tvb,
+               PROTO_ITEM_SET_GENERATED(tmp_item);
+
+               tmp_item=proto_tree_add_uint(tree, vers_id, tvb, 0, 0, rpc_call->vers);
+               PROTO_ITEM_SET_GENERATED(tmp_item);
+
+               tmp_item=proto_tree_add_uint_format(tree, proc_id, tvb,
                        0, 0, rpc_call->proc, "Procedure: %s (%u)",
                        procname, rpc_call->proc);
+               PROTO_ITEM_SET_GENERATED(tmp_item);
        }
 
        if (dissect_function == NULL) {
@@ -1615,10 +1624,8 @@ dissect_rpc_continuation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        proto_item *rpc_item;
        proto_tree *rpc_tree;
 
-       if (check_col(pinfo->cinfo, COL_PROTOCOL))
-               col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPC");
-       if (check_col(pinfo->cinfo, COL_INFO))
-               col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPC");
+       col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
 
        if (tree) {
                rpc_item = proto_tree_add_item(tree, proto_rpc, tvb, 0, -1,
@@ -1628,13 +1635,52 @@ dissect_rpc_continuation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        }
 }
 
+
+/**
+ *  Produce a dummy RPC program entry for the given RPC program key
+ *  and version values.
+ */
+
+static void 
+make_fake_rpc_prog_if_needed (rpc_prog_info_key *prpc_prog_key, guint prog_ver)
+{
+
+       rpc_prog_info_value *rpc_prog = NULL;
+
+       /* sanity check: no one uses versions > 10 */
+       if(prog_ver>10){
+               return;
+       }
+
+       if( (rpc_prog = g_hash_table_lookup(rpc_progs, prpc_prog_key)) == NULL) {
+               /* ok this is not a known rpc program so we
+                * will have to fake it.
+                */
+               int proto_rpc_unknown_program;
+               char *NAME, *Name, *name;
+               static const vsff unknown_proc[] = {
+                       { 0,"NULL",NULL,NULL },
+                       { 0,NULL,NULL,NULL }
+               };
+
+               NAME = g_strdup_printf("Unknown RPC Program:%d",prpc_prog_key->prog);
+               Name = g_strdup_printf("RPC:%d",prpc_prog_key->prog);
+               name = g_strdup_printf("rpc%d",prpc_prog_key->prog);
+               proto_rpc_unknown_program = proto_register_protocol(NAME, Name, name);
+
+               rpc_init_prog(proto_rpc_unknown_program, prpc_prog_key->prog, ett_rpc_unknown_program);
+               rpc_init_proc_table(prpc_prog_key->prog, prog_ver, unknown_proc, hf_rpc_procedure);
+
+       }
+}
+
+
 static gboolean
 dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     tvbuff_t *frag_tvb, fragment_data *ipfd_head, gboolean is_tcp,
     guint32 rpc_rm, gboolean first_pdu)
 {
        guint32 msg_type;
-       rpc_call_info_key rpc_call_key;
        rpc_call_info_value *rpc_call = NULL;
        rpc_prog_info_value *rpc_prog = NULL;
        rpc_prog_info_key rpc_prog_key;
@@ -1658,8 +1704,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
        const char *msg_type_name = NULL;
        const char *progname = NULL;
-       const char *procname = NULL;
-       static char procname_static[20];
+       char *procname = NULL;
 
        unsigned int vers_low;
        unsigned int vers_high;
@@ -1673,7 +1718,6 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        proto_tree *ptree = NULL;
        int offset = (is_tcp && tvb == frag_tvb) ? 4 : 0;
 
-       rpc_call_info_key       *new_rpc_call_key;
        rpc_proc_info_key       key;
        rpc_proc_info_value     *value = NULL;
        conversation_t* conversation;
@@ -1681,7 +1725,9 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        nstime_t ns;
 
        dissect_function_t *dissect_function = NULL;
-       gboolean dissect_rpc = TRUE;
+       gboolean dissect_rpc_flag = TRUE;
+
+       rpc_conv_info_t *rpc_conv_info=NULL;
 
 
        /*
@@ -1732,41 +1778,22 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                }
                /* let the user be able to weaken the heuristics if he need
                 * to look at proprietary protocols not known
-                * to ethereal.
+                * to wireshark.
                 */
                if(rpc_dissect_unknown_programs){
-                       /* if the user has specified that he wants to try to 
+                       guint32 version;
+
+                       /* if the user has specified that he wants to try to
                         * dissect even completely unknown RPC program numbers
                         * then let him do that.
-                        * In this case we only check that the program number 
+                        * In this case we only check that the program number
                         * is neither 0 nor -1 which is better than nothing.
                         */
                        if(rpc_prog_key.prog==0 || rpc_prog_key.prog==0xffffffff){
                                return FALSE;
                        }
-                       if( (rpc_prog = g_hash_table_lookup(rpc_progs, &rpc_prog_key)) == NULL) {
-                               /* ok this is not a known rpc program so we
-                                * will have to fake it.
-                                */
-                               int proto_rpc_unknown_program;
-                               char *NAME, *Name, *name;
-                               static const vsff unknown_proc[] = {
-                                       { 0,"NULL",NULL,NULL },
-                                       { 0,NULL,NULL,NULL }
-                               };
-
-                               NAME=g_malloc(36);
-                               Name=g_malloc(32);
-                               name=g_malloc(32);
-                               sprintf(NAME, "Unknown RPC Program:%d",rpc_prog_key.prog);
-                               sprintf(Name, "RPC:%d",rpc_prog_key.prog);
-                               sprintf(name, "rpc%d",rpc_prog_key.prog);
-                               proto_rpc_unknown_program = proto_register_protocol(NAME, Name, name);
-
-                               rpc_init_prog(proto_rpc_unknown_program, rpc_prog_key.prog, ett_rpc_unknown_program);
-                               rpc_init_proc_table(rpc_prog_key.prog, tvb_get_ntohl(tvb, offset + 16), unknown_proc, hf_rpc_procedure);
-
-                       }
+                       version=tvb_get_ntohl(tvb, offset+16);
+                       make_fake_rpc_prog_if_needed (&rpc_prog_key, version);
                }
                if( (rpc_prog = g_hash_table_lookup(rpc_progs, &rpc_prog_key)) == NULL) {
                        /* They're not, so it's probably not an RPC call. */
@@ -1815,16 +1842,55 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                           so we can't check for a reply to that call. */
                        return FALSE;
                }
+               /*
+                * Do we already have a state structure for this conv
+                */
+               rpc_conv_info = conversation_get_proto_data(conversation, proto_rpc);
+               if (!rpc_conv_info) {
+                       /* No.  Attach that information to the conversation, and add
+                        * it to the list of information structures.
+                        */
+                       rpc_conv_info = se_alloc(sizeof(rpc_conv_info_t));
+                       rpc_conv_info->xids=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rpc_xids");
+
+                       conversation_add_proto_data(conversation, proto_rpc, rpc_conv_info);
+               }
 
                /* The XIDs of the call and reply must match. */
-               rpc_call_key.xid = tvb_get_ntohl(tvb, offset + 0);
-               rpc_call_key.conversation = conversation;
-               rpc_call = g_hash_table_lookup(rpc_calls, &rpc_call_key);
+               xid = tvb_get_ntohl(tvb, offset + 0);
+               rpc_call = se_tree_lookup32(rpc_conv_info->xids, xid);
                if (rpc_call == NULL) {
                        /* The XID doesn't match a call from that
                           conversation, so it's probably not an RPC reply. */
-                       return FALSE;
+
+                       /* unless we're permitted to scan for embedded records
+                        * and this is a connection-oriented transport, give up */
+                       if ((! rpc_find_fragment_start) || (pinfo->ptype != PT_TCP)) {
+                               return FALSE;
+                       }
+
+                       /* in parse-partials, so define a dummy conversation for this reply */
+                       rpc_call = se_alloc(sizeof(rpc_call_info_value));
+                       rpc_call->req_num = 0;
+                       rpc_call->rep_num = pinfo->fd->num;
+                       rpc_call->prog = 0;
+                       rpc_call->vers = 0;
+                       rpc_call->proc = 0;
+                       rpc_call->private_data = NULL;
+                       rpc_call->xid = xid;
+                       rpc_call->flavor = FLAVOR_NOT_GSSAPI;  /* total punt */
+                       rpc_call->gss_proc = 0;
+                       rpc_call->gss_svc = 0;
+                       rpc_call->proc_info = value;
+                       rpc_call->req_time = pinfo->fd->abs_ts;
+
+                       /* store it */
+                       se_tree_insert32(rpc_conv_info->xids, xid, (void *)rpc_call);
+
+                       /* and fake up a matching program */
+                       rpc_prog_key.prog = rpc_call->prog;
                }
+
                /* pass rpc_info to subdissectors */
                rpc_call->request=FALSE;
                pinfo->private_data=rpc_call;
@@ -1855,8 +1921,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                }
        }
 
-       if (check_col(pinfo->cinfo, COL_PROTOCOL))
-               col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPC");
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPC");
 
        if (tree) {
                rpc_item = proto_tree_add_item(tree, proto_rpc, tvb, 0, -1,
@@ -1908,11 +1973,9 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                "Program: %s (%u)", progname, prog);
                }
 
-               if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
-                       /* Set the protocol name to the underlying
-                          program name. */
-                       col_set_str(pinfo->cinfo, COL_PROTOCOL, progname);
-               }
+               /* Set the protocol name to the underlying
+                  program name. */
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, progname);
 
                vers = tvb_get_ntohl(tvb, offset+8);
                if (rpc_tree) {
@@ -1928,7 +1991,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
                if ((value = g_hash_table_lookup(rpc_procs,&key)) != NULL) {
                        dissect_function = value->dissect_call;
-                       procname = value->name;
+                       procname = (char *)value->name;
                }
                else {
                        /* happens only with strange program versions or
@@ -1936,8 +1999,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 #if 0
                        dissect_function = NULL;
 #endif
-                       sprintf(procname_static, "proc-%u", proc);
-                       procname = procname_static;
+                       procname=ep_strdup_printf("proc-%u", proc);
                }
 
                /* Check for RPCSEC_GSS and AUTH_GSSAPI */
@@ -1975,9 +2037,9 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        if (tvb_get_ntohl(tvb, offset+28)) {
                                                flavor = FLAVOR_AUTHGSSAPI_MSG;
                                                gss_proc = proc;
-                                               procname =
-                                                   match_strval(gss_proc,
-                                                   rpc_authgssapi_proc);
+                                               procname = (char *)
+                                                   val_to_str(gss_proc,
+                                                   rpc_authgssapi_proc, "Unknown (%d)");
                                        } else {
                                                flavor = FLAVOR_AUTHGSSAPI;
                                        }
@@ -1999,16 +2061,18 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                "Procedure: %s (%u)", procname, proc);
                }
 
-               if (check_col(pinfo->cinfo, COL_INFO)) {
-                       if (first_pdu)
-                               col_clear(pinfo->cinfo, COL_INFO);
-                       else
-                               col_append_fstr(pinfo->cinfo, COL_INFO, "  ; ");
+               /* Print the program version, procedure name, and message type (call or reply). */
+               if (first_pdu)
+                       col_clear(pinfo->cinfo, COL_INFO);
+               else
+                       col_append_str(pinfo->cinfo, COL_INFO, "  ; ");
+               /* Special case for NFSv4 - if the type is COMPOUND, do not print the procedure name */
+               if (vers==4 && prog==NFS_PROGRAM && !strcmp(procname, "COMPOUND"))
+                       col_append_fstr(pinfo->cinfo, COL_INFO,"V%u %s", vers,
+                                       msg_type_name);
+               else
                        col_append_fstr(pinfo->cinfo, COL_INFO,"V%u %s %s",
-                               vers,
-                               procname,
-                               msg_type_name);
-               }
+                                       vers, procname, msg_type_name);
 
                /* Keep track of the address whence the call came, and the
                   port to which the call is being sent, so that we can
@@ -2058,18 +2122,28 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                    0, NO_ADDR2|NO_PORT2);
                        }
                }
+               /*
+                * Do we already have a state structure for this conv
+                */
+               rpc_conv_info = conversation_get_proto_data(conversation, proto_rpc);
+               if (!rpc_conv_info) {
+                       /* No.  Attach that information to the conversation, and add
+                        * it to the list of information structures.
+                        */
+                       rpc_conv_info = se_alloc(sizeof(rpc_conv_info_t));
+                       rpc_conv_info->xids=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rpc_xids");
+
+                       conversation_add_proto_data(conversation, proto_rpc, rpc_conv_info);
+               }
+
 
                /* Make the dissector for this conversation the non-heuristic
                   RPC dissector. */
                conversation_set_dissector(conversation,
                        (pinfo->ptype == PT_TCP) ? rpc_tcp_handle : rpc_handle);
 
-               /* prepare the key data */
-               rpc_call_key.xid = xid;
-               rpc_call_key.conversation = conversation;
-
                /* look up the request */
-               rpc_call = g_hash_table_lookup(rpc_calls, &rpc_call_key);
+               rpc_call = se_tree_lookup32(rpc_conv_info->xids, xid);
                if (rpc_call) {
                        /* We've seen a request with this XID, with the same
                           source and destination, before - but was it
@@ -2077,19 +2151,16 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        if (pinfo->fd->num != rpc_call->req_num) {
                                /* No, so it's a duplicate request.
                                   Mark it as such. */
-                               if (check_col(pinfo->cinfo, COL_INFO)) {
-                                       col_prepend_fstr(pinfo->cinfo, COL_INFO,
-                                               "[RPC retransmission of #%d]", rpc_call->req_num);
-                               }
-                               proto_tree_add_item(rpc_tree,
-                                       hf_rpc_dup, tvb, 0,0, TRUE);
-                               proto_tree_add_uint(rpc_tree,
-                                       hf_rpc_call_dup, tvb, 0,0, rpc_call->req_num);
+                               col_prepend_fstr(pinfo->cinfo, COL_INFO,
+                                                "[RPC retransmission of #%d]",
+                                                rpc_call->req_num);
+                               proto_tree_add_item(rpc_tree, hf_rpc_dup, tvb,
+                                                   0, 0, TRUE);
+                               proto_tree_add_uint(rpc_tree, hf_rpc_call_dup,
+                                                   tvb, 0,0, rpc_call->req_num);
                        }
                        if(rpc_call->rep_num){
-                               if (check_col(pinfo->cinfo, COL_INFO)) {
-                                       col_append_fstr(pinfo->cinfo, COL_INFO," (Reply In %d)", rpc_call->rep_num);
-                               }
+                               col_append_fstr(pinfo->cinfo, COL_INFO," (Reply In %d)", rpc_call->rep_num);
                        }
                } else {
                        /* Prepare the value data.
@@ -2097,9 +2168,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                           frame numbers are 1-origin, so we use 0
                           to mean "we don't yet know in which frame
                           the reply for this call appears". */
-                       new_rpc_call_key = g_mem_chunk_alloc(rpc_call_info_key_chunk);
-                       *new_rpc_call_key = rpc_call_key;
-                       rpc_call = g_mem_chunk_alloc(rpc_call_info_value_chunk);
+                       rpc_call = se_alloc(sizeof(rpc_call_info_value));
                        rpc_call->req_num = pinfo->fd->num;
                        rpc_call->rep_num = 0;
                        rpc_call->prog = prog;
@@ -2111,19 +2180,20 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        rpc_call->gss_proc = gss_proc;
                        rpc_call->gss_svc = gss_svc;
                        rpc_call->proc_info = value;
-                       rpc_call->req_time.secs=pinfo->fd->abs_secs;
-                       rpc_call->req_time.nsecs=pinfo->fd->abs_usecs*1000;
+                       rpc_call->req_time = pinfo->fd->abs_ts;
 
                        /* store it */
-                       g_hash_table_insert(rpc_calls, new_rpc_call_key,
-                           rpc_call);
+                       se_tree_insert32(rpc_conv_info->xids, xid, (void *)rpc_call);
                }
 
                if(rpc_call && rpc_call->rep_num){
-                       proto_tree_add_uint_format(rpc_tree, hf_rpc_repframe,
+                       proto_item *tmp_item;
+
+                       tmp_item=proto_tree_add_uint_format(rpc_tree, hf_rpc_reqframe,
                            tvb, 0, 0, rpc_call->rep_num,
                            "The reply to this request is in frame %u",
                            rpc_call->rep_num);
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
                }
 
                offset += 16;
@@ -2152,19 +2222,17 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                if (rpc_call->proc_info != NULL) {
                        dissect_function = rpc_call->proc_info->dissect_reply;
                        if (rpc_call->proc_info->name != NULL) {
-                               procname = rpc_call->proc_info->name;
+                               procname = (char *)rpc_call->proc_info->name;
                        }
                        else {
-                               sprintf(procname_static, "proc-%u", proc);
-                               procname = procname_static;
+                               procname=ep_strdup_printf("proc-%u", proc);
                        }
                }
                else {
 #if 0
                        dissect_function = NULL;
 #endif
-                       sprintf(procname_static, "proc-%u", proc);
-                       procname = procname_static;
+                       procname=ep_strdup_printf("proc-%u", proc);
                }
 
                /*
@@ -2175,7 +2243,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                 * AUTH_GSSAPI procname.
                 */
                if (flavor == FLAVOR_AUTHGSSAPI_MSG) {
-                       procname = match_strval(gss_proc, rpc_authgssapi_proc);
+                       procname = (char *)match_strval(gss_proc, rpc_authgssapi_proc);
                }
 
                rpc_prog_key.prog = prog;
@@ -2191,33 +2259,37 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        ett = rpc_prog->ett;
                        progname = rpc_prog->progname;
 
-                       if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
-                               /* Set the protocol name to the underlying
-                                  program name. */
-                               col_set_str(pinfo->cinfo, COL_PROTOCOL, progname);
-                       }
+                       /* Set the protocol name to the underlying
+                          program name. */
+                       col_set_str(pinfo->cinfo, COL_PROTOCOL, progname);
                }
 
-               if (check_col(pinfo->cinfo, COL_INFO)) {
-                       if (first_pdu)
-                               col_clear(pinfo->cinfo, COL_INFO);
-                       else
-                               col_append_fstr(pinfo->cinfo, COL_INFO, "  ; ");
+               /* Print the program version, procedure name, and message type (call or reply). */
+               if (first_pdu)
+                       col_clear(pinfo->cinfo, COL_INFO);
+               else
+                       col_append_str(pinfo->cinfo, COL_INFO, "  ; ");
+               /* Special case for NFSv4 - if the type is COMPOUND, do not print the procedure name */
+               if (vers==4 && prog==NFS_PROGRAM && !strcmp(procname, "COMPOUND"))
+                       col_append_fstr(pinfo->cinfo, COL_INFO,"V%u %s",
+                                       vers, msg_type_name);
+               else
                        col_append_fstr(pinfo->cinfo, COL_INFO,"V%u %s %s",
-                               vers,
-                               procname,
-                               msg_type_name);
-               }
+                                       vers, procname, msg_type_name);
 
                if (rpc_tree) {
-                       proto_tree_add_uint_format(rpc_tree,
+                       proto_item *tmp_item;
+                       tmp_item=proto_tree_add_uint_format(rpc_tree,
                                hf_rpc_program, tvb, 0, 0, prog,
                                "Program: %s (%u)", progname, prog);
-                       proto_tree_add_uint(rpc_tree,
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
+                       tmp_item=proto_tree_add_uint(rpc_tree,
                                hf_rpc_programversion, tvb, 0, 0, vers);
-                       proto_tree_add_uint_format(rpc_tree,
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
+                       tmp_item=proto_tree_add_uint_format(rpc_tree,
                                hf_rpc_procedure, tvb, 0, 0, proc,
                                "Procedure: %s (%u)", procname, proc);
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
                }
 
                reply_state = tvb_get_ntohl(tvb,offset+0);
@@ -2229,22 +2301,20 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
                /* Indicate the frame to which this is a reply. */
                if(rpc_call && rpc_call->req_num){
-                       proto_tree_add_uint_format(rpc_tree, hf_rpc_reqframe,
+                       proto_item *tmp_item;
+
+                       tmp_item=proto_tree_add_uint_format(rpc_tree, hf_rpc_repframe,
                            tvb, 0, 0, rpc_call->req_num,
                            "This is a reply to a request in frame %u",
                            rpc_call->req_num);
-                       ns.secs= pinfo->fd->abs_secs-rpc_call->req_time.secs;
-                       ns.nsecs=pinfo->fd->abs_usecs*1000-rpc_call->req_time.nsecs;
-                       if(ns.nsecs<0){
-                               ns.nsecs+=1000000000;
-                               ns.secs--;
-                       }
-                       proto_tree_add_time(rpc_tree, hf_rpc_time, tvb, offset, 0,
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
+
+                       nstime_delta(&ns, &pinfo->fd->abs_ts, &rpc_call->req_time);
+                       tmp_item=proto_tree_add_time(rpc_tree, hf_rpc_time, tvb, offset, 0,
                                &ns);
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
 
-                       if (check_col(pinfo->cinfo, COL_INFO)) {
-                               col_append_fstr(pinfo->cinfo, COL_INFO," (Call In %d)", rpc_call->req_num);
-                       }
+                       col_append_fstr(pinfo->cinfo, COL_INFO," (Call In %d)", rpc_call->req_num);
                }
 
 
@@ -2257,16 +2327,19 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        /* We have seen a reply to this call - but was it
                           *this* reply? */
                        if (rpc_call->rep_num != pinfo->fd->num) {
+                               proto_item *tmp_item;
+
                                /* No, so it's a duplicate reply.
                                   Mark it as such. */
-                               if (check_col(pinfo->cinfo, COL_INFO)) {
-                                       col_prepend_fstr(pinfo->cinfo, COL_INFO,
+                               col_prepend_fstr(pinfo->cinfo, COL_INFO,
                                                "[RPC duplicate of #%d]", rpc_call->rep_num);
-                               }
-                               proto_tree_add_item(rpc_tree,
+                               tmp_item=proto_tree_add_item(rpc_tree,
                                        hf_rpc_dup, tvb, 0,0, TRUE);
-                               proto_tree_add_uint(rpc_tree,
+                               PROTO_ITEM_SET_GENERATED(tmp_item);
+
+                               tmp_item=proto_tree_add_uint(rpc_tree,
                                        hf_rpc_reply_dup, tvb, 0,0, rpc_call->rep_num);
+                               PROTO_ITEM_SET_GENERATED(tmp_item);
                        }
                }
 
@@ -2303,7 +2376,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                 * There's no protocol reply, so don't
                                 * try to dissect it.
                                 */
-                               dissect_rpc = FALSE;
+                               dissect_rpc_flag = FALSE;
                                break;
 
                        default:
@@ -2311,7 +2384,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                 * There's no protocol reply, so don't
                                 * try to dissect it.
                                 */
-                               dissect_rpc = FALSE;
+                               dissect_rpc_flag = FALSE;
                                break;
                        }
                        break;
@@ -2351,7 +2424,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                         * There's no protocol reply, so don't
                         * try to dissect it.
                         */
-                       dissect_rpc = FALSE;
+                       dissect_rpc_flag = FALSE;
                        break;
 
                default:
@@ -2360,7 +2433,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                         * no clue what's going on; don't try to dissect
                         * the protocol reply.
                         */
-                       dissect_rpc = FALSE;
+                       dissect_rpc_flag = FALSE;
                        break;
                }
                break; /* end of RPC reply */
@@ -2375,20 +2448,29 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
        /* now we know, that RPC was shorter */
        if (rpc_item) {
+               if (offset < 0)
+                       THROW(ReportedBoundsError);
                tvb_ensure_bytes_exist(tvb, offset, 0);
                proto_item_set_end(rpc_item, tvb, offset);
        }
 
-       if (!dissect_rpc) {
+       if (!dissect_rpc_flag) {
                /*
                 * There's no RPC call or reply here; just dissect
                 * whatever's left as data.
                 */
                call_dissector(data_handle,
-                   tvb_new_subset(tvb, offset, -1, -1), pinfo, rpc_tree);
+                   tvb_new_subset_remaining(tvb, offset), pinfo, rpc_tree);
                return TRUE;
        }
 
+       /* we must queue this packet to the tap system before we actually
+          call the subdissectors since short packets (i.e. nfs read reply)
+          will cause an exception and execution would never reach the call
+          to tap_queue_packet() in that case
+       */
+       tap_queue_packet(rpc_tap, pinfo, rpc_call);
+
        /* create here the program specific sub-tree */
        if (tree && (flavor != FLAVOR_AUTHGSSAPI_MSG)) {
                pitem = proto_tree_add_item(tree, proto_id, tvb, offset, -1,
@@ -2398,9 +2480,12 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                }
 
                if (ptree) {
-                       proto_tree_add_uint(ptree,
+                       proto_item *tmp_item;
+
+                       tmp_item=proto_tree_add_uint(ptree,
                                hf_rpc_programversion, tvb, 0, 0, vers);
-                       if (rpc_prog->procedure_hfs->len > vers)
+                       PROTO_ITEM_SET_GENERATED(tmp_item);
+                       if (rpc_prog && (rpc_prog->procedure_hfs->len > vers) )
                                procedure_hf = g_array_index(rpc_prog->procedure_hfs, int, vers);
                        else {
                                /*
@@ -2409,28 +2494,30 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                procedure_hf = 0;
                        }
                        if (procedure_hf != 0 && procedure_hf != -1) {
-                               proto_tree_add_uint(ptree,
+                               tmp_item=proto_tree_add_uint(ptree,
                                        procedure_hf, tvb, 0, 0, proc);
+                               PROTO_ITEM_SET_GENERATED(tmp_item);
                        } else {
-                               proto_tree_add_uint_format(ptree,
+                               tmp_item=proto_tree_add_uint_format(ptree,
                                        hf_rpc_procedure, tvb, 0, 0, proc,
                                        "Procedure: %s (%u)", procname, proc);
+                               PROTO_ITEM_SET_GENERATED(tmp_item);
                        }
                }
        }
 
-       /* we must queue this packet to the tap system before we actually
-          call the subdissectors since short packets (i.e. nfs read reply)
-          will cause an exception and execution would never reach the call
-          to tap_queue_packet() in that case
-       */
-       tap_queue_packet(rpc_tap, pinfo, rpc_call);
-
        /* proto==0 if this is an unknown program */
        if( (proto==0) || !proto_is_protocol_enabled(proto)){
                dissect_function = NULL;
        }
 
+        /*
+         * Don't call any subdissector if we have no more date to dissect.
+         */
+        if (tvb_length_remaining(tvb, offset) == 0) {
+                return TRUE;
+        }
+
        /*
         * Handle RPCSEC_GSS and AUTH_GSSAPI specially.
         */
@@ -2516,16 +2603,12 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                 * protocol and info fields to indicate that this is
                 * an RPC auth level message, then process the args.
                 */
-               if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
-                       col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPC");
-               }
-               if (check_col(pinfo->cinfo, COL_INFO)) {
-                       col_clear(pinfo->cinfo, COL_INFO);
-                       col_append_fstr(pinfo->cinfo, COL_INFO,
-                           "%s %s XID 0x%x",
-                           match_strval(gss_proc, rpc_authgssapi_proc),
-                           msg_type_name, xid);
-               }
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPC");
+               col_clear(pinfo->cinfo, COL_INFO);
+               col_append_fstr(pinfo->cinfo, COL_INFO,
+                               "%s %s XID 0x%x",
+                               val_to_str(gss_proc, rpc_authgssapi_proc, "Unknown (%d)"),
+                               msg_type_name, xid);
 
                switch (gss_proc) {
 
@@ -2533,10 +2616,10 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                case AUTH_GSSAPI_CONTINUE_INIT:
                case AUTH_GSSAPI_MSG:
                        if (msg_type == RPC_CALL) {
-                           offset = dissect_rpc_authgssapi_initarg(tvb, 
+                           offset = dissect_rpc_authgssapi_initarg(tvb,
                                rpc_tree, offset, pinfo);
                        } else {
-                           offset = dissect_rpc_authgssapi_initres(tvb, 
+                           offset = dissect_rpc_authgssapi_initres(tvb,
                                rpc_tree, offset, pinfo);
                        }
                        break;
@@ -2565,40 +2648,29 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                break;
        }
 
-        if (tvb_length_remaining(tvb, offset) > 0) {  
-          /* 
-           * dissect any remaining bytes (incomplete dissection) as pure 
-           * data in the ptree 
+        if (tvb_length_remaining(tvb, offset) > 0) {
+          /*
+           * dissect any remaining bytes (incomplete dissection) as pure
+           * data in the ptree
            */
 
           call_dissector(data_handle,
-              tvb_new_subset(tvb, offset, -1, -1), pinfo, ptree);
+              tvb_new_subset_remaining(tvb, offset), pinfo, ptree);
         }
 
        /* XXX this should really loop over all fhandles registred for the frame */
        if(nfs_fhandle_reqrep_matching){
-               nfs_fhandle_data_t *fhd;
                switch (msg_type) {
                case RPC_CALL:
                        if(rpc_call && rpc_call->rep_num){
-                               fhd=(nfs_fhandle_data_t *)g_hash_table_lookup(
-                                       nfs_fhandle_frame_table,
-                                       GINT_TO_POINTER(rpc_call->rep_num));
-                               if(fhd){
-                                       dissect_fhandle_hidden(pinfo,
-                                               ptree, fhd);
-                               }
+                               dissect_fhandle_hidden(pinfo,
+                                               ptree, rpc_call->rep_num);
                        }
                        break;
                case RPC_REPLY:
                        if(rpc_call && rpc_call->req_num){
-                               fhd=(nfs_fhandle_data_t *)g_hash_table_lookup(
-                                       nfs_fhandle_frame_table,
-                                       GINT_TO_POINTER(rpc_call->req_num));
-                               if(fhd){
-                                       dissect_fhandle_hidden(pinfo,
-                                               ptree, fhd);
-                               }
+                               dissect_fhandle_hidden(pinfo,
+                                               ptree, rpc_call->req_num);
                        }
                        break;
                }
@@ -2630,13 +2702,12 @@ dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 static GHashTable *rpc_fragment_table = NULL;
 
 static GHashTable *rpc_reassembly_table = NULL;
-static GMemChunk *rpc_fragment_key_chunk = NULL;
-static int rpc_fragment_init_count = 200;
 
 typedef struct _rpc_fragment_key {
        guint32 conv_id;
        guint32 seq;
        guint32 offset;
+       guint32 port;
        /* xxx */
        guint32 start_seq;
 } rpc_fragment_key;
@@ -2656,7 +2727,7 @@ rpc_fragment_equal(gconstpointer k1, gconstpointer k2)
        const rpc_fragment_key *key2 = (const rpc_fragment_key *)k2;
 
        return key1->conv_id == key2->conv_id &&
-           key1->seq == key2->seq;
+           key1->seq == key2->seq && key1->port == key2->port;
 }
 
 static void
@@ -2748,6 +2819,7 @@ call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo,
 {
        const char *saved_proto;
        volatile gboolean rpc_succeeded;
+       void *pd_save;
 
        /*
         * Catch the ReportedBoundsError exception; if
@@ -2762,6 +2834,7 @@ call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo,
         */
        saved_proto = pinfo->current_proto;
        rpc_succeeded = FALSE;
+       pd_save = pinfo->private_data;
        TRY {
                rpc_succeeded = (*dissector)(rec_tvb, pinfo, tree,
                    frag_tvb, ipfd_head, TRUE, rpc_rm, first_pdu);
@@ -2773,6 +2846,12 @@ call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo,
                show_reported_bounds_error(tvb, pinfo, tree);
                pinfo->current_proto = saved_proto;
 
+               /*  Restore the private_data structure in case one of the
+                *  called dissectors modified it (and, due to the exception,
+                *  was unable to restore it).
+                */
+               pinfo->private_data = pd_save;
+
                /*
                 * We treat this as a "successful" dissection of
                 * an RPC packet, as "dissect_rpc_message()"
@@ -2937,6 +3016,7 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
        }
        old_rfk.conv_id = conversation->index;
        old_rfk.seq = seq;
+        old_rfk.port = pinfo->srcport;
        rfk = g_hash_table_lookup(rpc_reassembly_table, &old_rfk);
 
        if (rfk == NULL) {
@@ -2974,9 +3054,10 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
                         * We must remember this fragment.
                         */
 
-                       rfk = g_mem_chunk_alloc(rpc_fragment_key_chunk);
+                       rfk = se_alloc(sizeof(rpc_fragment_key));
                        rfk->conv_id = conversation->index;
                        rfk->seq = seq;
+                       rfk->port = pinfo->srcport;
                        rfk->offset = 0;
                        rfk->start_seq = seq;
                        g_hash_table_insert(rpc_reassembly_table, rfk, rfk);
@@ -2994,29 +3075,37 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
                         * we've seen, and the "last fragment" bit wasn't
                         * set on it.
                         */
-                       DISSECTOR_ASSERT(ipfd_head == NULL);
-
-                       new_rfk = g_mem_chunk_alloc(rpc_fragment_key_chunk);
-                       new_rfk->conv_id = rfk->conv_id;
-                       new_rfk->seq = seq + len;
-                       new_rfk->offset = rfk->offset + len - 4;
-                       new_rfk->start_seq = rfk->start_seq;
-                       g_hash_table_insert(rpc_reassembly_table, new_rfk,
-                           new_rfk);
+                       if (ipfd_head == NULL) {
+                               new_rfk = se_alloc(sizeof(rpc_fragment_key));
+                               new_rfk->conv_id = rfk->conv_id;
+                               new_rfk->seq = seq + len;
+                               new_rfk->port = pinfo->srcport;
+                               new_rfk->offset = rfk->offset + len - 4;
+                               new_rfk->start_seq = rfk->start_seq;
+                               g_hash_table_insert(rpc_reassembly_table, new_rfk,
+                                       new_rfk);
 
-                       /*
-                        * This is part of a fragmented record,
-                        * but it's not the first part.
-                        * Show it as a record marker plus data, under
-                        * a top-level tree for this protocol.
-                        */
-                       make_frag_tree(frag_tvb, tree, proto, ett,rpc_rm);
+                               /*
+                                * This is part of a fragmented record,
+                                * but it's not the first part.
+                                * Show it as a record marker plus data, under
+                                * a top-level tree for this protocol.
+                                */
+                               make_frag_tree(frag_tvb, tree, proto, ett,rpc_rm);
 
-                       /*
-                        * No more processing need be done, as we don't
-                        * have a complete record.
-                        */
-                       return len;
+                               /*
+                                * No more processing need be done, as we don't
+                                * have a complete record.
+                                */
+                               return len;
+                       } else {
+                               /* oddly, we have a first fragment, not marked as last,
+                                * but which the defragmenter thinks is complete.
+                                * So rather than creating a fragment reassembly tree,
+                                * we simply throw away the partial fragment structure
+                                * and fall though to our "sole fragment" processing below.
+                                */
+                       }
                }
 
                /*
@@ -3053,9 +3142,10 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
                         * RPC fragments aren't guaranteed to be provided
                         * in order, either.
                         */
-                       new_rfk = g_mem_chunk_alloc(rpc_fragment_key_chunk);
+                       new_rfk = se_alloc(sizeof(rpc_fragment_key));
                        new_rfk->conv_id = rfk->conv_id;
                        new_rfk->seq = seq + len;
+                        new_rfk->port = pinfo->srcport;
                        new_rfk->offset = rfk->offset + len - 4;
                        new_rfk->start_seq = rfk->start_seq;
                        g_hash_table_insert(rpc_reassembly_table, new_rfk,
@@ -3118,15 +3208,9 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
                 * Create a new TVB structure for
                 * defragmented data.
                 */
-               rec_tvb = tvb_new_real_data(ipfd_head->data,
+               rec_tvb = tvb_new_child_real_data(tvb, ipfd_head->data,
                    ipfd_head->datalen, ipfd_head->datalen);
 
-               /*
-                * Add this tvb as a child to the original
-                * one.
-                */
-               tvb_set_child_real_data_tvbuff(tvb, rec_tvb);
-
                /*
                 * Add defragmented data to the data source list.
                 */
@@ -3141,7 +3225,202 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
            frag_tvb, dissector, ipfd_head, rpc_rm, first_pdu))
                return 0;       /* not RPC */
        return len;
-}
+}  /* end of dissect_rpc_fragment() */
+
+/**
+ * Scans tvb, starting at given offset, to see if we can find
+ * what looks like a valid RPC-over-TCP reply header.
+ *
+ * @param tvb Buffer to inspect for RPC reply header.
+ * @param offset Offset to begin search of tvb at.
+ *
+ * @return -1 if no reply header found, else offset to start of header
+ *         (i.e., to the RPC record mark field).
+ */
+
+static int
+find_rpc_over_tcp_reply_start(tvbuff_t *tvb, int offset)
+{
+
+       /*
+        * Looking for partial header sequence.  From beginning of
+        * stream-style header, including "record mark", full ONC-RPC
+        * looks like:
+        *    BE int32    record mark (rfc 1831 sec. 10)
+        *    ?  int32    XID (rfc 1831 sec. 8)
+        *    BE int32    msg_type (ibid sec. 8, call = 0, reply = 1)
+        *
+        * -------------------------------------------------------------
+        * Then reply-specific fields are
+        *    BE int32    reply_stat (ibid, accept = 0, deny = 1)
+        *
+        * Then, assuming accepted,
+        *   opaque_auth
+        *    BE int32    auth_flavor (ibid, none = 0)
+        *    BE int32    ? auth_len (ibid, none = 0)
+        *
+        *    BE int32    accept_stat (ibid, success = 0, errs are 1..5 in rpc v2)
+        *
+        * -------------------------------------------------------------
+        * Or, call-specific fields are
+        *    BE int32    rpc_vers (rfc 1831 sec 8, always == 2)
+        *    BE int32    prog (NFS == 000186A3)
+        *    BE int32    prog_ver (NFS v2/3 == 2 or 3)
+        *    BE int32    proc_id (NFS, <= 256 ???)
+        *   opaque_auth
+        *    ...
+        */
+
+       /* Initially, we search only for something matching the template
+        * of a successful reply with no auth verifier.
+        * Our first qualification test is search for a string of zero bytes,
+        * corresponding the four guint32 values
+        *    reply_stat
+        *    auth_flavor
+        *    auth_len
+        *    accept_stat
+        *
+        * If this string of zeros matches, then we go back and check the
+        * preceding msg_type and record_mark fields.
+        */
+
+       const gint     cbZeroTail = 4 * 4;     /* four guint32s of zeros */
+       const gint     ibPatternStart = 3 * 4;    /* offset of zero fill from reply start */
+       const guint8 * pbWholeBuf;    /* all of tvb, from offset onwards */
+       const int      NoMatch = -1;
+
+       gint     ibSearchStart;       /* offset of search start, in case of false hits. */
+
+       const    guint8 * pbBuf;
+
+       gint     cbInBuf;       /* bytes in tvb, from offset onwards */
+
+       guint32  ulMsgType;
+       guint32  ulRecMark;
+
+       int      i;
+
+
+       cbInBuf = tvb_reported_length_remaining(tvb, offset);
+
+       /* start search at first possible location */
+       ibSearchStart = ibPatternStart;
+
+       if (cbInBuf < (cbZeroTail + ibSearchStart)) {
+               /* nothing to search, so claim no RPC */
+               return (NoMatch);
+       }
+
+       pbWholeBuf = tvb_get_ptr(tvb, offset, cbInBuf);
+       if (pbWholeBuf == NULL) {
+               /* probably never take this, as get_ptr seems to assert */
+               return (NoMatch);
+       }
+
+       while ((cbInBuf - ibSearchStart) > cbZeroTail) {
+               /* First test for long tail of zeros, starting at the back.
+                * A failure lets us skip the maximum possible buffer amount.
+                */
+               pbBuf = pbWholeBuf + ibSearchStart + cbZeroTail - 1;
+               for (i = cbZeroTail; i > 0;  i --)
+                       {
+                       if (*pbBuf != 0)
+                               {
+                               /* match failure.  Since we need N contiguous zeros,
+                                * we can increment next match start so zero testing
+                                * begins right after this failure spot.
+                                */
+                               ibSearchStart += i;
+                               pbBuf = NULL;
+                               break;
+                               }
+
+                       pbBuf --;
+                       }
+
+               if (pbBuf == NULL) {
+                       continue;
+               }
+
+               /* got a match in zero-fill region, verify reply ID and
+                * record mark fields */
+               ulMsgType = pntohl (pbWholeBuf + ibSearchStart - 4);
+               ulRecMark = pntohl (pbWholeBuf + ibSearchStart - ibPatternStart);
+
+               if ((ulMsgType == RPC_REPLY) &&
+                        ((ulRecMark & ~0x80000000) <= (unsigned) max_rpc_tcp_pdu_size)) {
+                       /* looks ok, try dissect */
+                       return (offset + ibSearchStart - ibPatternStart);
+               }
+
+               /* no match yet, nor egregious miss either.  Inch along to next try */
+               ibSearchStart ++;
+       }
+
+       return (NoMatch);
+
+}  /* end of find_rpc_over_tcp_reply_start() */
+
+/**
+ * Scans tvb for what looks like a valid RPC call / reply header.
+ * If found, calls standard dissect_rpc_fragment() logic to digest
+ * the (hopefully valid) fragment.
+ *
+ * With any luck, one invocation of this will be sufficient to get
+ * us back in alignment with the stream, and no further calls to
+ * this routine will be needed for a given conversation.  As if.  :-)
+ *
+ * Can return:
+ *       Same as dissect_rpc_fragment().  Will return zero (no frame)
+ *       if no valid RPC header is found.
+ */
+
+static int
+find_and_dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                             proto_tree *tree, rec_dissector_t dissector,
+                             gboolean is_heur,
+                             int proto, int ett, gboolean defragment)
+{
+
+       int   offReply;
+       int   len;
+
+
+       offReply = find_rpc_over_tcp_reply_start(tvb, offset);
+       if (offReply < 0) {
+               /* could search for request, but not needed (or testable) thus far */
+               return (0);    /* claim no RPC */
+       }
+
+       len = dissect_rpc_fragment(tvb, offReply,
+                                  pinfo, tree,
+                                  dissector, is_heur, proto, ett,
+                                  defragment,
+                                  TRUE /* force first-pdu state */);
+
+       /* misses are reported as-is */
+       if (len == 0)
+       {
+               return (0);
+       }
+
+       /* returning a non-zero length, correct it to reflect the extra offset
+        * we found necessary
+        */
+       if (len > 0) {
+               len += offReply - offset;
+       }
+       else {
+               /* negative length seems to only be used as a flag,
+                * don't mess it up until found necessary
+                */
+/*      len -= offReply - offset; */
+       }
+
+       return (len);
+
+}  /* end of find_and_dissect_rpc_fragment */
+
 
 /*
  * Can return:
@@ -3175,6 +3454,17 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                len = dissect_rpc_fragment(tvb, offset, pinfo, tree,
                    dissect_rpc_message, is_heur, proto_rpc, ett_rpc,
                    rpc_defragment, first_pdu);
+
+               if ((len == 0) && first_pdu && rpc_find_fragment_start) {
+                       /*
+                        * Try discarding some leading bytes from tvb, on assumption
+                        * that we are looking at the middle of a stream-based transfer
+                        */
+                       len = find_and_dissect_rpc_fragment(tvb, offset, pinfo, tree,
+                                dissect_rpc_message, is_heur, proto_rpc, ett_rpc,
+                                rpc_defragment);
+               }
+
                first_pdu = FALSE;
                if (len < 0) {
                        /*
@@ -3190,6 +3480,14 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        break;
                }
 
+               /*  Set a fence so whatever the subdissector put in the
+                *  Info column stays there.  This is useful when the
+                *  subdissector clears the column (which it might have to do
+                *  if it runs over some other protocol too) and there are
+                *  multiple PDUs in one frame.
+                */
+               col_set_fence(pinfo->cinfo, COL_INFO);
+
                /* PDU tracking
                  If the length indicates that the PDU continues beyond
                  the end of this tvb, then tell TCP about it so that it
@@ -3239,45 +3537,11 @@ dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 static void
 rpc_init_protocol(void)
 {
-       if (rpc_calls != NULL) {
-               g_hash_table_destroy(rpc_calls);
-               rpc_calls = NULL;
-       }
-       if (rpc_indir_calls != NULL) {
-               g_hash_table_destroy(rpc_indir_calls);
-               rpc_indir_calls = NULL;
-       }
-       if (rpc_call_info_key_chunk != NULL) {
-               g_mem_chunk_destroy(rpc_call_info_key_chunk);
-               rpc_call_info_key_chunk = NULL;
-       }
-       if (rpc_call_info_value_chunk != NULL) {
-               g_mem_chunk_destroy(rpc_call_info_value_chunk);
-               rpc_call_info_value_chunk = NULL;
-       }
-       if (rpc_fragment_key_chunk != NULL) {
-               g_mem_chunk_destroy(rpc_fragment_key_chunk);
-               rpc_fragment_key_chunk = NULL;
-       }
        if (rpc_reassembly_table != NULL) {
                g_hash_table_destroy(rpc_reassembly_table);
                rpc_reassembly_table = NULL;
        }
 
-       rpc_calls = g_hash_table_new(rpc_call_hash, rpc_call_equal);
-       rpc_indir_calls = g_hash_table_new(rpc_call_hash, rpc_call_equal);
-       rpc_call_info_key_chunk = g_mem_chunk_new("call_info_key_chunk",
-           sizeof(rpc_call_info_key),
-           200 * sizeof(rpc_call_info_key),
-           G_ALLOC_ONLY);
-       rpc_call_info_value_chunk = g_mem_chunk_new("call_info_value_chunk",
-           sizeof(rpc_call_info_value),
-           200 * sizeof(rpc_call_info_value),
-           G_ALLOC_ONLY);
-       rpc_fragment_key_chunk = g_mem_chunk_new("rpc_fragment_key_chunk",
-           sizeof(rpc_fragment_key),
-           rpc_fragment_init_count*sizeof(rpc_fragment_key),
-           G_ALLOC_ONLY);
        rpc_reassembly_table = g_hash_table_new(rpc_fragment_hash,
            rpc_fragment_equal);
 
@@ -3291,166 +3555,169 @@ proto_register_rpc(void)
        static hf_register_info hf[] = {
                { &hf_rpc_reqframe, {
                        "Request Frame", "rpc.reqframe", FT_FRAMENUM, BASE_NONE,
-                       NULL, 0, "Request Frame", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_repframe, {
                        "Reply Frame", "rpc.repframe", FT_FRAMENUM, BASE_NONE,
-                       NULL, 0, "Reply Frame", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_lastfrag, {
                        "Last Fragment", "rpc.lastfrag", FT_BOOLEAN, 32,
-                       &yesno, RPC_RM_LASTFRAG, "Last Fragment", HFILL }},
+                       TFS(&tfs_yes_no), RPC_RM_LASTFRAG, NULL, HFILL }},
                { &hf_rpc_fraglen, {
                        "Fragment Length", "rpc.fraglen", FT_UINT32, BASE_DEC,
-                       NULL, RPC_RM_FRAGLEN, "Fragment Length", HFILL }},
+                       NULL, RPC_RM_FRAGLEN, NULL, HFILL }},
                { &hf_rpc_xid, {
                        "XID", "rpc.xid", FT_UINT32, BASE_HEX,
-                       NULL, 0, "XID", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_msgtype, {
                        "Message Type", "rpc.msgtyp", FT_UINT32, BASE_DEC,
-                       VALS(rpc_msg_type), 0, "Message Type", HFILL }},
+                       VALS(rpc_msg_type), 0, NULL, HFILL }},
                { &hf_rpc_state_reply, {
                        "Reply State", "rpc.replystat", FT_UINT32, BASE_DEC,
-                       VALS(rpc_reply_state), 0, "Reply State", HFILL }},
+                       VALS(rpc_reply_state), 0, NULL, HFILL }},
                { &hf_rpc_state_accept, {
                        "Accept State", "rpc.state_accept", FT_UINT32, BASE_DEC,
-                       VALS(rpc_accept_state), 0, "Accept State", HFILL }},
+                       VALS(rpc_accept_state), 0, NULL, HFILL }},
                { &hf_rpc_state_reject, {
                        "Reject State", "rpc.state_reject", FT_UINT32, BASE_DEC,
-                       VALS(rpc_reject_state), 0, "Reject State", HFILL }},
+                       VALS(rpc_reject_state), 0, NULL, HFILL }},
                { &hf_rpc_state_auth, {
                        "Auth State", "rpc.state_auth", FT_UINT32, BASE_DEC,
-                       VALS(rpc_auth_state), 0, "Auth State", HFILL }},
+                       VALS(rpc_auth_state), 0, NULL, HFILL }},
                { &hf_rpc_version, {
                        "RPC Version", "rpc.version", FT_UINT32, BASE_DEC,
-                       NULL, 0, "RPC Version", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_version_min, {
                        "RPC Version (Minimum)", "rpc.version.min", FT_UINT32,
                        BASE_DEC, NULL, 0, "Program Version (Minimum)", HFILL }},
                { &hf_rpc_version_max, {
                        "RPC Version (Maximum)", "rpc.version.max", FT_UINT32,
-                       BASE_DEC, NULL, 0, "RPC Version (Maximum)", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_program, {
                        "Program", "rpc.program", FT_UINT32, BASE_DEC,
-                       NULL, 0, "Program", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_programversion, {
                        "Program Version", "rpc.programversion", FT_UINT32,
-                       BASE_DEC, NULL, 0, "Program Version", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_programversion_min, {
                        "Program Version (Minimum)", "rpc.programversion.min", FT_UINT32,
-                       BASE_DEC, NULL, 0, "Program Version (Minimum)", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_programversion_max, {
                        "Program Version (Maximum)", "rpc.programversion.max", FT_UINT32,
-                       BASE_DEC, NULL, 0, "Program Version (Maximum)", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_procedure, {
                        "Procedure", "rpc.procedure", FT_UINT32, BASE_DEC,
-                       NULL, 0, "Procedure", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_auth_flavor, {
                        "Flavor", "rpc.auth.flavor", FT_UINT32, BASE_DEC,
-                       VALS(rpc_auth_flavor), 0, "Flavor", HFILL }},
+                       VALS(rpc_auth_flavor), 0, NULL, HFILL }},
                { &hf_rpc_auth_length, {
                        "Length", "rpc.auth.length", FT_UINT32, BASE_DEC,
-                       NULL, 0, "Length", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_auth_stamp, {
                        "Stamp", "rpc.auth.stamp", FT_UINT32, BASE_HEX,
-                       NULL, 0, "Stamp", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_auth_uid, {
                        "UID", "rpc.auth.uid", FT_UINT32, BASE_DEC,
-                       NULL, 0, "UID", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_auth_gid, {
                        "GID", "rpc.auth.gid", FT_UINT32, BASE_DEC,
-                       NULL, 0, "GID", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_v, {
                        "GSS Version", "rpc.authgss.version", FT_UINT32,
-                       BASE_DEC, NULL, 0, "GSS Version", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_proc, {
                        "GSS Procedure", "rpc.authgss.procedure", FT_UINT32,
-                       BASE_DEC, VALS(rpc_authgss_proc), 0, "GSS Procedure", HFILL }},
+                       BASE_DEC, VALS(rpc_authgss_proc), 0, NULL, HFILL }},
                { &hf_rpc_authgss_seq, {
                        "GSS Sequence Number", "rpc.authgss.seqnum", FT_UINT32,
-                       BASE_DEC, NULL, 0, "GSS Sequence Number", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_svc, {
                        "GSS Service", "rpc.authgss.service", FT_UINT32,
-                       BASE_DEC, VALS(rpc_authgss_svc), 0, "GSS Service", HFILL }},
+                       BASE_DEC, VALS(rpc_authgss_svc), 0, NULL, HFILL }},
                { &hf_rpc_authgss_ctx, {
                        "GSS Context", "rpc.authgss.context", FT_BYTES,
-                       BASE_HEX, NULL, 0, "GSS Context", HFILL }},
+                       BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_major, {
                        "GSS Major Status", "rpc.authgss.major", FT_UINT32,
-                       BASE_DEC, NULL, 0, "GSS Major Status", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_minor, {
                        "GSS Minor Status", "rpc.authgss.minor", FT_UINT32,
-                       BASE_DEC, NULL, 0, "GSS Minor Status", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_window, {
                        "GSS Sequence Window", "rpc.authgss.window", FT_UINT32,
-                       BASE_DEC, NULL, 0, "GSS Sequence Window", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_token_length, {
                        "GSS Token Length", "rpc.authgss.token_length", FT_UINT32,
-                       BASE_DEC, NULL, 0, "GSS Token Length", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_data_length, {
                        "Length", "rpc.authgss.data.length", FT_UINT32,
-                       BASE_DEC, NULL, 0, "Length", HFILL }},
+                       BASE_DEC, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_data, {
                        "GSS Data", "rpc.authgss.data", FT_BYTES,
-                       BASE_HEX, NULL, 0, "GSS Data", HFILL }},
+                       BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgss_checksum, {
                        "GSS Checksum", "rpc.authgss.checksum", FT_BYTES,
-                       BASE_HEX, NULL, 0, "GSS Checksum", HFILL }},
+                       BASE_NONE, NULL, 0, NULL, HFILL }},
+               { &hf_rpc_authgss_token, {
+                       "GSS Token", "rpc.authgss.token", FT_BYTES,
+                       BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgssapi_v, {
                        "AUTH_GSSAPI Version", "rpc.authgssapi.version",
-                       FT_UINT32, BASE_DEC, NULL, 0, "AUTH_GSSAPI Version",
+                       FT_UINT32, BASE_DEC, NULL, 0, NULL,
                        HFILL }},
                { &hf_rpc_authgssapi_msg, {
                        "AUTH_GSSAPI Message", "rpc.authgssapi.message",
-                       FT_BOOLEAN, BASE_NONE, &yesno, 0, "AUTH_GSSAPI Message",
+                       FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0, NULL,
                        HFILL }},
                { &hf_rpc_authgssapi_msgv, {
                        "Msg Version", "rpc.authgssapi.msgversion",
-                       FT_UINT32, BASE_DEC, NULL, 0, "Msg Version",
+                       FT_UINT32, BASE_DEC, NULL, 0, NULL,
                        HFILL }},
                { &hf_rpc_authgssapi_handle, {
                        "Client Handle", "rpc.authgssapi.handle",
-                       FT_BYTES, BASE_HEX, NULL, 0, "Client Handle", HFILL }},
+                       FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authgssapi_isn, {
                        "Signed ISN", "rpc.authgssapi.isn",
-                       FT_BYTES, BASE_HEX, NULL, 0, "Signed ISN", HFILL }},
+                       FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authdes_namekind, {
                        "Namekind", "rpc.authdes.namekind", FT_UINT32, BASE_DEC,
-                       VALS(rpc_authdes_namekind), 0, "Namekind", HFILL }},
+                       VALS(rpc_authdes_namekind), 0, NULL, HFILL }},
                { &hf_rpc_authdes_netname, {
                        "Netname", "rpc.authdes.netname", FT_STRING,
-                       BASE_DEC, NULL, 0, "Netname", HFILL }},
+                       BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authdes_convkey, {
                        "Conversation Key (encrypted)", "rpc.authdes.convkey", FT_UINT32,
-                       BASE_HEX, NULL, 0, "Conversation Key (encrypted)", HFILL }},
+                       BASE_HEX, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authdes_window, {
                        "Window (encrypted)", "rpc.authdes.window", FT_UINT32,
                        BASE_HEX, NULL, 0, "Windows (encrypted)", HFILL }},
                { &hf_rpc_authdes_nickname, {
                        "Nickname", "rpc.authdes.nickname", FT_UINT32,
-                       BASE_HEX, NULL, 0, "Nickname", HFILL }},
+                       BASE_HEX, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authdes_timestamp, {
                        "Timestamp (encrypted)", "rpc.authdes.timestamp", FT_UINT32,
-                       BASE_HEX, NULL, 0, "Timestamp (encrypted)", HFILL }},
+                       BASE_HEX, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authdes_windowverf, {
                        "Window verifier (encrypted)", "rpc.authdes.windowverf", FT_UINT32,
-                       BASE_HEX, NULL, 0, "Window verifier (encrypted)", HFILL }},
+                       BASE_HEX, NULL, 0, NULL, HFILL }},
                { &hf_rpc_authdes_timeverf, {
                        "Timestamp verifier (encrypted)", "rpc.authdes.timeverf", FT_UINT32,
-                       BASE_HEX, NULL, 0, "Timestamp verifier (encrypted)", HFILL }},
+                       BASE_HEX, NULL, 0, NULL, HFILL }},
                { &hf_rpc_auth_machinename, {
                        "Machine Name", "rpc.auth.machinename", FT_STRING,
-                       BASE_DEC, NULL, 0, "Machine Name", HFILL }},
+                       BASE_NONE, NULL, 0, NULL, HFILL }},
                { &hf_rpc_dup, {
                        "Duplicate Call/Reply", "rpc.dup", FT_NONE, BASE_NONE,
-                       NULL, 0, "Duplicate Call/Reply", HFILL }},
+                       NULL, 0, NULL, HFILL }},
                { &hf_rpc_call_dup, {
-                       "Duplicate to the call in", "rpc.call.dup", FT_FRAMENUM, BASE_DEC,
+                       "Duplicate to the call in", "rpc.call.dup", FT_FRAMENUM, BASE_NONE,
                        NULL, 0, "This is a duplicate to the call in frame", HFILL }},
                { &hf_rpc_reply_dup, {
-                       "Duplicate to the reply in", "rpc.reply.dup", FT_FRAMENUM, BASE_DEC,
+                       "Duplicate to the reply in", "rpc.reply.dup", FT_FRAMENUM, BASE_NONE,
                        NULL, 0, "This is a duplicate to the reply in frame", HFILL }},
                { &hf_rpc_value_follows, {
                        "Value Follows", "rpc.value_follows", FT_BOOLEAN, BASE_NONE,
-                       &yesno, 0, "Value Follows", HFILL }},
+                       TFS(&tfs_yes_no), 0x0, NULL, HFILL }},
                { &hf_rpc_array_len, {
                        "num", "rpc.array.len", FT_UINT32, BASE_DEC,
                        NULL, 0, "Length of RPC array", HFILL }},
@@ -3481,11 +3748,15 @@ proto_register_rpc(void)
 
                { &hf_rpc_fragment,
                { "RPC Fragment", "rpc.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
-                       "RPC Fragment", HFILL }},
+                       NULL, HFILL }},
 
                { &hf_rpc_fragments,
                { "RPC Fragments", "rpc.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
-                       "RPC Fragments", HFILL }},
+                       NULL, HFILL }},
+
+               { &hf_rpc_reassembled_length,
+               { "Reassembled RPC length", "rpc.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x0,
+                       "The total length of the reassembled payload", HFILL }},
        };
        static gint *ett[] = {
                &ett_rpc,
@@ -3530,13 +3801,16 @@ proto_register_rpc(void)
 
        prefs_register_bool_preference(rpc_module, "dissect_unknown_programs",
                "Dissect unknown RPC program numbers",
-               "Whether the RPC dissector should attempt to dissect RPC PDUs containing programs that are not known to Ethereal. This will make the heuristics significantly weaker and elevate the risk for falsely identifying and misdissecting packets significantly.",
+               "Whether the RPC dissector should attempt to dissect RPC PDUs containing programs that are not known to Wireshark. This will make the heuristics significantly weaker and elevate the risk for falsely identifying and misdissecting packets significantly.",
                &rpc_dissect_unknown_programs);
 
+       prefs_register_bool_preference(rpc_module, "find_fragment_start",
+               "Attempt to locate start-of-fragment in partial RPC-over-TCP captures",
+               "Whether the RPC dissector should attempt to locate RPC PDU boundaries when initial fragment alignment is not known.  This may cause false positives, or slow operation.",
+               &rpc_find_fragment_start);
+
        register_dissector("rpc", dissect_rpc, proto_rpc);
-       rpc_handle = find_dissector("rpc");
        register_dissector("rpc-tcp", dissect_rpc_tcp, proto_rpc);
-       rpc_tcp_handle = find_dissector("rpc-tcp");
        rpc_tap = register_tap("rpc");
 
        /*
@@ -3557,9 +3831,6 @@ proto_register_rpc(void)
 void
 proto_reg_handoff_rpc(void)
 {
-       dissector_handle_t rpc_tcp_handle;
-       dissector_handle_t rpc_udp_handle;
-
        /* tcp/udp port 111 is used by portmapper which is an onc-rpc service.
           we register onc-rpc on this port so that we can choose RPC in
           the list offered by DecodeAs, and so that traffic to or from
@@ -3568,13 +3839,27 @@ proto_reg_handoff_rpc(void)
           probably RPC traffic from some randomly-chosen port that happens
           to match some port for which we have a dissector)
        */
-       rpc_tcp_handle = create_dissector_handle(dissect_rpc_tcp, proto_rpc);
+       rpc_tcp_handle = find_dissector("rpc-tcp");
        dissector_add("tcp.port", 111, rpc_tcp_handle);
-       rpc_udp_handle = create_dissector_handle(dissect_rpc, proto_rpc);
-       dissector_add("udp.port", 111, rpc_udp_handle);
+       rpc_handle = find_dissector("rpc");
+       dissector_add("udp.port", 111, rpc_handle);
 
        heur_dissector_add("tcp", dissect_rpc_tcp_heur, proto_rpc);
        heur_dissector_add("udp", dissect_rpc_heur, proto_rpc);
        gssapi_handle = find_dissector("gssapi");
        data_handle = find_dissector("data");
 }
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * ex: set shiftwidth=8 tabstop=8 noexpandtab
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
+