From Anand V. Narwani:
[obnox/wireshark/wip.git] / packet-rpc.c
index 381691edf5a19dcdf73679d12e072a408f935016..107836cd3d63ad89a9d71e9a5720d7043a92a843 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for rpc dissection
  * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
  * 
- * $Id: packet-rpc.c,v 1.73 2001/10/29 20:49:28 guy Exp $
+ * $Id: packet-rpc.c,v 1.97 2002/06/07 10:11:39 guy Exp $
  * 
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
-#include "packet.h"
-#include "conversation.h"
+#include <epan/packet.h>
+#include <epan/conversation.h>
 #include "packet-rpc.h"
+#include "packet-frame.h"
+#include "packet-tcp.h"
 #include "prefs.h"
+#include "reassemble.h"
+#include "rpc_defrag.h"
+#include "packet-nfs.h"
 
 /*
  * See:
  *     although we don't currently dissect AUTH_DES or AUTH_KERB.
  */
 
-#define RPC_RM_FRAGLEN  0x7fffffffL
-
 /* desegmentation of RPC over TCP */
 static gboolean rpc_desegment = TRUE;
 
+/* defragmentation of fragmented RPC over TCP records */
+static gboolean rpc_defragment = FALSE;
+
 static struct true_false_string yesno = { "Yes", "No" };
 
 
@@ -184,8 +190,19 @@ static int hf_rpc_call_dup = -1;
 static int hf_rpc_reply_dup = -1;
 static int hf_rpc_value_follows = -1;
 static int hf_rpc_array_len = -1;
+static int hf_rpc_time = -1;
+static int hf_rpc_fragments = -1;
+static int hf_rpc_fragment = -1;
+static int hf_rpc_fragment_overlap = -1;
+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 gint ett_rpc = -1;
+static gint ett_rpc_fragments = -1;
+static gint ett_rpc_fragment = -1;
+static gint ett_rpc_fraghdr = -1;
 static gint ett_rpc_string = -1;
 static gint ett_rpc_cred = -1;
 static gint ett_rpc_verf = -1;
@@ -193,6 +210,23 @@ static gint ett_rpc_gids = -1;
 static gint ett_rpc_gss_data = -1;
 static gint ett_rpc_array = -1;
 
+static dissector_handle_t rpc_tcp_handle;
+static dissector_handle_t rpc_handle;
+static dissector_handle_t data_handle;
+
+fragment_items rpc_frag_items = {
+       &ett_rpc_fragment,
+       &ett_rpc_fragments,
+       &hf_rpc_fragments,
+       &hf_rpc_fragment,
+       &hf_rpc_fragment_overlap,
+       &hf_rpc_fragment_overlap_conflict,
+       &hf_rpc_fragment_multiple_tails,
+       &hf_rpc_fragment_too_long_fragment,
+       &hf_rpc_fragment_error,
+       "fragments"
+};
+
 /* Hash table with info on RPC program numbers */
 static GHashTable *rpc_progs;
 
@@ -229,7 +263,7 @@ static void dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 /***********************************/
 
 /* compare 2 keys */
-gint
+static gint
 rpc_proc_equal(gconstpointer k1, gconstpointer k2)
 {
        rpc_proc_info_key* key1 = (rpc_proc_info_key*) k1;
@@ -242,7 +276,7 @@ rpc_proc_equal(gconstpointer k1, gconstpointer k2)
 }
 
 /* calculate a hash key */
-guint
+static guint
 rpc_proc_hash(gconstpointer k)
 {
        rpc_proc_info_key* key = (rpc_proc_info_key*) k;
@@ -308,7 +342,7 @@ char *rpc_proc_name(guint32 prog, guint32 vers, guint32 proc)
 /*********************************/
 
 /* compare 2 keys */
-gint
+static gint
 rpc_prog_equal(gconstpointer k1, gconstpointer k2)
 {
        rpc_prog_info_key* key1 = (rpc_prog_info_key*) k1;
@@ -320,7 +354,7 @@ rpc_prog_equal(gconstpointer k1, gconstpointer k2)
 
 
 /* calculate a hash key */
-guint
+static guint
 rpc_prog_hash(gconstpointer k)
 {
        rpc_prog_info_key* key = (rpc_prog_info_key*) k;
@@ -377,25 +411,6 @@ typedef struct _rpc_call_info_key {
 
 static GMemChunk *rpc_call_info_key_chunk;
 
-typedef enum {
-       FLAVOR_UNKNOWN,         /* authentication flavor unknown */
-       FLAVOR_NOT_GSSAPI,      /* flavor isn't GSSAPI */
-       FLAVOR_GSSAPI_NO_INFO,  /* flavor is GSSAPI, procedure & service unknown */
-       FLAVOR_GSSAPI           /* flavor is GSSAPI, procedure & service known */
-} flavor_t;
-
-typedef struct _rpc_call_info_value {
-       guint32 req_num;        /* frame number of first request seen */
-       guint32 rep_num;        /* frame number of first reply seen */
-       guint32 prog;
-       guint32 vers;
-       guint32 proc;
-       flavor_t flavor;
-       guint32 gss_proc;
-       guint32 gss_svc;
-       rpc_proc_info_value*    proc_info;
-} rpc_call_info_value;
-
 static GMemChunk *rpc_call_info_value_chunk;
 
 static GHashTable *rpc_calls;
@@ -403,7 +418,7 @@ static GHashTable *rpc_calls;
 static GHashTable *rpc_indir_calls;
 
 /* compare 2 keys */
-gint
+static gint
 rpc_call_equal(gconstpointer k1, gconstpointer k2)
 {
        rpc_call_info_key* key1 = (rpc_call_info_key*) k1;
@@ -415,7 +430,7 @@ rpc_call_equal(gconstpointer k1, gconstpointer k2)
 
 
 /* calculate a hash key */
-guint
+static guint
 rpc_call_hash(gconstpointer k)
 {
        rpc_call_info_key* key = (rpc_call_info_key*) k;
@@ -433,7 +448,7 @@ rpc_roundup(unsigned int a)
 
 
 int
-dissect_rpc_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+dissect_rpc_bool(tvbuff_t *tvb, proto_tree *tree,
 int hfindex, int offset)
 {
        if (tree)
@@ -443,7 +458,7 @@ int hfindex, int offset)
 
 
 int
-dissect_rpc_uint32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+dissect_rpc_uint32(tvbuff_t *tvb, proto_tree *tree,
 int hfindex, int offset)
 {
        if (tree)
@@ -453,29 +468,22 @@ int hfindex, int offset)
 
 
 int
-dissect_rpc_uint64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+dissect_rpc_uint64(tvbuff_t *tvb, proto_tree *tree,
 int hfindex, int offset)
 {
-       guint32 value_low;
-       guint32 value_high;
+       header_field_info       *hfinfo;
 
-       value_high = tvb_get_ntohl(tvb, offset + 0);
-       value_low  = tvb_get_ntohl(tvb, offset + 4);
-
-       if (tree) {
-               if (value_high)
-                       proto_tree_add_text(tree, tvb, offset, 8,
-                               "%s: 0x%x%08x", proto_registrar_get_name(hfindex), value_high, value_low);
-               else
-                       proto_tree_add_uint(tree, hfindex, tvb, offset, 8, value_low);
-       }
+       hfinfo = proto_registrar_get_nth(hfindex);
+       g_assert(hfinfo->type == FT_UINT64);
+       if (tree)
+               proto_tree_add_item(tree, hfindex, tvb, offset, 8, FALSE);
 
        return offset + 8;
 }
 
 
 static int
-dissect_rpc_opaque_data(tvbuff_t *tvb, int offset, packet_info *pinfo,
+dissect_rpc_opaque_data(tvbuff_t *tvb, int offset,
     proto_tree *tree, int hfindex, gboolean string_data,
     char **string_buffer_ret)
 {
@@ -483,47 +491,54 @@ dissect_rpc_opaque_data(tvbuff_t *tvb, int offset, packet_info *pinfo,
        proto_tree *string_tree = NULL;
        int old_offset = offset;
 
-       int length_truncated = 0;
-
-       int string_truncated = 0;
-       guint32 string_length = 0;
+       guint32 string_length;
        guint32 string_length_full;
        guint32 string_length_packet;
-       guint32 string_length_copy = 0;
+       guint32 string_length_captured;
+       guint32 string_length_copy;
+
+       int fill_truncated;
+       guint32 fill_length;
+       guint32 fill_length_packet;
+       guint32 fill_length_captured;
+       guint32 fill_length_copy;
 
-       int fill_truncated = 0;
-       guint32 fill_length  = 0;
-       guint32 fill_length_packet  = 0;
-       guint32 fill_length_copy  = 0;
+       int exception = 0;
 
        char *string_buffer = NULL;
        char *string_buffer_print = NULL;
 
        string_length = tvb_get_ntohl(tvb,offset+0);
        string_length_full = rpc_roundup(string_length);
-       /* XXX - just let the tvbuff stuff throw an exception? */
-       string_length_packet = tvb_length_remaining(tvb, offset + 4);
-       if (string_length_packet < string_length) {
+       string_length_captured = tvb_length_remaining(tvb, offset + 4);
+       string_length_packet = tvb_reported_length_remaining(tvb, offset + 4);
+       if (string_length_captured < string_length) {
                /* truncated string */
-               string_truncated = 1;
-               string_length_copy = string_length_packet;
+               string_length_copy = string_length_captured;
                fill_truncated = 2;
                fill_length = 0;
-               fill_length_packet = 0;
                fill_length_copy = 0;
+               if (string_length_packet < string_length)
+                       exception = ReportedBoundsError;
+               else
+                       exception = BoundsError;
        }
        else {
                /* full string data */
-               string_truncated = 0;
                string_length_copy = string_length;
                fill_length = string_length_full - string_length;
-               /* XXX - just let the tvbuff stuff throw an exception? */
-               fill_length_packet = tvb_length_remaining(tvb,
+               fill_length_captured = tvb_length_remaining(tvb,
+                   offset + 4 + string_length);
+               fill_length_packet = tvb_reported_length_remaining(tvb,
                    offset + 4 + string_length);
-               if (fill_length_packet < fill_length) {
+               if (fill_length_captured < fill_length) {
                        /* truncated fill bytes */
                        fill_length_copy = fill_length_packet;
                        fill_truncated = 1;
+                       if (fill_length_packet < fill_length)
+                               exception = ReportedBoundsError;
+                       else
+                               exception = BoundsError;
                }
                else {
                        /* full fill bytes */
@@ -575,7 +590,7 @@ dissect_rpc_opaque_data(tvbuff_t *tvb, int offset, packet_info *pinfo,
        }
 
        if (tree) {
-               string_item = proto_tree_add_text(tree, tvb,offset+0, tvb_length_remaining(tvb, offset),
+               string_item = proto_tree_add_text(tree, tvb,offset+0, -1,
                        "%s: %s", proto_registrar_get_name(hfindex), string_buffer_print);
                if (string_data) {
                        proto_tree_add_string_hidden(tree, hfindex, tvb, offset+4,
@@ -585,47 +600,39 @@ dissect_rpc_opaque_data(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        string_tree = proto_item_add_subtree(string_item, ett_rpc_string);
                }
        }
-       if (length_truncated) {
-               if (string_tree)
-                       proto_tree_add_text(string_tree, tvb,
-                               offset, tvb_length_remaining(tvb, offset),
-                               "length: <TRUNCATED>");
-               offset = tvb_length(tvb);
-       } else {
-               if (string_tree)
-                       proto_tree_add_text(string_tree, tvb,offset+0,4,
-                               "length: %u", string_length);
-               offset += 4;
+       if (string_tree)
+               proto_tree_add_text(string_tree, tvb,offset+0,4,
+                       "length: %u", string_length);
+       offset += 4;
 
+       if (string_tree) {
+               if (string_data) {
+                       proto_tree_add_string_format(string_tree,
+                           hfindex, tvb, offset, string_length_copy,
+                               string_buffer_print, 
+                               "contents: %s", string_buffer_print);
+               } else {
+                       proto_tree_add_bytes_format(string_tree,
+                           hfindex, tvb, offset, string_length_copy,
+                               string_buffer_print, 
+                               "contents: %s", string_buffer_print);
+               }
+       }
+       offset += string_length_copy;
+       if (fill_length) {
                if (string_tree) {
-                       if (string_data) {
-                               proto_tree_add_string_format(string_tree,
-                                   hfindex, tvb, offset, string_length_copy,
-                                       string_buffer_print, 
-                                       "contents: %s", string_buffer_print);
-                       } else {
-                               proto_tree_add_bytes_format(string_tree,
-                                   hfindex, tvb, offset, string_length_copy,
-                                       string_buffer_print, 
-                                       "contents: %s", string_buffer_print);
+                       if (fill_truncated) {
+                               proto_tree_add_text(string_tree, tvb,
+                               offset,fill_length_copy,
+                               "fill bytes: opaque data<TRUNCATED>");
                        }
-               }
-               offset += string_length_copy;
-               if (fill_length) {
-                       if (string_tree) {
-                               if (fill_truncated) {
-                                       proto_tree_add_text(string_tree, tvb,
-                                       offset,fill_length_copy,
-                                       "fill bytes: opaque data<TRUNCATED>");
-                               }
-                               else {
-                                       proto_tree_add_text(string_tree, tvb,
-                                       offset,fill_length_copy,
-                                       "fill bytes: opaque data");
-                               }
+                       else {
+                               proto_tree_add_text(string_tree, tvb,
+                               offset,fill_length_copy,
+                               "fill bytes: opaque data");
                        }
-                       offset += fill_length_copy;
                }
+               offset += fill_length_copy;
        }
        
        if (string_item) {
@@ -639,25 +646,32 @@ dissect_rpc_opaque_data(tvbuff_t *tvb, int offset, packet_info *pinfo,
                else
                        g_free (string_buffer_print);
        }
+
+       /*
+        * If the data was truncated, throw the appropriate exception,
+        * so that dissection stops and the frame is properly marked.
+        */
+       if (exception != 0)
+               THROW(exception);
        return offset;
 }
 
 
 int
-dissect_rpc_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+dissect_rpc_string(tvbuff_t *tvb, proto_tree *tree,
     int hfindex, int offset, char **string_buffer_ret)
 {
-       offset = dissect_rpc_opaque_data(tvb, offset, pinfo, tree,
+       offset = dissect_rpc_opaque_data(tvb, offset, tree,
            hfindex, TRUE, string_buffer_ret);
        return offset;
 }
 
 
 int
-dissect_rpc_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+dissect_rpc_data(tvbuff_t *tvb, proto_tree *tree,
     int hfindex, int offset)
 {
-       offset = dissect_rpc_opaque_data(tvb, offset, pinfo, tree, hfindex,
+       offset = dissect_rpc_opaque_data(tvb, offset, tree, hfindex,
            FALSE, NULL);
 
        return offset;
@@ -706,12 +720,11 @@ dissect_rpc_array(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                return offset;
        }
 
-       lock_item = proto_tree_add_item(tree, hfindex, tvb, offset,
-                       0, FALSE);
+       lock_item = proto_tree_add_item(tree, hfindex, tvb, offset, -1, FALSE);
 
        lock_tree = proto_item_add_subtree(lock_item, ett_rpc_array);   
 
-       offset = dissect_rpc_uint32(tvb, pinfo, lock_tree,
+       offset = dissect_rpc_uint32(tvb, lock_tree,
                        hf_rpc_array_len, offset);
 
        while (num--) {
@@ -723,7 +736,7 @@ dissect_rpc_array(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 }
 
 static int
-dissect_rpc_authunix_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset)
+dissect_rpc_authunix_cred(tvbuff_t* tvb, proto_tree* tree, int offset)
 {
        guint stamp;
        guint uid;
@@ -740,7 +753,7 @@ dissect_rpc_authunix_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, i
                        offset+0, 4, stamp);
        offset += 4;
 
-       offset = dissect_rpc_string(tvb, pinfo, tree,
+       offset = dissect_rpc_string(tvb, tree,
                        hf_rpc_auth_machinename, offset, NULL);
 
        uid = tvb_get_ntohl(tvb,offset+0);
@@ -777,7 +790,7 @@ dissect_rpc_authunix_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, i
 }
 
 static int
-dissect_rpc_authgss_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset)
+dissect_rpc_authgss_cred(tvbuff_t* tvb, proto_tree* tree, int offset)
 {
        guint agc_v;
        guint agc_proc;
@@ -808,14 +821,14 @@ dissect_rpc_authgss_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, in
                                    tvb, offset+0, 4, agc_svc);
        offset += 4;
        
-       offset = dissect_rpc_data(tvb, pinfo, tree, hf_rpc_authgss_ctx,
+       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_ctx,
                        offset);
        
        return offset;
 }
 
-int
-dissect_rpc_authdes_desblock(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+static int
+dissect_rpc_authdes_desblock(tvbuff_t *tvb, proto_tree *tree,
 int hfindex, int offset)
 {
        guint32 value_low;
@@ -834,7 +847,7 @@ int hfindex, int offset)
 }
 
 static int
-dissect_rpc_authdes_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset)
+dissect_rpc_authdes_cred(tvbuff_t* tvb, proto_tree* tree, int offset)
 {
        guint adc_namekind;
        guint window = 0;
@@ -849,9 +862,9 @@ dissect_rpc_authdes_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, in
        switch(adc_namekind)
        {
        case AUTHDES_NAMEKIND_FULLNAME:
-               offset = dissect_rpc_string(tvb, pinfo, tree, 
+               offset = dissect_rpc_string(tvb, tree, 
                        hf_rpc_authdes_netname, offset, NULL);
-               offset = dissect_rpc_authdes_desblock(tvb, pinfo, tree,
+               offset = dissect_rpc_authdes_desblock(tvb, tree,
                        hf_rpc_authdes_convkey, offset);
                window = tvb_get_ntohl(tvb, offset+0);
                proto_tree_add_uint(tree, hf_rpc_authdes_window, tvb, offset+0, 4,
@@ -871,7 +884,7 @@ dissect_rpc_authdes_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, in
 }
 
 static int
-dissect_rpc_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset)
+dissect_rpc_cred(tvbuff_t* tvb, proto_tree* tree, int offset)
 {
        guint flavor;
        guint length;
@@ -894,7 +907,7 @@ dissect_rpc_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
 
                switch (flavor) {
                case AUTH_UNIX:
-                       dissect_rpc_authunix_cred(tvb, pinfo, ctree, offset+8);
+                       dissect_rpc_authunix_cred(tvb, ctree, offset+8);
                        break;
                /*
                case AUTH_SHORT:
@@ -902,11 +915,11 @@ dissect_rpc_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
                break;
                */
                case AUTH_DES:
-                       dissect_rpc_authdes_cred(tvb, pinfo, ctree, offset+8);
+                       dissect_rpc_authdes_cred(tvb, ctree, offset+8);
                        break;
                        
                case RPCSEC_GSS:
-                       dissect_rpc_authgss_cred(tvb, pinfo, ctree, offset+8);
+                       dissect_rpc_authgss_cred(tvb, ctree, offset+8);
                        break;
                default:
                        if (length)
@@ -924,7 +937,7 @@ dissect_rpc_cred(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
  * verifier we're decoding (CALL or REPLY).
  */
 static int
-dissect_rpc_verf(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset, int msg_type)
+dissect_rpc_verf(tvbuff_t* tvb, proto_tree* tree, int offset, int msg_type)
 {
        guint flavor;
        guint length;
@@ -947,7 +960,7 @@ dissect_rpc_verf(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
                case AUTH_UNIX:
                        proto_tree_add_uint(vtree, hf_rpc_auth_length, tvb,
                                            offset+4, 4, length);
-                       dissect_rpc_authunix_cred(tvb, pinfo, vtree, offset+8);
+                       dissect_rpc_authunix_cred(tvb, vtree, offset+8);
                        break;
                case AUTH_DES:
                        proto_tree_add_uint(vtree, hf_rpc_auth_length, tvb,
@@ -957,7 +970,7 @@ dissect_rpc_verf(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
                        {
                                guint window;
 
-                               dissect_rpc_authdes_desblock(tvb, pinfo, vtree,
+                               dissect_rpc_authdes_desblock(tvb, vtree,
                                        hf_rpc_authdes_timestamp, offset+8);
                                window = tvb_get_ntohl(tvb, offset+16);
                                proto_tree_add_uint(vtree, hf_rpc_authdes_windowverf, tvb, 
@@ -968,7 +981,7 @@ dissect_rpc_verf(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
                                /* must be an RPC_REPLY */
                                guint nickname;
 
-                               dissect_rpc_authdes_desblock(tvb, pinfo, vtree,
+                               dissect_rpc_authdes_desblock(tvb, vtree,
                                        hf_rpc_authdes_timeverf, offset+8);
                                nickname = tvb_get_ntohl(tvb, offset+16);
                                proto_tree_add_uint(vtree, hf_rpc_authdes_nickname, tvb, 
@@ -976,7 +989,7 @@ dissect_rpc_verf(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
                        }
                        break;
                case RPCSEC_GSS:
-                       dissect_rpc_data(tvb, pinfo, vtree,
+                       dissect_rpc_data(tvb, vtree,
                                hf_rpc_authgss_checksum, offset+4);
                        break;
                default:
@@ -994,19 +1007,19 @@ dissect_rpc_verf(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset
 }
 
 static int
-dissect_rpc_authgss_initarg(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset)
+dissect_rpc_authgss_initarg(tvbuff_t* tvb, proto_tree* tree, int offset)
 {
-       offset = dissect_rpc_data(tvb, pinfo, tree, hf_rpc_authgss_token,
+       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_token,
                        offset);
        return offset;
 }
 
 static int
-dissect_rpc_authgss_initres(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset)
+dissect_rpc_authgss_initres(tvbuff_t* tvb, proto_tree* tree, int offset)
 {
        int major, minor, window;
        
-       offset = dissect_rpc_data(tvb, pinfo, tree, hf_rpc_authgss_ctx,
+       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_ctx,
                        offset);
        
        major = tvb_get_ntohl(tvb,offset+0);
@@ -1027,7 +1040,7 @@ dissect_rpc_authgss_initres(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree,
                                    offset+0, 4, window);
        offset += 4;
 
-       offset = dissect_rpc_data(tvb, pinfo, tree, hf_rpc_authgss_token,
+       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_token,
                        offset);
 
        return offset;
@@ -1089,16 +1102,16 @@ dissect_rpc_authgss_integ_data(tvbuff_t *tvb, packet_info *pinfo,
                                      dissect_function, progname);
        }
        offset += length - 4;
-       offset = dissect_rpc_data(tvb, pinfo, tree, hf_rpc_authgss_checksum,
+       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_checksum,
                        offset);
        return offset;
 }
 
 
 static int
-dissect_rpc_authgss_priv_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
+dissect_rpc_authgss_priv_data(tvbuff_t *tvb, proto_tree *tree, int offset)
 {
-       offset = dissect_rpc_data(tvb, pinfo, tree, hf_rpc_authgss_data,
+       offset = dissect_rpc_data(tvb, tree, hf_rpc_authgss_data,
                        offset);
        return offset;
 }
@@ -1181,7 +1194,7 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                /* Make the dissector for this conversation the non-heuristic
                   RPC dissector. */
                conversation_set_dissector(conversation,
-                   (pinfo->ptype == PT_TCP) ? dissect_rpc_tcp : dissect_rpc);
+                   (pinfo->ptype == PT_TCP) ? rpc_tcp_handle : rpc_handle);
 
                /* Prepare the key data.
 
@@ -1207,6 +1220,7 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        rpc_call->prog = prog;
                        rpc_call->vers = vers;
                        rpc_call->proc = proc;
+                       rpc_call->private_data = NULL;
 
                        /*
                         * XXX - what about RPCSEC_GSS?
@@ -1226,7 +1240,7 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                   Happens only with strange program versions or
                   non-existing dissectors.
                   Just show the arguments as opaque data. */
-               offset = dissect_rpc_data(tvb, pinfo, tree, args_id,
+               offset = dissect_rpc_data(tvb, tree, args_id,
                    offset);
                return offset;
        }
@@ -1296,7 +1310,7 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                /* We haven't seen an RPC call for that conversation,
                   so we can't check for a reply to that call.
                   Just show the reply stuff as opaque data. */
-               offset = dissect_rpc_data(tvb, pinfo, tree, result_id,
+               offset = dissect_rpc_data(tvb, tree, result_id,
                    offset);
                return offset;
        }
@@ -1309,7 +1323,7 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                /* The XID doesn't match a call from that
                   conversation, so it's probably not an RPC reply.
                   Just show the reply stuff as opaque data. */
-               offset = dissect_rpc_data(tvb, pinfo, tree, result_id,
+               offset = dissect_rpc_data(tvb, tree, result_id,
                    offset);
                return offset;
        }
@@ -1347,7 +1361,7 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        if (dissect_function == NULL) {
                /* We don't know how to dissect the reply procedure.
                   Just show the reply stuff as opaque data. */
-               offset = dissect_rpc_data(tvb, pinfo, tree, result_id,
+               offset = dissect_rpc_data(tvb, tree, result_id,
                    offset);
                return offset;
        }
@@ -1375,23 +1389,23 @@ dissect_rpc_continuation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        proto_item *rpc_item;
        proto_tree *rpc_tree;
 
-       if (check_col(pinfo->fd, COL_PROTOCOL))
-               col_set_str(pinfo->fd, COL_PROTOCOL, "RPC");
-       if (check_col(pinfo->fd, COL_INFO))
-               col_set_str(pinfo->fd, COL_INFO, "Continuation");
+       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");
 
        if (tree) {
-               rpc_item = proto_tree_add_item(tree, proto_rpc, tvb, 0,
-                               tvb_length(tvb), FALSE);
+               rpc_item = proto_tree_add_item(tree, proto_rpc, tvb, 0, -1,
+                               FALSE);
                rpc_tree = proto_item_add_subtree(rpc_item, ett_rpc);
-               proto_tree_add_text(rpc_tree, tvb, 0, tvb_length(tvb),
-                   "Continuation data");
+               proto_tree_add_text(rpc_tree, tvb, 0, -1, "Continuation data");
        }
 }
 
 static gboolean
-dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
-    proto_tree *tree, gboolean use_rm, guint32 rpc_rm)
+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)
 {
        guint32 msg_type;
        rpc_call_info_key rpc_call_key;
@@ -1424,11 +1438,12 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
        unsigned int auth_state;
 
-       proto_item *rpc_item=NULL;
+       proto_item *rpc_item = NULL;
        proto_tree *rpc_tree = NULL;
 
-       proto_item *pitem=NULL;
+       proto_item *pitem = NULL;
        proto_tree *ptree = NULL;
+       int offset = (is_tcp && tvb == frag_tvb) ? 4 : 0;
        int offset_old = offset;
 
        rpc_call_info_key       *new_rpc_call_key;
@@ -1436,8 +1451,10 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
        rpc_proc_info_value     *value = NULL;
        conversation_t* conversation;
        static address null_address = { AT_NONE, 0, NULL };
+       nstime_t ns;
 
        dissect_function_t *dissect_function = NULL;
+       gboolean dissect_rpc = TRUE;
 
        /*
         * Check to see whether this looks like an RPC call or reply.
@@ -1464,7 +1481,7 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                   We already have the message type.
                   Check whether an RPC version number of 2 is in the
                   location where it would be, and that an RPC program
-                  number we know about is in the locaton where it would be. */
+                  number we know about is in the location where it would be. */
                rpc_prog_key.prog = tvb_get_ntohl(tvb, offset + 12);
                if (tvb_get_ntohl(tvb, offset + 8) != 2 ||
                    ((rpc_prog = g_hash_table_lookup(rpc_progs, &rpc_prog_key))
@@ -1522,6 +1539,9 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                           conversation, so it's probably not an RPC reply. */
                        return FALSE;
                }
+               /* pass rpc_info to subdissectors */
+               rpc_call->request=FALSE;
+               pinfo->private_data=rpc_call;
                break;
 
        default:
@@ -1531,24 +1551,38 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                return FALSE;
        }
 
-       if (check_col(pinfo->fd, COL_PROTOCOL))
-               col_set_str(pinfo->fd, COL_PROTOCOL, "RPC");
-       if (check_col(pinfo->fd, COL_INFO))
-               col_clear(pinfo->fd, COL_INFO);
-
-       if (tree) {
-               rpc_item = proto_tree_add_item(tree, proto_rpc, tvb, 0,
-                               tvb_length(tvb), FALSE);
-               if (rpc_item) {
-                       rpc_tree = proto_item_add_subtree(rpc_item, ett_rpc);
+       if (is_tcp) {
+               /*
+                * This is RPC-over-TCP; check if this is the last
+                * fragment.
+                */
+               if (!(rpc_rm & RPC_RM_LASTFRAG)) {
+                       /*
+                        * This isn't the last fragment.
+                        * If we're doing reassembly, just return
+                        * TRUE to indicate that this looks like
+                        * the beginning of an RPC message,
+                        * and let them do fragment reassembly.
+                        */
+                       if (rpc_defragment)
+                               return TRUE;
                }
        }
 
-       if (use_rm && rpc_tree) {
-               proto_tree_add_boolean(rpc_tree,hf_rpc_lastfrag, tvb,
-                       offset-4, 4, (rpc_rm >> 31) & 0x1);
-               proto_tree_add_uint(rpc_tree,hf_rpc_fraglen, tvb,
-                       offset-4, 4, rpc_rm & RPC_RM_FRAGLEN);
+       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);
+
+       if (tree) {
+               rpc_item = proto_tree_add_item(tree, proto_rpc, tvb, 0, -1,
+                   FALSE);
+               rpc_tree = proto_item_add_subtree(rpc_item, ett_rpc);
+
+               if (is_tcp) {
+                       show_rpc_fraginfo(tvb, frag_tvb, rpc_tree, rpc_rm,
+                           ipfd_head, pinfo);
+               }
        }
 
        xid      = tvb_get_ntohl(tvb, offset + 0);
@@ -1588,10 +1622,10 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                "Program: %s (%u)", progname, prog);
                }
                
-               if (check_col(pinfo->fd, COL_PROTOCOL)) {
+               if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
                        /* Set the protocol name to the underlying
                           program name. */
-                       col_set_str(pinfo->fd, COL_PROTOCOL, progname);
+                       col_set_str(pinfo->cinfo, COL_PROTOCOL, progname);
                }
 
                vers = tvb_get_ntohl(tvb, offset+8);
@@ -1626,8 +1660,8 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                "Procedure: %s (%u)", procname, proc);
                }
 
-               if (check_col(pinfo->fd, COL_INFO)) {
-                       col_add_fstr(pinfo->fd, COL_INFO,"V%u %s %s XID 0x%x",
+               if (check_col(pinfo->cinfo, COL_INFO)) {
+                       col_add_fstr(pinfo->cinfo, COL_INFO,"V%u %s %s XID 0x%x",
                                vers,
                                procname,
                                msg_type_name,
@@ -1716,7 +1750,7 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                /* Make the dissector for this conversation the non-heuristic
                   RPC dissector. */
                conversation_set_dissector(conversation,
-                   (pinfo->ptype == PT_TCP) ? dissect_rpc_tcp : dissect_rpc);
+                   (pinfo->ptype == PT_TCP) ? rpc_tcp_handle : rpc_handle);
 
                /* prepare the key data */
                rpc_call_key.xid = xid;
@@ -1731,8 +1765,8 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (pinfo->fd->num != rpc_call->req_num) {
                                /* No, so it's a duplicate request.
                                   Mark it as such. */
-                               if (check_col(pinfo->fd, COL_INFO)) {
-                                       col_append_fstr(pinfo->fd, COL_INFO,
+                               if (check_col(pinfo->cinfo, COL_INFO)) {
+                                       col_append_fstr(pinfo->cinfo, COL_INFO,
                                                " dup XID 0x%x", xid);
                                        if (rpc_tree) {
                                                proto_tree_add_uint_hidden(rpc_tree,
@@ -1757,19 +1791,34 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        rpc_call->prog = prog;
                        rpc_call->vers = vers;
                        rpc_call->proc = proc;
+                       rpc_call->private_data = NULL;
+                       rpc_call->xid = xid;
                        rpc_call->flavor = flavor;
                        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;
+
                        /* store it */
                        g_hash_table_insert(rpc_calls, new_rpc_call_key,
                            rpc_call);
                }
 
+               if(rpc_call && rpc_call->rep_num){
+                       proto_tree_add_text(rpc_tree, tvb, 0, 0,
+                           "The reply to this request is in frame %u",
+                           rpc_call->rep_num);
+               }
+
                offset += 16;
 
-               offset = dissect_rpc_cred(tvb, pinfo, rpc_tree, offset);
-               offset = dissect_rpc_verf(tvb, pinfo, rpc_tree, offset, msg_type);
+               offset = dissect_rpc_cred(tvb, rpc_tree, offset);
+               offset = dissect_rpc_verf(tvb, rpc_tree, offset, msg_type);
+
+               /* pass rpc_info to subdissectors */
+               rpc_call->request=TRUE;
+               pinfo->private_data=rpc_call;
 
                /* go to the next dissector */
 
@@ -1785,11 +1834,6 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                gss_proc = rpc_call->gss_proc;
                gss_svc = rpc_call->gss_svc;
 
-               /* Indicate the frame to which this is a reply. */
-               proto_tree_add_text(rpc_tree, tvb, 0, 0,
-                   "This is a reply to a request in frame %u",
-                   rpc_call->req_num);
-
                if (rpc_call->proc_info != NULL) {
                        dissect_function = rpc_call->proc_info->dissect_reply;
                        if (rpc_call->proc_info->name != NULL) {
@@ -1819,15 +1863,15 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        ett = rpc_prog->ett;
                        progname = rpc_prog->progname;
 
-                       if (check_col(pinfo->fd, COL_PROTOCOL)) {
+                       if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
                                /* Set the protocol name to the underlying
                                   program name. */
-                               col_set_str(pinfo->fd, COL_PROTOCOL, progname);
+                               col_set_str(pinfo->cinfo, COL_PROTOCOL, progname);
                        }
                }
 
-               if (check_col(pinfo->fd, COL_INFO)) {
-                       col_add_fstr(pinfo->fd, COL_INFO,"V%u %s %s XID 0x%x",
+               if (check_col(pinfo->cinfo, COL_INFO)) {
+                       col_add_fstr(pinfo->cinfo, COL_INFO,"V%u %s %s XID 0x%x",
                                vers,
                                procname,
                                msg_type_name,
@@ -1845,6 +1889,29 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                "Procedure: %s (%u)", procname, proc);
                }
 
+               reply_state = tvb_get_ntohl(tvb,offset+0);
+               if (rpc_tree) {
+                       proto_tree_add_uint(rpc_tree, hf_rpc_state_reply, tvb,
+                               offset+0, 4, reply_state);
+               }
+               offset += 4;
+
+               /* Indicate the frame to which this is a reply. */
+               if(rpc_call && rpc_call->req_num){
+                       proto_tree_add_text(rpc_tree, tvb, 0, 0,
+                           "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,
+                               &ns);
+               }
+
+
                if (rpc_call->rep_num == 0) {
                        /* We have not yet seen a reply to that call, so
                           this must be the first reply; remember its
@@ -1856,8 +1923,8 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (rpc_call->rep_num != pinfo->fd->num) {
                                /* No, so it's a duplicate reply.
                                   Mark it as such. */
-                               if (check_col(pinfo->fd, COL_INFO)) {
-                                       col_append_fstr(pinfo->fd, COL_INFO,
+                               if (check_col(pinfo->cinfo, COL_INFO)) {
+                                       col_append_fstr(pinfo->cinfo, COL_INFO,
                                                " dup XID 0x%x", xid);
                                        if (rpc_tree) {
                                                proto_tree_add_uint_hidden(rpc_tree,
@@ -1869,15 +1936,10 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        }
                }
 
-               reply_state = tvb_get_ntohl(tvb,offset+0);
-               if (rpc_tree) {
-                       proto_tree_add_uint(rpc_tree, hf_rpc_state_reply, tvb,
-                               offset+0, 4, reply_state);
-               }
-               offset += 4;
+               switch (reply_state) {
 
-               if (reply_state == MSG_ACCEPTED) {
-                       offset = dissect_rpc_verf(tvb, pinfo, rpc_tree, offset, msg_type);
+               case MSG_ACCEPTED:
+                       offset = dissect_rpc_verf(tvb, rpc_tree, offset, msg_type);
                        accept_state = tvb_get_ntohl(tvb,offset+0);
                        if (rpc_tree) {
                                proto_tree_add_uint(rpc_tree, hf_rpc_state_accept, tvb,
@@ -1902,13 +1964,25 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                                tvb, offset+4, 4, vers_high);
                                }
                                offset += 8;
+
+                               /*
+                                * There's no protocol reply, so don't
+                                * try to dissect it.
+                                */
+                               dissect_rpc = FALSE;
                                break;
 
                        default:
-                               /* void */
+                               /*
+                                * There's no protocol reply, so don't
+                                * try to dissect it.
+                                */
+                               dissect_rpc = FALSE;
                                break;
                        }
-               } else if (reply_state == MSG_DENIED) {
+                       break;
+
+               case MSG_DENIED:
                        reject_state = tvb_get_ntohl(tvb,offset+0);
                        if (rpc_tree) {
                                proto_tree_add_uint(rpc_tree,
@@ -1938,6 +2012,22 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                }
                                offset += 4;
                        }
+
+                       /*
+                        * There's no protocol reply, so don't
+                        * try to dissect it.
+                        */
+                       dissect_rpc = FALSE;
+                       break;
+
+               default:
+                       /*
+                        * This isn't a valid reply state, so we have
+                        * no clue what's going on; don't try to dissect
+                        * the protocol reply.
+                        */
+                       dissect_rpc = FALSE;
+                       break;
                } 
                break; /* end of RPC reply */
 
@@ -1954,10 +2044,20 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                proto_item_set_len(rpc_item, offset - offset_old);
        }
 
+       if (!dissect_rpc) {
+               /*
+                * 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);
+               return TRUE;
+       }
+
        /* create here the program specific sub-tree */
        if (tree) {
-               pitem = proto_tree_add_item(tree, proto, tvb,
-                               offset, tvb_length(tvb) - offset, FALSE);
+               pitem = proto_tree_add_item(tree, proto, tvb, offset, -1,
+                   FALSE);
                if (pitem) {
                        ptree = proto_item_add_subtree(pitem, ett);
                }
@@ -1984,7 +2084,7 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                 * We don't know the authentication flavor, so we can't
                 * dissect the payload.
                 */
-               proto_tree_add_text(ptree, tvb, offset, tvb_length_remaining(tvb, offset),
+               proto_tree_add_text(ptree, tvb, offset, -1,
                    "Unknown authentication flavor - cannot dissect");
                return TRUE;
 
@@ -2003,7 +2103,7 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                 * procedure and service information, so we can't dissect
                 * the payload.
                 */
-               proto_tree_add_text(ptree, tvb, offset, tvb_length_remaining(tvb, offset),
+               proto_tree_add_text(ptree, tvb, offset, -1,
                    "GSS-API authentication, but procedure and service unknown - cannot dissect");
                return TRUE;
 
@@ -2019,11 +2119,11 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                case RPCSEC_GSS_CONTINUE_INIT:
                        if (msg_type == RPC_CALL) {
                                offset = dissect_rpc_authgss_initarg(tvb,
-                                       pinfo, ptree, offset);
+                                       ptree, offset);
                        }
                        else {
                                offset = dissect_rpc_authgss_initres(tvb,
-                                       pinfo, ptree, offset);
+                                       ptree, offset);
                        }
                        break;
 
@@ -2042,7 +2142,7 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        }
                        else if (gss_svc == RPCSEC_GSS_SVC_PRIVACY) {
                                offset = dissect_rpc_authgss_priv_data(tvb,
-                                               pinfo, ptree, offset);
+                                               ptree, offset);
                        }
                        break;
 
@@ -2053,7 +2153,38 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
        /* dissect any remaining bytes (incomplete dissection) as pure data in
           the ptree */
-       dissect_data(tvb, offset, pinfo, ptree);
+       call_dissector(data_handle,
+           tvb_new_subset(tvb, offset, -1, -1), 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, 
+                                       (gconstpointer)rpc_call->rep_num);
+                               if(fhd){
+                                       dissect_fhandle_hidden(pinfo,
+                                               ptree, fhd);
+                               }
+                       }
+                       break;
+               case RPC_REPLY:
+                       if(rpc_call && rpc_call->req_num){
+                               fhd=(nfs_fhandle_data_t *)g_hash_table_lookup(
+                                       nfs_fhandle_frame_table, 
+                                       (gconstpointer)rpc_call->req_num);
+                               if(fhd){
+                                       dissect_fhandle_hidden(pinfo,
+                                               ptree, fhd);
+                               }
+                       }
+                       break;
+               }
+       }
 
        return TRUE;
 }
@@ -2061,16 +2192,511 @@ dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
 static gboolean
 dissect_rpc_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       return dissect_rpc_message(tvb, 0, pinfo, tree, FALSE, 0);
+       return dissect_rpc_message(tvb, pinfo, tree, NULL, NULL, FALSE, 0);
 }
 
 static void
 dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       if (!dissect_rpc_message(tvb, 0, pinfo, tree, FALSE, 0))
+       if (!dissect_rpc_message(tvb, pinfo, tree, NULL, NULL, FALSE, 0))
                dissect_rpc_continuation(tvb, pinfo, tree);
 }
 
+/* Defragmentation of RPC-over-TCP records */
+/* table to hold defragmented RPC records */
+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;
+       /* xxx */
+       guint32 start_seq;
+} rpc_fragment_key;
+
+static guint
+rpc_fragment_hash(gconstpointer k)
+{
+       rpc_fragment_key *key = (rpc_fragment_key *)k;
+
+       return key->conv_id + key->seq;
+}
+
+static gint
+rpc_fragment_equal(gconstpointer k1, gconstpointer k2)
+{
+       rpc_fragment_key *key1 = (rpc_fragment_key *)k1;
+       rpc_fragment_key *key2 = (rpc_fragment_key *)k2;
+
+       return key1->conv_id == key2->conv_id &&
+           key1->seq == key2->seq;
+}
+
+static void
+show_rpc_fragheader(tvbuff_t *tvb, proto_tree *tree, guint32 rpc_rm)
+{
+       proto_item *hdr_item;
+       proto_tree *hdr_tree;
+       guint32 fraglen;
+
+       if (tree) {
+               fraglen = rpc_rm & RPC_RM_FRAGLEN;
+
+               hdr_item = proto_tree_add_text(tree, tvb, 0, 4,
+                   "Fragment header: %s%u %s",
+                   (rpc_rm & RPC_RM_LASTFRAG) ? "Last fragment, " : "",
+                   fraglen, plurality(fraglen, "byte", "bytes"));
+               hdr_tree = proto_item_add_subtree(hdr_item, ett_rpc_fraghdr);
+
+               proto_tree_add_boolean(hdr_tree, hf_rpc_lastfrag, tvb, 0, 4,
+                   rpc_rm);
+               proto_tree_add_uint(hdr_tree, hf_rpc_fraglen, tvb, 0, 4,
+                   rpc_rm);
+       }
+}
+
+static void
+show_rpc_fragment(tvbuff_t *tvb, proto_tree *tree, guint32 rpc_rm)
+{
+       if (tree) {
+               /*
+                * Show the fragment header and the data for the fragment.
+                */
+               show_rpc_fragheader(tvb, tree, rpc_rm);
+               proto_tree_add_text(tree, tvb, 4, -1, "Fragment Data");
+       }
+}
+
+static void
+make_frag_tree(tvbuff_t *tvb, proto_tree *tree, int proto, gint ett,
+    guint32 rpc_rm)
+{
+       proto_item *frag_item;
+       proto_tree *frag_tree;
+
+       if (tree == NULL)
+               return;         /* nothing to do */
+
+       frag_item = proto_tree_add_protocol_format(tree, proto, tvb, 0, -1,
+           "%s Fragment", proto_get_protocol_name(proto));
+       frag_tree = proto_item_add_subtree(frag_item, ett);
+       show_rpc_fragment(tvb, frag_tree, rpc_rm);
+}
+
+void
+show_rpc_fraginfo(tvbuff_t *tvb, tvbuff_t *frag_tvb, proto_tree *tree,
+    guint32 rpc_rm, fragment_data *ipfd_head, packet_info *pinfo)
+{
+       if (tree == NULL)
+               return;         /* don't do any work */
+
+       if (tvb != frag_tvb) {
+               /*
+                * This message was not all in one fragment,
+                * so show the fragment header *and* the data
+                * for the fragment (which is the last fragment),
+                * and a tree with information about all fragments.
+                */
+               show_rpc_fragment(frag_tvb, tree, rpc_rm);
+
+               /*
+                * Show a tree with information about all fragments.
+                */
+               show_fragment_tree(ipfd_head, &rpc_frag_items, tree, pinfo, tvb);
+       } else {
+               /*
+                * This message was all in one fragment, so just show
+                * the fragment header.
+                */
+               show_rpc_fragheader(tvb, tree, rpc_rm);
+       }
+}
+
+static gboolean
+call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo,
+    proto_tree *tree, tvbuff_t *frag_tvb, rec_dissector_t dissector,
+    fragment_data *ipfd_head, guint32 rpc_rm)
+{
+       const char *saved_proto;
+       volatile gboolean rpc_succeeded;
+
+       /*
+        * Catch the ReportedBoundsError exception; if
+        * this particular message happens to get a
+        * ReportedBoundsError exception, that doesn't
+        * mean that we should stop dissecting RPC
+        * messages within this frame or chunk of
+        * reassembled data.
+        *
+        * If it gets a BoundsError, we can stop, as there's
+        * nothing more to see, so we just re-throw it.
+        */
+       saved_proto = pinfo->current_proto;
+       rpc_succeeded = FALSE;
+       TRY {
+               rpc_succeeded = (*dissector)(rec_tvb, pinfo, tree,
+                   frag_tvb, ipfd_head, TRUE, rpc_rm);
+       }
+       CATCH(BoundsError) {
+               RETHROW;
+       }
+       CATCH(ReportedBoundsError) {
+               show_reported_bounds_error(tvb, pinfo, tree);
+               pinfo->current_proto = saved_proto;
+
+               /*
+                * We treat this as a "successful" dissection of
+                * an RPC packet, as "dissect_rpc_message()"
+                * *did* decide it was an RPC packet, throwing
+                * an exception while dissecting it as such.
+                */
+               rpc_succeeded = TRUE;
+       }
+       ENDTRY;
+       return rpc_succeeded;
+}
+
+int
+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)
+{
+       struct tcpinfo *tcpinfo = pinfo->private_data;
+       guint32 seq = tcpinfo->seq + offset;
+       guint32 rpc_rm;
+       volatile gint32 len;
+       gint32 seglen;
+       gint tvb_len, tvb_reported_len;
+       tvbuff_t *frag_tvb;
+       gboolean rpc_succeeded;
+       gboolean save_fragmented;
+       rpc_fragment_key old_rfk, *rfk, *new_rfk;
+       conversation_t *conversation;
+       fragment_data *ipfd_head;
+       tvbuff_t *rec_tvb;
+
+       /*
+        * Get the record mark.
+        */
+       if (!tvb_bytes_exist(tvb, offset, 4)) {
+               /*
+                * XXX - we should somehow arrange to handle
+                * a record mark split across TCP segments.
+                */
+               return 0;       /* not enough to tell if it's valid */
+       }
+       rpc_rm = tvb_get_ntohl(tvb, offset);
+
+       len = rpc_rm & RPC_RM_FRAGLEN;
+
+       /*
+        * Do TCP desegmentation, if enabled.
+        *
+        * XXX - reject fragments bigger than 2 megabytes.
+        * This is arbitrary, but should at least prevent
+        * some crashes from either packets with really
+        * large RPC-over-TCP fragments or from stuff that's
+        * not really valid for this protocol.
+        */
+       if (len > 2*1024*1024)
+               return 0;       /* pretend it's not valid */
+       if (rpc_desegment) {
+               seglen = tvb_length_remaining(tvb, offset + 4);
+
+               if (len > seglen && pinfo->can_desegment) {
+                       /*
+                        * This frame doesn't have all of the
+                        * data for this message, but we can do
+                        * reassembly on it.
+                        *
+                        * If this is a heuristic dissector, just
+                        * return 0 - we don't want to try to get
+                        * more data, as that's too likely to cause
+                        * us to misidentify this as valid.
+                        *
+                        * If this isn't a heuristic dissector,
+                        * we've already identified this conversation
+                        * as containing data for this protocol, as we
+                        * saw valid data in previous frames.  Try to
+                        * get more data.
+                        */
+                       if (is_heur)
+                               return 0;       /* not valid */
+                       else {
+                               pinfo->desegment_offset = offset;
+                               pinfo->desegment_len = len - seglen;
+                               return -pinfo->desegment_len;
+                       }
+               }
+       }
+       len += 4;       /* include record mark */
+       tvb_len = tvb_length_remaining(tvb, offset);
+       tvb_reported_len = tvb_reported_length_remaining(tvb, offset);
+       if (tvb_len > len)
+               tvb_len = len;
+       if (tvb_reported_len > len)
+               tvb_reported_len = len;
+       frag_tvb = tvb_new_subset(tvb, offset, tvb_len,
+           tvb_reported_len);
+
+       /*
+        * If we're not defragmenting, just hand this to the
+        * disssector.
+        */
+       if (!defragment) {
+               /*
+                * This is the first fragment we've seen, and it's also
+                * the last fragment; that means the record wasn't
+                * fragmented.  Hand the dissector the tvbuff for the
+                * fragment as the tvbuff for the record.
+                */
+               rec_tvb = frag_tvb;
+               ipfd_head = NULL;
+
+               /*
+                * Mark this as fragmented, so if somebody throws an
+                * exception, we don't report it as a malformed frame.
+                */
+               save_fragmented = pinfo->fragmented;
+               pinfo->fragmented = TRUE;
+               rpc_succeeded = call_message_dissector(tvb, rec_tvb, pinfo,
+                   tree, frag_tvb, dissector, ipfd_head, rpc_rm);
+               pinfo->fragmented = save_fragmented;
+               if (!rpc_succeeded)
+                       return 0;       /* not RPC */
+               return len;
+       }
+
+       /*
+        * First, we check to see if this fragment is part of a record
+        * that we're in the process of defragmenting.
+        *
+        * The key is the conversation ID for the conversation to which
+        * the packet belongs and the current sequence number.
+        * We must first find the conversation and, if we don't find
+        * one, create it.  We know this is running over TCP, so the
+        * conversation should not wildcard either address or port.
+        */
+       conversation = find_conversation(&pinfo->src, &pinfo->dst,
+           pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+       if (conversation == NULL) {
+               /*
+                * It's not part of any conversation - create a new one.
+                */
+               conversation = conversation_new(&pinfo->src, &pinfo->dst,
+                   pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+       }
+       old_rfk.conv_id = conversation->index;
+       old_rfk.seq = seq;
+       rfk = g_hash_table_lookup(rpc_reassembly_table, &old_rfk);
+
+       if (rfk == NULL) {
+               /*
+                * This fragment was not found in our table, so it doesn't
+                * contain a continuation of a higher-level PDU.
+                * Is it the last fragment?
+                */
+               if (!(rpc_rm & RPC_RM_LASTFRAG)) {
+                       /*
+                        * This isn't the last fragment, so we don't
+                        * have the complete record.
+                        *
+                        * It's the first fragment we've seen, so if
+                        * it's truly the first fragment of the record,
+                        * and it has enough data, the dissector can at
+                        * least check whether it looks like a valid
+                        * message, as it contains the start of the
+                        * message.
+                        *
+                        * The dissector should not dissect anything
+                        * if the "last fragment" flag isn't set in
+                        * the record marker, so it shouldn't throw
+                        * an exception.
+                        */
+                       if (!(*dissector)(frag_tvb, pinfo, tree, frag_tvb,
+                           NULL, TRUE, rpc_rm))
+                               return 0;       /* not valid */
+
+                       /*
+                        * OK, now start defragmentation with that
+                        * fragment.  Add this fragment, and set up
+                        * next packet/sequence number as well.
+                        *
+                        * We must remember this fragment.
+                        */
+
+                       rfk = g_mem_chunk_alloc(rpc_fragment_key_chunk);
+                       rfk->conv_id = conversation->index;
+                       rfk->seq = seq;
+                       rfk->offset = 0;
+                       rfk->start_seq = seq;
+                       g_hash_table_insert(rpc_reassembly_table, rfk, rfk);
+
+                       /*
+                        * Start defragmentation.
+                        */
+                       ipfd_head = fragment_add(tvb, offset + 4, pinfo,
+                           rfk->start_seq, rpc_fragment_table,
+                           rfk->offset, len - 4, TRUE);
+
+                       /*
+                        * Make sure that defragmentation isn't complete;
+                        * it shouldn't be, as this is the first fragment
+                        * we've seen, and the "last fragment" bit wasn't
+                        * set on it.
+                        */
+                       g_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);
+
+                       /*
+                        * 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;
+               }
+
+               /*
+                * This is the first fragment we've seen, and it's also
+                * the last fragment; that means the record wasn't
+                * fragmented.  Hand the dissector the tvbuff for the
+                * fragment as the tvbuff for the record.
+                */
+               rec_tvb = frag_tvb;
+               ipfd_head = NULL;
+       } else {
+               /*
+                * OK, this fragment was found, which means it continues
+                * a record.  This means we must defragment it.
+                * Add it to the defragmentation lists.
+                */
+               ipfd_head = fragment_add(tvb, offset + 4, pinfo,
+                   rfk->start_seq, rpc_fragment_table,
+                   rfk->offset, len - 4, !(rpc_rm & RPC_RM_LASTFRAG));
+
+               if (ipfd_head == NULL) {
+                       /*
+                        * fragment_add() returned NULL, This means that 
+                        * defragmentation is not completed yet.
+                        *
+                        * We must add an entry to the hash table with
+                        * the sequence number following this fragment
+                        * as the starting sequence number, so that when
+                        * we see that fragment we'll find that entry.
+                        *
+                        * XXX - as TCP stream data is not currently
+                        * guaranteed to be provided in order to dissectors,
+                        * RPC fragments aren't guaranteed to be provided
+                        * in order, either.
+                        */
+                       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);
+
+                       /*
+                        * 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,
+                        * but don't hand it to the dissector
+                        */
+                       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;
+               }
+
+               /*
+                * It's completely defragmented.
+                *
+                * We only call subdissector for the last fragment.
+                * XXX - this assumes in-order delivery of RPC
+                * fragments, which requires in-order delivery of TCP
+                * segments.
+                */
+               if (!(rpc_rm & RPC_RM_LASTFRAG)) {
+                       /*
+                        * Well, it's defragmented, but this isn't
+                        * the last fragment; this probably means
+                        * this isn't the first pass, so we don't
+                        * need to start defragmentation.
+                        *
+                        * 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,
+                        * but don't show it to the dissector.
+                        */
+                       make_frag_tree(frag_tvb, tree, proto, ett, rpc_rm);
+
+                       /*
+                        * No more processing need be done, as we
+                        * only disssect the data with the last
+                        * fragment.
+                        */
+                       return len;
+               }
+
+               /*
+                * OK, this is the last segment.
+                * Create a tvbuff for the defragmented
+                * record.
+                */
+
+               /*
+                * Create a new TVB structure for
+                * defragmented data.
+                */
+               rec_tvb = tvb_new_real_data(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.
+                */
+               add_new_data_source(pinfo, rec_tvb, "Defragmented");
+       }
+
+       /*
+        * We have something to hand to the RPC message
+        * dissector.
+        */
+       if (!call_message_dissector(tvb, rec_tvb, pinfo, tree,
+           frag_tvb, dissector, ipfd_head, rpc_rm))
+               return 0;       /* not RPC */
+       return len;
+}
+
 /*
  * Can return:
  *
@@ -2092,81 +2718,30 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     gboolean is_heur)
 {
        int offset = 0;
-       guint32 rpc_rm;
        gboolean saw_rpc = FALSE;
-       gint32 len, seglen;
-       gint tvb_len, tvb_reported_len;
-       tvbuff_t *msg_tvb;
+       int len;
 
        while (tvb_reported_length_remaining(tvb, offset) != 0) {
                /*
-                * XXX - we need to handle records that don't have the "last
-                * fragment" bit set, and reassemble fragments.
+                * Process this fragment.
                 */
-
-               /* the first 4 bytes are special in "record marking mode" */
-               if (!tvb_bytes_exist(tvb, offset, 4)) {
+               len = dissect_rpc_fragment(tvb, offset, pinfo, tree,
+                   dissect_rpc_message, is_heur, proto_rpc, ett_rpc,
+                   rpc_defragment);
+               if (len < 0) {
                        /*
-                        * XXX - we should somehow arrange to handle
-                        * a record mark split across TCP segments.
+                        * We need more data from the TCP stream for
+                        * this fragment.
                         */
-                       return saw_rpc ? IS_RPC : IS_NOT_RPC;
+                       return NEED_MORE_DATA;
                }
-               rpc_rm = tvb_get_ntohl(tvb, offset);
-
-               len = rpc_rm&RPC_RM_FRAGLEN;
-
-               /*
-                * XXX - reject fragments bigger than 2 megabytes.
-                * This is arbitrary, but should at least prevent
-                * some crashes from either packets with really
-                * large RPC-over-TCP fragments or from stuff that's
-                * not really RPC.
-                */
-               if (len > 2*1024*1024)
-                       return saw_rpc ? IS_RPC : IS_NOT_RPC;
-               if (rpc_desegment) {
-                       seglen = tvb_length_remaining(tvb, offset + 4);
-
-                       if (len > seglen && pinfo->can_desegment) {
-                               /*
-                                * This frame doesn't have all of the
-                                * data for this message, but we can do
-                                * reassembly on it.
-                                *
-                                * If this is a heuristic dissector, just
-                                * return IS_NOT_RPC - we don't want to try
-                                * to get more data, as that's too likely
-                                * to cause us to misidentify this as
-                                * RPC.
-                                *
-                                * If this isn't a heuristic dissector,
-                                * we've already identified this conversation
-                                * as containing RPC data, as we saw RPC
-                                * data in previous frames.  Try to get
-                                * more data.
-                                */
-                               if (is_heur)
-                                       return IS_NOT_RPC;
-                               else {
-                                       pinfo->desegment_offset = offset;
-                                       pinfo->desegment_len = len - seglen;
-                                       return NEED_MORE_DATA;
-                               }
-                       }
-               }
-               len += 4;       /* include record mark */
-               tvb_len = tvb_length_remaining(tvb, offset);
-               tvb_reported_len = tvb_reported_length_remaining(tvb, offset);
-               if (tvb_len > len)
-                       tvb_len = len;
-               if (tvb_reported_len > len)
-                       tvb_reported_len = len;
-               msg_tvb = tvb_new_subset(tvb, offset, tvb_len,
-                   tvb_reported_len);
-               if (!dissect_rpc_message(msg_tvb, 4, pinfo, tree,
-                   TRUE, rpc_rm))
+               if (len == 0) {
+                       /*
+                        * It's not RPC.  Stop processing.
+                        */
                        break;
+               }
+
                offset += len;
                saw_rpc = TRUE;
        }
@@ -2202,14 +2777,30 @@ dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 static void
 rpc_init_protocol(void)
 {
-       if (rpc_calls != NULL)
+       if (rpc_calls != NULL) {
                g_hash_table_destroy(rpc_calls);
-       if (rpc_indir_calls != NULL)
+               rpc_calls = NULL;
+       }
+       if (rpc_indir_calls != NULL) {
                g_hash_table_destroy(rpc_indir_calls);
-       if (rpc_call_info_key_chunk != NULL)
+               rpc_indir_calls = NULL;
+       }
+       if (rpc_call_info_key_chunk != NULL) {
                g_mem_chunk_destroy(rpc_call_info_key_chunk);
-       if (rpc_call_info_value_chunk != NULL)
+               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);
@@ -2221,6 +2812,14 @@ rpc_init_protocol(void)
            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);
+
+       fragment_table_init(&rpc_fragment_table);
 }
 
 /* will be called once from register.c at startup time */
@@ -2229,11 +2828,11 @@ proto_register_rpc(void)
 {
        static hf_register_info hf[] = {
                { &hf_rpc_lastfrag, {
-                       "Last Fragment", "rpc.lastfrag", FT_BOOLEAN, BASE_NONE,
-                       &yesno, 0, "Last Fragment", HFILL }},
+                       "Last Fragment", "rpc.lastfrag", FT_BOOLEAN, 32,
+                       &yesno, RPC_RM_LASTFRAG, "Last Fragment", HFILL }},
                { &hf_rpc_fraglen, {
                        "Fragment Length", "rpc.fraglen", FT_UINT32, BASE_DEC,
-                       NULL, 0, "Fragment Length", HFILL }},
+                       NULL, RPC_RM_FRAGLEN, "Fragment Length", HFILL }},
                { &hf_rpc_xid, {
                        "XID", "rpc.xid", FT_UINT32, BASE_HEX,
                        NULL, 0, "XID", HFILL }},
@@ -2369,9 +2968,44 @@ proto_register_rpc(void)
                { &hf_rpc_array_len, {
                        "num", "rpc.array.len", FT_UINT32, BASE_DEC,
                        NULL, 0, "Length of RPC array", HFILL }},
+
+               { &hf_rpc_time, {
+                       "Time from request", "rpc.time", FT_RELATIVE_TIME, BASE_NONE,
+                       NULL, 0, "Time between Request and Reply for ONC-RPC calls", HFILL }},
+
+               { &hf_rpc_fragment_overlap,
+               { "Fragment overlap",   "rpc.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Fragment overlaps with other fragments", HFILL }},
+
+               { &hf_rpc_fragment_overlap_conflict,
+               { "Conflicting data in fragment overlap",       "rpc.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Overlapping fragments contained conflicting data", HFILL }},
+
+               { &hf_rpc_fragment_multiple_tails,
+               { "Multiple tail fragments found",      "rpc.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Several tails were found when defragmenting the packet", HFILL }},
+
+               { &hf_rpc_fragment_too_long_fragment,
+               { "Fragment too long",  "rpc.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Fragment contained data past end of packet", HFILL }},
+
+               { &hf_rpc_fragment_error,
+               { "Defragmentation error", "rpc.fragment.error", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "Defragmentation error due to illegal fragments", HFILL }},
+
+               { &hf_rpc_fragment,
+               { "RPC Fragment", "rpc.fragment", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "RPC Fragment", HFILL }},
+
+               { &hf_rpc_fragments,
+               { "RPC Fragments", "rpc.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "RPC Fragments", HFILL }},
        };
        static gint *ett[] = {
                &ett_rpc,
+               &ett_rpc_fragments,
+               &ett_rpc_fragment,
+               &ett_rpc_fraghdr,
                &ett_rpc_string,
                &ett_rpc_cred,
                &ett_rpc_verf,
@@ -2386,11 +3020,21 @@ proto_register_rpc(void)
        proto_register_field_array(proto_rpc, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
        register_init_routine(&rpc_init_protocol);
+
        rpc_module = prefs_register_protocol(proto_rpc, NULL);
        prefs_register_bool_preference(rpc_module, "desegment_rpc_over_tcp",
-               "Desegment all RPC over TCP commands",
-               "Whether the RPC dissector should desegment all RPC over TCP commands",
+               "Desegment all RPC-over-TCP messages",
+               "Whether the RPC dissector should desegment all RPC-over-TCP messages",
                &rpc_desegment);
+       prefs_register_bool_preference(rpc_module, "defragment_rpc_over_tcp",
+               "Defragment all RPC-over-TCP messages",
+               "Whether the RPC dissector should defragment multi-fragment RPC-over-TCP messages",
+               &rpc_defragment);
+
+       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");
 
        /*
         * Init the hash tables.  Dissectors for RPC protocols must
@@ -2412,4 +3056,5 @@ proto_reg_handoff_rpc(void)
 {
        heur_dissector_add("tcp", dissect_rpc_tcp_heur, proto_rpc);
        heur_dissector_add("udp", dissect_rpc_heur, proto_rpc);
+       data_handle = find_dissector("data");
 }