Use "%u", not "%d", to print unsigned integral quantities.
[obnox/wireshark/wip.git] / packet-rpc.c
index b97eca666abbdfcb21b895bf34891d36ac52f370..e279cb42b38c6d0680f45d03f733c268489c0a43 100644 (file)
@@ -2,10 +2,10 @@
  * Routines for rpc dissection
  * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
  * 
- * $Id: packet-rpc.c,v 1.18 1999/11/19 13:09:56 gram Exp $
+ * $Id: packet-rpc.c,v 1.42 2000/10/21 05:52:21 guy Exp $
  * 
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@unicom.net>
+ * By Gerald Combs <gerald@zing.org>
  * Copyright 1998 Gerald Combs
  * 
  * Copied from packet-smb.c
 #include "conversation.h"
 #include "packet-rpc.h"
 
+/*
+ * See:
+ *
+ *     RFC 1831, "RPC: Remote Procedure Call Protocol Specification
+ *     Version 2";
+ *
+ *     RFC 1832, "XDR: External Data Representation Standard";
+ *
+ *     RFC 2203, "RPCSEC_GSS Protocol Specification".
+ *
+ * See also
+ *
+ *     RFC 2695, "Authentication Mechanisms for ONC RPC"
+ *
+ *     although we don't currently dissec AUTH_DES or AUTH_KERB.
+ */
 
 #define RPC_RM_FRAGLEN  0x7fffffffL
 
 static struct true_false_string yesno = { "Yes", "No" };
 
 
-static const value_string rpc_msg_type[3] = {
+static const value_string rpc_msg_type[] = {
        { RPC_CALL, "Call" },
        { RPC_REPLY, "Reply" },
        { 0, NULL }
 };
 
-static const value_string rpc_reply_state[3] = {
+static const value_string rpc_reply_state[] = {
        { MSG_ACCEPTED, "accepted" },
        { MSG_DENIED, "denied" },
        { 0, NULL }
 };
 
-value_string rpc_auth_flavor[] = {
+const value_string rpc_auth_flavor[] = {
        { AUTH_NULL, "AUTH_NULL" },
        { AUTH_UNIX, "AUTH_UNIX" },
        { AUTH_SHORT, "AUTH_SHORT" },
        { AUTH_DES, "AUTH_DES" },
+       { RPCSEC_GSS, "RPCSEC_GSS" },
+       { 0, NULL }
+};
+
+static const value_string rpc_authgss_proc[] = {
+       { RPCSEC_GSS_DATA, "RPCSEC_GSS_DATA" },
+       { RPCSEC_GSS_INIT, "RPCSEC_GSS_INIT" },
+       { RPCSEC_GSS_CONTINUE_INIT, "RPCSEC_GSS_CONTINUE_INIT" },
+       { RPCSEC_GSS_DESTROY, "RPCSEC_GSS_DESTROY" },
+       { 0, NULL }
+};
+
+static 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" },
        { 0, NULL }
 };
 
-static const value_string rpc_accept_state[6] = {
+static const value_string rpc_accept_state[] = {
        { SUCCESS, "RPC executed successfully" },
        { PROG_UNAVAIL, "remote hasn't exported program" },
        { PROG_MISMATCH, "remote can't support version #" },
@@ -76,18 +108,20 @@ static const value_string rpc_accept_state[6] = {
        { 0, NULL }
 };
 
-static const value_string rpc_reject_state[3] = {
+static const value_string rpc_reject_state[] = {
        { RPC_MISMATCH, "RPC_MISMATCH" },
        { AUTH_ERROR, "AUTH_ERROR" },
        { 0, NULL }
 };
 
-static const value_string rpc_auth_state[6] = {
+static const value_string rpc_auth_state[] = {
        { AUTH_BADCRED, "bad credential (seal broken)" },
        { AUTH_REJECTEDCRED, "client must begin new session" },
        { AUTH_BADVERF, "bad verifier (seal broken)" },
        { AUTH_REJECTEDVERF, "verifier expired or replayed" },
        { AUTH_TOOWEAK, "rejected for security reasons" },
+       { RPCSEC_GSSCREDPROB, "GSS credential problem" },
+       { RPCSEC_GSSCTXPROB, "GSS context problem" },
        { 0, NULL }
 };
 
@@ -112,16 +146,33 @@ static int hf_rpc_auth_machinename = -1;
 static int hf_rpc_auth_stamp = -1;
 static int hf_rpc_auth_uid = -1;
 static int hf_rpc_auth_gid = -1;
+static int hf_rpc_authgss_v = -1;
+static int hf_rpc_authgss_proc = -1;
+static int hf_rpc_authgss_seq = -1;
+static int hf_rpc_authgss_svc = -1;
+static int hf_rpc_authgss_ctx = -1;
+static int hf_rpc_authgss_major = -1;
+static int hf_rpc_authgss_minor = -1;
+static int hf_rpc_authgss_window = -1;
+static int hf_rpc_authgss_token = -1;
+static int hf_rpc_authgss_data_length = -1;
+static int hf_rpc_authgss_data = -1;
+static int hf_rpc_authgss_checksum = -1;
 static int hf_rpc_state_accept = -1;
 static int hf_rpc_state_reply = -1;
 static int hf_rpc_state_reject = -1;
 static int hf_rpc_state_auth = -1;
+static int hf_rpc_dup = -1;
+static int hf_rpc_call_dup = -1;
+static int hf_rpc_reply_dup = -1;
+static int hf_rpc_value_follows = -1;
 
 static gint ett_rpc = -1;
 static gint ett_rpc_string = -1;
 static gint ett_rpc_cred = -1;
 static gint ett_rpc_verf = -1;
 static gint ett_rpc_gids = -1;
+static gint ett_rpc_gss_data = -1;
 
 /* Hash table with info on RPC program numbers */
 static GHashTable *rpc_progs;
@@ -265,28 +316,28 @@ char *rpc_prog_name(guint32 prog)
 /* end of Hash array with program names */
 /*--------------------------------------*/
 
-
-/*
- * Init the hash tables. It will be called from ethereal_proto_init().
- * ethereal_proto_init() calls later proto_init(), which calls 
- * register_all_protocols().
- * The proto_register_<some rpc program> functions use these hash tables
- * here, so we need this order!
- */
-void
-init_dissect_rpc()
-{
-       rpc_progs = g_hash_table_new(rpc_prog_hash, rpc_prog_equal);
-       rpc_procs = g_hash_table_new(rpc_proc_hash, rpc_proc_equal);
-}
-
 /* static array, first quick implementation, I'll switch over to GList soon */ 
+typedef struct _rpc_call_info {
+       guint32 xid;
+       conversation_t *conversation;
+       guint32 req_num;        /* frame number of first request seen */
+       guint32 rep_num;        /* frame number of first reply seen */
+       guint32 prog;
+       guint32 vers;
+       guint32 proc;
+       guint32 flavor;
+       guint32 gss_proc;
+       guint32 gss_svc;
+       rpc_proc_info_value*    proc_info;
+} rpc_call_info;
+
+#define RPC_CALL_TABLE_LENGTH 1000
+
 rpc_call_info rpc_call_table[RPC_CALL_TABLE_LENGTH];
 guint32 rpc_call_index = 0;
 guint32 rpc_call_firstfree = 0;
 
-void
+static void
 rpc_call_insert(rpc_call_info *call)
 {
        /* some space left? */
@@ -310,7 +361,7 @@ rpc_call_insert(rpc_call_info *call)
 }
 
 
-rpc_call_info*
+static rpc_call_info*
 rpc_call_lookup(rpc_call_info *call)
 {
        int i;
@@ -341,9 +392,35 @@ rpc_roundup(unsigned int a)
 }
 
 
+int
+dissect_rpc_bool(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
+int hfindex)
+{
+       guint32 value;
+
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       value = EXTRACT_UINT(pd, offset+0);
+       if (tree)
+               proto_tree_add_boolean(tree, hfindex, NullTVB, offset, 4, value);
+       offset += 4;
+
+       return offset;
+}
+
+
+int
+dissect_rpc_bool_tvb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+int hfindex, int offset)
+{
+       if (tree)
+               proto_tree_add_item(tree, hfindex, tvb, offset, 4, FALSE);
+       return offset + 4;
+}
+
+
 int
 dissect_rpc_uint32(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
-char* name, char* type)
+char* name)
 {
        guint32 value;
 
@@ -351,7 +428,7 @@ char* name, char* type)
        value = EXTRACT_UINT(pd, offset+0);
 
        if (tree) {
-               proto_tree_add_text(tree, offset, 4,
+               proto_tree_add_text(tree, NullTVB, offset, 4,
                "%s: %u", name, value);
        }
 
@@ -360,9 +437,19 @@ char* name, char* type)
 }
 
 
+int
+dissect_rpc_uint32_tvb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+int hfindex, int offset)
+{
+       if (tree)
+               proto_tree_add_item(tree, hfindex, tvb, offset, 4, FALSE);
+       return offset + 4;
+}
+
+
 int
 dissect_rpc_uint64(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
-char* name, char* type)
+char* name)
 {
        guint32 value_low;
        guint32 value_high;
@@ -373,10 +460,10 @@ char* name, char* type)
 
        if (tree) {
                if (value_high)
-                       proto_tree_add_text(tree, offset, 8,
-                               "%s: %x%08x", name, value_high, value_low);
+                       proto_tree_add_text(tree, NullTVB, offset, 8,
+                               "%s: 0x%x%08x", name, value_high, value_low);
                else
-                       proto_tree_add_text(tree, offset, 8,
+                       proto_tree_add_text(tree, NullTVB, offset, 8,
                                "%s: %u", name, value_low);
        }
 
@@ -386,18 +473,49 @@ char* name, char* type)
 
 
 int
-dissect_rpc_string(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hfindex)
+dissect_rpc_uint64_tvb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+int hfindex, int offset)
+{
+       guint32 value_low;
+       guint32 value_high;
+
+       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);
+       }
+
+       return offset + 8;
+}
+
+
+static int
+dissect_rpc_opaque_data(const u_char *pd, int offset, frame_data *fd,
+    proto_tree *tree, int hfindex, gboolean string_data,
+    char **string_buffer_ret)
 {
        proto_item *string_item = NULL;
        proto_tree *string_tree = NULL;
        int old_offset = offset;
 
-       int truncated_length = 0;
+       int length_truncated = 0;
+
+       int string_truncated = 0;
        guint32 string_length = 0;
+       guint32 string_length_full;
        guint32 string_length_packet;
        guint32 string_length_copy = 0;
-       guint32 string_length_full;
-       guint32 string_fill = 0;
+
+       int fill_truncated = 0;
+       guint32 fill_length  = 0;
+       guint32 fill_length_packet  = 0;
+       guint32 fill_length_copy  = 0;
+
        char *string_buffer = NULL;
        char *string_buffer_print = NULL;
 
@@ -405,31 +523,69 @@ dissect_rpc_string(const u_char *pd, int offset, frame_data *fd, proto_tree *tre
                string_length = EXTRACT_UINT(pd,offset+0);
                string_length_full = rpc_roundup(string_length);
                string_length_packet = pi.captured_len - (offset + 4);
-               if (string_length > string_length_packet) {
+               if (string_length_packet < string_length) {
+                       /* truncated string */
+                       string_truncated = 1;
                        string_length_copy = string_length_packet;
-                       string_fill = 0;
+                       fill_truncated = 2;
+                       fill_length = 0;
+                       fill_length_packet = 0;
+                       fill_length_copy = 0;
                }
                else {
+                       /* full string data */
+                       string_truncated = 0;
                        string_length_copy = string_length;
-                       string_fill = string_length_full - string_length;
+                       fill_length = string_length_full - string_length;
+                       fill_length_packet = pi.captured_len - (offset + 4 + string_length);
+                       if (fill_length_packet < fill_length) {
+                               /* truncated fill bytes */
+                               fill_length_copy = fill_length_packet;
+                               fill_truncated = 1;
+                       }
+                       else {
+                               /* full fill bytes */
+                               fill_length_copy = fill_length;
+                               fill_truncated = 0;
+                       }
                }
-               string_buffer = (char*)g_malloc(string_length_copy + 1);
+               string_buffer = (char*)g_malloc(string_length_copy + 
+                       (string_data ? 1 : 0));
                memcpy(string_buffer,pd+offset+4,string_length_copy);
-               string_buffer[string_length_copy] = '\0';
-
-               /* I use here strdup functions. This works only, if the
-                  string does not contain 0 bytes. */
+               if (string_data)
+                       string_buffer[string_length_copy] = '\0';
 
                /* calculate a nice printable string */
                if (string_length) {
                        if (string_length != string_length_copy) {
-                               string_buffer_print = (char*)g_malloc(string_length_copy + 1 + 12);
-                               memcpy(string_buffer_print,string_buffer,string_length_copy);
-                               memcpy(string_buffer_print+string_length_copy,
-                                       "<TRUNCATED>", 12);
+                               if (string_data) {
+                                       /* alloc maximum data area */
+                                       string_buffer_print = (char*)g_malloc(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>");
+                               }
+                               else {
+                                       string_buffer_print = g_strdup("<DATA><TRUNCATED>");
+                               }
                        }
                        else {
-                               string_buffer_print = g_strdup(string_buffer);
+                               if (string_data) {
+                                       string_buffer_print = g_strdup(string_buffer);
+                               }
+                               else {
+                                       string_buffer_print = g_strdup("<DATA>");
+                               }
                        }
                }
                else {
@@ -437,130 +593,281 @@ dissect_rpc_string(const u_char *pd, int offset, frame_data *fd, proto_tree *tre
                }
        }
        else {
-               truncated_length = 1;
+               length_truncated = 1;
+               string_truncated = 2;
+               fill_truncated = 2;
                string_buffer = g_strdup("");
                string_buffer_print = g_strdup("<TRUNCATED>");
        }
 
        if (tree) {
-               string_item = proto_tree_add_text(tree,offset+0, END_OF_FRAME,
+               string_item = proto_tree_add_text(tree, NullTVB,offset+0, END_OF_FRAME,
                        "%s: %s", proto_registrar_get_name(hfindex), string_buffer_print);
-               proto_tree_add_item_hidden(tree, hfindex, offset+4,
-                       string_length, string_buffer);
+               if (string_data) {
+                       proto_tree_add_string_hidden(tree, hfindex, NullTVB, offset+4,
+                               string_length_copy, string_buffer);
+               }
                if (string_item) {
                        string_tree = proto_item_add_subtree(string_item, ett_rpc_string);
                }
        }
-       if (string_tree) {
-               if (truncated_length) {
-                       proto_tree_add_text(string_tree,
+       if (length_truncated) {
+               if (string_tree)
+                       proto_tree_add_text(string_tree, NullTVB,
                                offset,pi.captured_len-offset,
                                "length: <TRUNCATED>");
-                       offset = pi.captured_len;
-               }
-               else {
-                       proto_tree_add_text(string_tree,offset+0,4,
+               offset = pi.captured_len;
+       } else {
+               if (string_tree)
+                       proto_tree_add_text(string_tree, NullTVB,offset+0,4,
                                "length: %u", string_length);
-                       offset += 4;
-               }
-               proto_tree_add_text(string_tree,offset,string_length_copy,
-                       "text: %s", string_buffer_print);
+               offset += 4;
+
+               if (string_tree)
+                       proto_tree_add_text(string_tree, NullTVB,offset,string_length_copy,
+                               "contents: %s", string_buffer_print);
                offset += string_length_copy;
-               if (string_fill) {
-                       proto_tree_add_text(string_tree,offset,string_fill,
-                               "fill bytes: opaque data");
-                       offset += string_fill;
+               if (fill_length) {
+                       if (string_tree) {
+                               if (fill_truncated) {
+                                       proto_tree_add_text(string_tree, NullTVB,
+                                       offset,fill_length_copy,
+                                       "fill bytes: opaque data<TRUNCATED>");
+                               }
+                               else {
+                                       proto_tree_add_text(string_tree, NullTVB,
+                                       offset,fill_length_copy,
+                                       "fill bytes: opaque data");
+                               }
+                       }
+                       offset += fill_length_copy;
                }
        }
-
+       
        if (string_item) {
                proto_item_set_len(string_item, offset - old_offset);
        }
 
        if (string_buffer       != NULL) g_free (string_buffer      );
-       if (string_buffer_print != NULL) g_free (string_buffer_print);
+       if (string_buffer_print != NULL) {
+               if (string_buffer_ret != NULL)
+                       *string_buffer_ret = string_buffer_print;
+               else
+                       g_free (string_buffer_print);
+       }
        return offset;
 }
 
 
-void
-dissect_rpc_auth( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+int
+dissect_rpc_string(const u_char *pd, int offset, frame_data *fd,
+    proto_tree *tree, int hfindex, char **string_buffer_ret)
 {
-        guint flavor;
-        guint length;
-        guint length_full;
+       offset = dissect_rpc_opaque_data(pd, offset, fd, tree, hfindex, TRUE,
+           string_buffer_ret);
 
-       /* both checks are made outside */
-       /* if (!BYTES_ARE_IN_FRAME(offset,8)) return; */
-       flavor = EXTRACT_UINT(pd,offset+0);
-       length = EXTRACT_UINT(pd,offset+4);
-       length_full = rpc_roundup(length);
-       /* if (!BYTES_ARE_IN_FRAME(offset+8,full_length)) return; */
+       return offset;
+}
+
+
+int
+dissect_rpc_string_tvb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int hfindex, int offset, char **string_buffer_ret)
+{
+       const guint8 *pd;
+       int compat_offset;
+       int compat_offset_new;
+
+       tvb_compat(tvb, &pd, &compat_offset);
+       compat_offset += offset;
+       
+       compat_offset_new = dissect_rpc_string(pd, compat_offset, pinfo->fd,
+                               tree, hfindex, string_buffer_ret);
+       offset += (compat_offset_new - compat_offset);
+       return offset;
+}
+
+
+int
+dissect_rpc_data(const u_char *pd, int offset, frame_data *fd,
+    proto_tree *tree, int hfindex)
+{
+       offset = dissect_rpc_opaque_data(pd, offset, fd, tree, hfindex, FALSE,
+           NULL);
+
+       return offset;
+}
+
+
+int
+dissect_rpc_data_tvb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int hfindex, int offset)
+{
+       const guint8 *pd;
+       int compat_offset;
+       int compat_offset_new;
+
+       tvb_compat(tvb, &pd, &compat_offset);
+       compat_offset += offset;
+       
+       compat_offset_new = dissect_rpc_data(pd, compat_offset, pinfo->fd,
+                               tree, hfindex);
+       offset += (compat_offset_new - compat_offset);
+       return offset;
+}
+
+
+int
+dissect_rpc_list(const u_char *pd, int offset, frame_data *fd,
+       proto_tree *tree, dissect_function_t *rpc_list_dissector)
+{
+       guint32 value_follows;
+
+       while (1) {
+               if (!BYTES_ARE_IN_FRAME(offset,4)) break;
+               value_follows = EXTRACT_UINT(pd, offset+0);
+               proto_tree_add_boolean(tree,hf_rpc_value_follows, NullTVB,
+                       offset+0, 4, value_follows);
+               offset += 4;
+               if (value_follows == 1) {
+                       offset = rpc_list_dissector(pd, offset, fd, tree);
+               }
+               else {
+                       break;
+               }
+       }
+
+       return offset;
+}
+
+static int
+dissect_rpc_authunix_cred(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+       guint stamp;
+       guint uid;
+       guint gid;
+       guint gids_count;
+       guint gids_i;
+       guint gids_entry;
+       proto_item *gitem;
+       proto_tree *gtree = NULL;
+
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       stamp = EXTRACT_UINT(pd,offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_auth_stamp, NullTVB,
+                       offset+0, 4, stamp);
+       offset += 4;
+
+       offset = dissect_rpc_string(pd,offset,fd,
+               tree,hf_rpc_auth_machinename,NULL);
+
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       uid = EXTRACT_UINT(pd,offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_auth_uid, NullTVB,
+                       offset+0, 4, uid);
+       offset += 4;
+
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       gid = EXTRACT_UINT(pd,offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_auth_gid, NullTVB,
+                       offset+0, 4, gid);
+       offset += 4;
 
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       gids_count = EXTRACT_UINT(pd,offset+0);
        if (tree) {
-               proto_tree_add_item(tree, hf_rpc_auth_flavor, offset+0, 4,
-                       flavor);
-               proto_tree_add_item(tree, hf_rpc_auth_length, offset+4, 4,
-                       length);
+               gitem = proto_tree_add_text(tree, NullTVB, offset, 4+gids_count*4,
+               "Auxiliary GIDs");
+               gtree = proto_item_add_subtree(gitem, ett_rpc_gids);
+       }
+       offset += 4;
+       
+       if (!BYTES_ARE_IN_FRAME(offset,4*gids_count)) return offset;
+       for (gids_i = 0 ; gids_i < gids_count ; gids_i++) {
+               gids_entry = EXTRACT_UINT(pd,offset+0);
+               if (gtree)
+               proto_tree_add_uint(gtree, hf_rpc_auth_gid, NullTVB,
+                       offset, 4, gids_entry);
+               offset+=4;
        }
+       /* how can I NOW change the gitem to print a list with
+               the first 16 gids? */
 
-       offset += 8;
+       return offset;
+}
 
-       switch (flavor) {
-               case AUTH_UNIX: {
-                       guint stamp;
-                       guint uid;
-                       guint gid;
-                       guint gids_count;
-                       guint gids_i;
-                       guint gids_entry;
-                       proto_item *gitem;
-                       proto_tree *gtree = NULL;
-
-                       if (!BYTES_ARE_IN_FRAME(offset,4)) return;
-                       stamp = EXTRACT_UINT(pd,offset+0);
-                       if (tree)
-                               proto_tree_add_item(tree, hf_rpc_auth_stamp,
-                                       offset+0, 4, stamp);
-                       offset += 4;
+static int
+dissect_rpc_authgss_cred(const u_char *pd, int offset,
+                        frame_data *fd, proto_tree *tree)
+{
+       guint agc_v;
+       guint agc_proc;
+       guint agc_seq;
+       guint agc_svc;
 
-                       offset = dissect_rpc_string(pd,offset,fd,
-                               tree,hf_rpc_auth_machinename);
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       agc_v = EXTRACT_UINT(pd, offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_v,
+                                   NullTVB, offset+0, 4, agc_v);
+       offset += 4;
+       
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       agc_proc = EXTRACT_UINT(pd, offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_proc,
+                                   NullTVB, offset+0, 4, agc_proc);
+       offset += 4;
+       
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       agc_seq = EXTRACT_UINT(pd, offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_seq,
+                                   NullTVB, offset+0, 4, agc_seq);
+       offset += 4;
+       
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       agc_svc = EXTRACT_UINT(pd, offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_svc,
+                                   NullTVB, offset+0, 4, agc_svc);
+       offset += 4;
+       
+       offset = dissect_rpc_data(pd,offset,fd,tree,
+                                 hf_rpc_authgss_ctx);
+       
+       return offset;
+}
 
-                       if (!BYTES_ARE_IN_FRAME(offset,4)) return;
-                       uid = EXTRACT_UINT(pd,offset+0);
-                       if (tree)
-                               proto_tree_add_item(tree, hf_rpc_auth_uid,
-                                       offset+0, 4, uid);
-                       offset += 4;
+static int
+dissect_rpc_cred( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
+{
+       guint flavor;
+       guint length;
 
-                       if (!BYTES_ARE_IN_FRAME(offset,4)) return;
-                       gid = EXTRACT_UINT(pd,offset+0);
-                       if (tree)
-                               proto_tree_add_item(tree, hf_rpc_auth_gid,
-                                       offset+0, 4, gid);
-                       offset += 4;
+       proto_item *citem;
+       proto_tree *ctree;
 
-                       if (!BYTES_ARE_IN_FRAME(offset,4)) return;
-                       gids_count = EXTRACT_UINT(pd,offset+0);
-                       if (tree) {
-                               gitem = proto_tree_add_text(tree, offset, 4+gids_count*4,
-                               "Auxiliary GIDs");
-                               gtree = proto_item_add_subtree(gitem, ett_rpc_gids);
-                       }
-                       offset += 4;
-                       if (!BYTES_ARE_IN_FRAME(offset,4*gids_count)) return;
-                       for (gids_i = 0 ; gids_i < gids_count ; gids_i++) {
-                               gids_entry = EXTRACT_UINT(pd,offset+0);
-                               if (gtree)
-                               proto_tree_add_item(gtree, hf_rpc_auth_gid,
-                                       offset, 4, gids_entry);
-                               offset+=4;
-                       }
-                       /* how can I NOW change the gitem to print a list with
-                               the first 16 gids? */
-               }
-               break;
+       if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
+       flavor = EXTRACT_UINT(pd,offset+0);
+       length = EXTRACT_UINT(pd,offset+4);
+       length = rpc_roundup(length);
+       if (!BYTES_ARE_IN_FRAME(offset+8,length)) return offset;
+
+       if (tree) {
+               citem = proto_tree_add_text(tree, NullTVB, offset,
+                                           8+length, "Credentials");
+               ctree = proto_item_add_subtree(citem, ett_rpc_cred);
+               proto_tree_add_uint(ctree, hf_rpc_auth_flavor, NullTVB,
+                                   offset+0, 4, flavor);
+               proto_tree_add_uint(ctree, hf_rpc_auth_length, NullTVB,
+                                   offset+4, 4, length);
+
+               switch (flavor) {
+               case AUTH_UNIX:
+                       dissect_rpc_authunix_cred(pd, offset+8, fd, ctree);
+                       break;
                /*
                case AUTH_SHORT:
 
@@ -573,64 +880,147 @@ dissect_rpc_auth( const u_char *pd, int offset, frame_data *fd, proto_tree *tree
 
                break;
                */
+               case RPCSEC_GSS:
+                       dissect_rpc_authgss_cred(pd, offset+8, fd, ctree);
+                       break;
                default:
-                       if (length_full) {
-                               if (tree)
-                               proto_tree_add_text(tree,offset,
-                               length_full, "opaque data");
-                       }
+                       if (length)
+                               proto_tree_add_text(ctree, NullTVB, offset+8,
+                                                   length,"opaque data");
+                       break;
        }
+       }
+       offset += 8 + length;
+
+       return offset;
 }
 
-int
-dissect_rpc_cred( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
+static int
+dissect_rpc_verf( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
 {
+       guint flavor;
        guint length;
-       guint length_full;
-       proto_item *citem;
-       proto_tree *ctree;
+       
+       proto_item *vitem;
+       proto_tree *vtree;
 
        if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
+       flavor = EXTRACT_UINT(pd,offset+0);
        length = EXTRACT_UINT(pd,offset+4);
-       length_full = rpc_roundup(length);
-       if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
+       length = rpc_roundup(length);
+       if (!BYTES_ARE_IN_FRAME(offset+8,length)) return offset;
 
        if (tree) {
-               citem = proto_tree_add_text(tree, offset, 8+length_full,
-                       "Credentials");
-               ctree = proto_item_add_subtree(citem, ett_rpc_cred);
-               dissect_rpc_auth(pd, offset, fd, ctree);
+               vitem = proto_tree_add_text(tree, NullTVB, offset,
+                                           8+length, "Verifier");
+               vtree = proto_item_add_subtree(vitem, ett_rpc_verf);
+               proto_tree_add_uint(vtree, hf_rpc_auth_flavor, NullTVB,
+                                   offset+0, 4, flavor);
+
+               switch (flavor) {
+               case AUTH_UNIX:
+                       proto_tree_add_uint(vtree, hf_rpc_auth_length, NullTVB,
+                                           offset+4, 4, length);
+                       dissect_rpc_authunix_cred(pd, offset+8, fd, vtree);
+                       break;
+               case RPCSEC_GSS:
+                       dissect_rpc_data(pd, offset+4, fd, vtree,
+                                        hf_rpc_authgss_checksum);
+                       break;
+               default:
+                       proto_tree_add_uint(vtree, hf_rpc_auth_length, NullTVB,
+                                           offset+4, 4, length);
+                       if (length)
+                               proto_tree_add_text(vtree, NullTVB, offset+8,
+                                                   length, "opaque data");
+                       break;
+               }
        }
-       offset += 8 + length_full;
+       offset += 8 + length;
 
        return offset;
 }
 
+static int
+dissect_rpc_authgss_initarg(const u_char *pd, int offset,
+                           frame_data *fd, proto_tree *tree)
+{
+       offset = dissect_rpc_data(pd, offset, fd, tree, hf_rpc_authgss_token);
+       return offset;
+}
 
-int
-dissect_rpc_verf( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
+static int
+dissect_rpc_authgss_initres(const u_char *pd, int offset,
+                           frame_data *fd, proto_tree *tree)
 {
-       unsigned int length;
-       unsigned int length_full;
-       proto_item *vitem;
-       proto_tree *vtree;
+       int major, minor, window;
+       
+       offset = dissect_rpc_data(pd, offset, fd, tree, hf_rpc_authgss_ctx);
+       
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       major = EXTRACT_UINT(pd,offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_major, NullTVB,
+                                   offset+0, 4, major);
+       offset += 4;
 
-       if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
-       length = EXTRACT_UINT(pd,offset+4);
-       length_full = rpc_roundup(length);
-       if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       minor = EXTRACT_UINT(pd,offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_minor, NullTVB,
+                                   offset+0, 4, minor);
+       offset += 4;
+
+       if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
+       window = EXTRACT_UINT(pd,offset+0);
+       if (tree)
+               proto_tree_add_uint(tree, hf_rpc_authgss_window, NullTVB,
+                                   offset+0, 4, window);
+       offset += 4;
+
+       offset = dissect_rpc_data(pd, offset, fd, tree, hf_rpc_authgss_token);
+
+       return offset;
+}
+
+static int
+dissect_rpc_authgss_integ_data(const u_char *pd, int offset,
+                              frame_data *fd, proto_tree *tree,
+                              dissect_function_t *dissect_function)
+{
+       guint32 length, seq;
+       
+       proto_item *gitem;
+       proto_tree *gtree;
+
+       if (!BYTES_ARE_IN_FRAME(offset, 8)) return offset;
+       length = EXTRACT_UINT(pd, offset+0);
+       length = rpc_roundup(length);
+       seq = EXTRACT_UINT(pd,offset+4);
 
        if (tree) {
-               vitem = proto_tree_add_text(tree, offset, 8+length_full,
-                       "Verifier");
-               vtree = proto_item_add_subtree(vitem, ett_rpc_verf);
-               dissect_rpc_auth(pd, offset, fd, vtree);
+               gitem = proto_tree_add_text(tree, NullTVB, offset,
+                                           4+length, "GSS Data");
+               gtree = proto_item_add_subtree(gitem, ett_rpc_gss_data);
+               proto_tree_add_uint(gtree, hf_rpc_authgss_data_length,
+                                   NullTVB, offset+0, 4, length);
+               proto_tree_add_uint(gtree, hf_rpc_authgss_seq,
+                                   NullTVB, offset+4, 4, seq);
+               if (dissect_function != NULL)
+                       offset = dissect_function(pd, offset, fd, gtree);
        }
-       offset += 8 + length_full;
-
+       offset += 8 + length;
+       offset = dissect_rpc_data(pd, offset, fd, tree, hf_rpc_authgss_checksum);
        return offset;
 }
 
+static int
+dissect_rpc_authgss_priv_data(const u_char *pd, int offset,
+                        frame_data *fd, proto_tree *tree)
+{
+       offset = dissect_rpc_data(pd, offset, fd, tree, hf_rpc_authgss_data);
+       return offset;
+}
 
 gboolean
 dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
@@ -646,6 +1036,9 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
        unsigned int prog = 0;
        unsigned int vers = 0;
        unsigned int proc = 0;
+       unsigned int flavor = 0;
+       unsigned int gss_proc = 0;
+       unsigned int gss_svc = 0;
        int     proto = 0;
        int     ett = 0;
 
@@ -681,6 +1074,9 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
 
        dissect_function_t *dissect_function = NULL;
 
+       if (!proto_is_protocol_enabled(proto_rpc))
+         return FALSE;
+
        /* TCP uses record marking */
        use_rm = (pi.ptype == PT_TCP);
 
@@ -737,7 +1133,7 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                   no guarantee that the reply will come from the address
                   to which the call was sent.) */
                conversation = find_conversation(&null_address, &pi.dst,
-                   pi.ptype, pi.srcport, pi.destport);
+                   pi.ptype, pi.srcport, pi.destport, 0);
                if (conversation == NULL) {
                        /* We haven't seen an RPC call for that conversation,
                           so we can't check for a reply to that call. */
@@ -765,28 +1161,28 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                col_add_str(fd, COL_PROTOCOL, "RPC");
 
        if (tree) {
-               rpc_item = proto_tree_add_item(tree, proto_rpc, offset, END_OF_FRAME, NULL);
+               rpc_item = proto_tree_add_item(tree, proto_rpc, NullTVB, offset, END_OF_FRAME, FALSE);
                if (rpc_item) {
                        rpc_tree = proto_item_add_subtree(rpc_item, ett_rpc);
                }
        }
 
        if (use_rm && rpc_tree) {
-               proto_tree_add_item(rpc_tree,hf_rpc_lastfrag,
+               proto_tree_add_boolean(rpc_tree,hf_rpc_lastfrag, NullTVB,
                        offset-4, 4, (rpc_rm >> 31) & 0x1);
-               proto_tree_add_item(rpc_tree,hf_rpc_fraglen,
+               proto_tree_add_uint(rpc_tree,hf_rpc_fraglen, NullTVB,
                        offset-4, 4, rpc_rm & RPC_RM_FRAGLEN);
        }
 
        xid      = EXTRACT_UINT(pd,offset+0);
        if (rpc_tree) {
-               proto_tree_add_item_format(rpc_tree,hf_rpc_xid,
+               proto_tree_add_uint_format(rpc_tree,hf_rpc_xid, NullTVB,
                        offset+0, 4, xid, "XID: 0x%x (%u)", xid, xid);
        }
 
        msg_type_name = val_to_str(msg_type,rpc_msg_type,"%u");
        if (rpc_tree) {
-               proto_tree_add_item(rpc_tree, hf_rpc_msgtype,
+               proto_tree_add_uint(rpc_tree, hf_rpc_msgtype, NullTVB,
                        offset+4, 4, msg_type);
        }
 
@@ -801,15 +1197,15 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
 
                rpcvers = EXTRACT_UINT(pd,offset+0);
                if (rpc_tree) {
-                       proto_tree_add_item(rpc_tree,
-                               hf_rpc_version, offset+0, 4, rpcvers);
+                       proto_tree_add_uint(rpc_tree,
+                               hf_rpc_version, NullTVB, offset+0, 4, rpcvers);
                }
 
                prog = EXTRACT_UINT(pd,offset+4);
                
                if (rpc_tree) {
-                       proto_tree_add_item_format(rpc_tree,
-                               hf_rpc_program, offset+4, 4, prog,
+                       proto_tree_add_uint_format(rpc_tree,
+                               hf_rpc_program, NullTVB, offset+4, 4, prog,
                                "Program: %s (%u)", progname, prog);
                }
                
@@ -823,20 +1219,27 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                        return TRUE;
                vers = EXTRACT_UINT(pd,offset+8);
                if (rpc_tree) {
-                       proto_tree_add_item(rpc_tree,
-                               hf_rpc_programversion, offset+8, 4, vers);
+                       proto_tree_add_uint(rpc_tree,
+                               hf_rpc_programversion, NullTVB, offset+8, 4, vers);
                }
 
                if (!BYTES_ARE_IN_FRAME(offset+12,4))
                        return TRUE;
                proc = EXTRACT_UINT(pd,offset+12);
 
+               /* Check for RPCSEC_GSS */
+               if (proc == 0 && BYTES_ARE_IN_FRAME(offset+16,28)) {
+                       flavor = EXTRACT_UINT(pd, offset+16);
+                       if (flavor == RPCSEC_GSS) {
+                               gss_proc = EXTRACT_UINT(pd, offset+28);
+                               gss_svc = EXTRACT_UINT(pd, offset+34);
+                       }
+               }
                key.prog = prog;
                key.vers = vers;
                key.proc = proc;
 
-               value = g_hash_table_lookup(rpc_procs,&key);
-               if (value != NULL) {
+               if ((value = g_hash_table_lookup(rpc_procs,&key)) != NULL) {
                        dissect_function = value->dissect_call;
                        procname = value->name;
                }
@@ -847,9 +1250,10 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                        sprintf(procname_static, "proc-%u", proc);
                        procname = procname_static;
                }
+               
                if (rpc_tree) {
-                       proto_tree_add_item_format(rpc_tree,
-                               hf_rpc_procedure, offset+12, 4, prog,
+                       proto_tree_add_uint_format(rpc_tree,
+                               hf_rpc_procedure, NullTVB, offset+12, 4, proc,
                                "Procedure: %s (%u)", procname, proc);
                }
 
@@ -869,11 +1273,11 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                   guarantee that the reply will come from the address
                   to which the call was sent.) */
                conversation = find_conversation(&pi.src, &null_address,
-                   pi.ptype, pi.srcport, pi.destport);
+                   pi.ptype, pi.srcport, pi.destport, 0);
                if (conversation == NULL) {
                        /* It's not part of any conversation - create a new one. */
                        conversation = conversation_new(&pi.src, &null_address,
-                           pi.ptype, pi.srcport, pi.destport, NULL);
+                           pi.ptype, pi.srcport, pi.destport, NULL, 0);
                }
 
                /* prepare the key data */
@@ -881,18 +1285,38 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                rpc_call_msg.conversation = conversation;
 
                /* look up the request */
-               if (rpc_call_lookup(&rpc_call_msg)) {
-                       /* duplicate request */
-                       if (check_col(fd, COL_INFO)) {
-                               col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
+               if ((rpc_call = rpc_call_lookup(&rpc_call_msg)) != NULL) {
+                       /* We've seen a request with this XID, with the same
+                          source and destination, before - but was it
+                          *this* request? */
+                       if (fd->num != rpc_call->req_num) {
+                               /* No, so it's a duplicate request.
+                                  Mark it as such. */
+                               if (check_col(fd, COL_INFO)) {
+                                       col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
+                                       if (rpc_tree) {
+                                               proto_tree_add_uint_hidden(rpc_tree,
+                                                       hf_rpc_dup, NullTVB, 0,0, xid);
+                                               proto_tree_add_uint_hidden(rpc_tree,
+                                                       hf_rpc_call_dup, NullTVB, 0,0, xid);
+                                       }
+                               }
                        }
                }
                else {
-                       /* prepare the value data */
-                       rpc_call_msg.replies = 0;
+                       /* Prepare the value data.
+                          "req_num" and "rep_num" are frame numbers;
+                          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". */
+                       rpc_call_msg.req_num = fd->num;
+                       rpc_call_msg.rep_num = 0;
                        rpc_call_msg.prog = prog;
                        rpc_call_msg.vers = vers;
                        rpc_call_msg.proc = proc;
+                       rpc_call_msg.flavor = flavor;
+                       rpc_call_msg.gss_proc = gss_proc;
+                       rpc_call_msg.gss_svc = gss_svc;
                        rpc_call_msg.proc_info = value;
                        /* store it */
                        rpc_call_insert(&rpc_call_msg);
@@ -904,7 +1328,6 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
 
                /* go to the next dissector */
-               /* goto dissect_rpc_prog; */
 
        } /* end of RPC call */
        else if (msg_type == RPC_REPLY)
@@ -914,6 +1337,15 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                prog = rpc_call->prog;
                vers = rpc_call->vers;
                proc = rpc_call->proc;
+               flavor = rpc_call->flavor;
+               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, NullTVB, 0, 0,
+                   "This is a reply to a request starting 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) {
@@ -929,7 +1361,6 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                        sprintf(procname_static, "proc-%u", proc);
                        procname = procname_static;
                }
-               rpc_call->replies++;
 
                rpc_prog_key.prog = prog;
                if ((rpc_prog = g_hash_table_lookup(rpc_progs,&rpc_prog_key)) == NULL) {
@@ -959,19 +1390,36 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                }
 
                if (rpc_tree) {
-                       proto_tree_add_item_format(rpc_tree,
-                               hf_rpc_program, 0, 0, prog,
+                       proto_tree_add_uint_format(rpc_tree,
+                               hf_rpc_program, NullTVB, 0, 0, prog,
                                "Program: %s (%u)", progname, prog);
-                       proto_tree_add_item(rpc_tree,
-                               hf_rpc_programversion, 0, 0, vers);
-                       proto_tree_add_item_format(rpc_tree,
-                               hf_rpc_procedure, 0, 0, prog,
+                       proto_tree_add_uint(rpc_tree,
+                               hf_rpc_programversion, NullTVB, 0, 0, vers);
+                       proto_tree_add_uint_format(rpc_tree,
+                               hf_rpc_procedure, NullTVB, 0, 0, proc,
                                "Procedure: %s (%u)", procname, proc);
                }
 
-               if (rpc_call->replies>1) {
-                       if (check_col(fd, COL_INFO)) {
-                               col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
+               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
+                          frame number. */
+                       rpc_call->rep_num = fd->num;
+               } else {
+                       /* We have seen a reply to this call - but was it
+                          *this* reply? */
+                       if (rpc_call->rep_num != fd->num) {
+                               /* No, so it's a duplicate reply.
+                                  Mark it as such. */
+                               if (check_col(fd, COL_INFO)) {
+                                       col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
+                                       if (rpc_tree) {
+                                               proto_tree_add_uint_hidden(rpc_tree,
+                                                       hf_rpc_dup, NullTVB, 0,0, xid);
+                                               proto_tree_add_uint_hidden(rpc_tree,
+                                                       hf_rpc_reply_dup, NullTVB, 0,0, xid);
+                                       }
+                               }
                        }
                }
 
@@ -979,7 +1427,7 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                        return TRUE;
                reply_state = EXTRACT_UINT(pd,offset+0);
                if (rpc_tree) {
-                       proto_tree_add_item(rpc_tree, hf_rpc_state_reply,
+                       proto_tree_add_uint(rpc_tree, hf_rpc_state_reply, NullTVB,
                                offset+0, 4, reply_state);
                }
                offset += 4;
@@ -990,14 +1438,13 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                                return TRUE;
                        accept_state = EXTRACT_UINT(pd,offset+0);
                        if (rpc_tree) {
-                               proto_tree_add_item(rpc_tree, hf_rpc_state_accept,
+                               proto_tree_add_uint(rpc_tree, hf_rpc_state_accept, NullTVB,
                                        offset+0, 4, accept_state);
                        }
                        offset += 4;
                        switch (accept_state) {
                                case SUCCESS:
-                                       /* now goto the lower protocol */
-                                       goto dissect_rpc_prog;
+                                       /* go to the next dissector */
                                break;
                                case PROG_MISMATCH:
                                        if (!BYTES_ARE_IN_FRAME(offset,8))
@@ -1005,12 +1452,12 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                                        vers_low = EXTRACT_UINT(pd,offset+0);
                                        vers_high = EXTRACT_UINT(pd,offset+4);
                                        if (rpc_tree) {
-                                               proto_tree_add_item(rpc_tree,
+                                               proto_tree_add_uint(rpc_tree,
                                                        hf_rpc_programversion_min,
-                                                       offset+0, 4, vers_low);
-                                               proto_tree_add_item(rpc_tree,
+                                                       NullTVB, offset+0, 4, vers_low);
+                                               proto_tree_add_uint(rpc_tree,
                                                        hf_rpc_programversion_max,
-                                                       offset+4, 4, vers_high);
+                                                       NullTVB, offset+4, 4, vers_high);
                                        }
                                        offset += 8;
                                break;
@@ -1023,8 +1470,8 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                                return TRUE;
                        reject_state = EXTRACT_UINT(pd,offset+0);
                        if (rpc_tree) {
-                               proto_tree_add_item(rpc_tree,
-                                       hf_rpc_state_reject, offset+0, 4,
+                               proto_tree_add_uint(rpc_tree,
+                                       hf_rpc_state_reject, NullTVB, offset+0, 4,
                                        reject_state);
                        }
                        offset += 4;
@@ -1035,12 +1482,12 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                                vers_low = EXTRACT_UINT(pd,offset+0);
                                vers_high = EXTRACT_UINT(pd,offset+4);
                                if (rpc_tree) {
-                                       proto_tree_add_item(rpc_tree,
+                                       proto_tree_add_uint(rpc_tree,
                                                hf_rpc_version_min,
-                                               offset+0, 4, vers_low);
-                                       proto_tree_add_item(rpc_tree,
+                                               NullTVB, offset+0, 4, vers_low);
+                                       proto_tree_add_uint(rpc_tree,
                                                hf_rpc_version_max,
-                                               offset+4, 4, vers_high);
+                                               NullTVB, offset+4, 4, vers_high);
                                }
                                offset += 8;
                        } else if (reject_state==AUTH_ERROR) {
@@ -1048,8 +1495,8 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                                        return TRUE;
                                auth_state = EXTRACT_UINT(pd,offset+0);
                                if (rpc_tree) {
-                                       proto_tree_add_item(rpc_tree,
-                                               hf_rpc_state_auth, offset+0, 4,
+                                       proto_tree_add_uint(rpc_tree,
+                                               hf_rpc_state_auth, NullTVB, offset+0, 4,
                                                auth_state);
                                }
                                offset += 4;
@@ -1057,9 +1504,6 @@ dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                } 
        } /* end of RPC reply */
 
-dissect_rpc_prog:
-       /* I know, goto is evil but it works as it is. */
-
        /* now we know, that RPC was shorter */
        if (rpc_item) {
                proto_item_set_len(rpc_item, offset - offset_old);
@@ -1067,28 +1511,60 @@ dissect_rpc_prog:
 
        /* create here the program specific sub-tree */
        if (tree) {
-               pitem = proto_tree_add_item(tree, proto, offset, END_OF_FRAME);
+               pitem = proto_tree_add_item(tree, proto, NullTVB, offset, END_OF_FRAME, FALSE);
                if (pitem) {
                        ptree = proto_item_add_subtree(pitem, ett);
                }
 
                if (ptree) {
-                       proto_tree_add_item(ptree,
-                               hf_rpc_programversion, 0, 0, vers);
-                       proto_tree_add_item_format(ptree,
-                               hf_rpc_procedure, 0, 0, prog,
+                       proto_tree_add_uint(ptree,
+                               hf_rpc_programversion, NullTVB, 0, 0, vers);
+                       proto_tree_add_uint_format(ptree,
+                               hf_rpc_procedure, NullTVB, 0, 0, proc,
                                "Procedure: %s (%u)", procname, proc);
                }
        }
 
-       /* call a specific dissection */
-       if (dissect_function != NULL) {
+       /* RPCSEC_GSS processing. */
+       if (flavor == RPCSEC_GSS) {
+               switch (gss_proc) {
+               case RPCSEC_GSS_INIT:
+               case RPCSEC_GSS_CONTINUE_INIT:
+                       if (msg_type == RPC_CALL) {
+                               offset = dissect_rpc_authgss_initarg(pd, offset, fd, ptree);
+                       }
+                       else {
+                               offset = dissect_rpc_authgss_initres(pd, offset, fd, ptree);
+                       }
+                       break;
+               case RPCSEC_GSS_DATA:
+                       if (gss_svc == RPCSEC_GSS_SVC_NONE) {
+                               if (dissect_function != NULL && 
+                                       proto_is_protocol_enabled(proto))
+                                       offset = dissect_function(pd, offset, fd, ptree);
+                       }
+                       else if (gss_svc == RPCSEC_GSS_SVC_INTEGRITY) {
+                               offset = dissect_rpc_authgss_integ_data(pd, offset, fd, ptree, 
+                               (proto_is_protocol_enabled(proto) ? 
+                               dissect_function : NULL));
+                       }
+                       else if (gss_svc == RPCSEC_GSS_SVC_PRIVACY) {
+                               offset = dissect_rpc_authgss_priv_data(pd, offset, fd, ptree);
+                       }
+                       break;
+               default:
+                       dissect_function = NULL;
+                       break;
+               }
+       }
+       else if (dissect_function != NULL &&
+               proto_is_protocol_enabled(proto)) {
                offset = dissect_function(pd, offset, fd, ptree);
        }
 
        /* dissect any remaining bytes (incomplete dissection) as pure data in
           the ptree */
-       dissect_data(pd, offset, fd, ptree);
+       old_dissect_data(pd, offset, fd, ptree);
 
        return TRUE;
 }
@@ -1172,9 +1648,57 @@ proto_register_rpc(void)
                { &hf_rpc_auth_gid, {
                        "GID", "rpc.auth.gid", FT_UINT32, BASE_DEC,
                        NULL, 0, "GID" }},
+               { &hf_rpc_authgss_v, {
+                       "GSS Version", "rpc.authgss.version", FT_UINT32,
+                       BASE_DEC, NULL, 0, "GSS Version" }},
+               { &hf_rpc_authgss_proc, {
+                       "GSS Procedure", "rpc.authgss.procedure", FT_UINT32,
+                       BASE_DEC, VALS(rpc_authgss_proc), 0, "GSS Procedure" }},
+               { &hf_rpc_authgss_seq, {
+                       "GSS Sequence Number", "rpc.authgss.seqnum", FT_UINT32,
+                       BASE_DEC, NULL, 0, "GSS Sequence Number" }},
+               { &hf_rpc_authgss_svc, {
+                       "GSS Service", "rpc.authgss.service", FT_UINT32,
+                       BASE_DEC, VALS(rpc_authgss_svc), 0, "GSS Service" }},
+               { &hf_rpc_authgss_ctx, {
+                       "GSS Context", "rpc.authgss.context", FT_BYTES,
+                       BASE_HEX, NULL, 0, "GSS Context" }},
+               { &hf_rpc_authgss_major, {
+                       "GSS Major Status", "rpc.authgss.major", FT_UINT32,
+                       BASE_DEC, NULL, 0, "GSS Major Status" }},
+               { &hf_rpc_authgss_minor, {
+                       "GSS Minor Status", "rpc.authgss.minor", FT_UINT32,
+                       BASE_DEC, NULL, 0, "GSS Minor Status" }},
+               { &hf_rpc_authgss_window, {
+                       "GSS Sequence Window", "rpc.authgss.window", FT_UINT32,
+                       BASE_DEC, NULL, 0, "GSS Sequence Window" }},
+               { &hf_rpc_authgss_token, {
+                       "GSS Token", "rpc.authgss.token", FT_BYTES,
+                       BASE_HEX, NULL, 0, "GSS Token" }},
+               { &hf_rpc_authgss_data_length, {
+                       "Length", "rpc.authgss.data.length", FT_UINT32,
+                       BASE_DEC, NULL, 0, "Length" }},
+               { &hf_rpc_authgss_data, {
+                       "GSS Data", "rpc.authgss.data", FT_BYTES,
+                       BASE_HEX, NULL, 0, "GSS Data" }},
+               { &hf_rpc_authgss_checksum, {
+                       "GSS Checksum", "rpc.authgss.checksum", FT_BYTES,
+                       BASE_HEX, NULL, 0, "GSS Checksum" }},
                { &hf_rpc_auth_machinename, {
                        "Machine Name", "rpc.auth.machinename", FT_STRING, 
                        BASE_DEC, NULL, 0, "Machine Name" }},
+               { &hf_rpc_dup, {
+                       "Duplicate Transaction", "rpc.dup", FT_UINT32, BASE_DEC,
+                       NULL, 0, "Duplicate Transaction" }},
+               { &hf_rpc_call_dup, {
+                       "Duplicate Call", "rpc.call.dup", FT_UINT32, BASE_DEC,
+                       NULL, 0, "Duplicate Call" }},
+               { &hf_rpc_reply_dup, {
+                       "Duplicate Reply", "rpc.reply.dup", FT_UINT32, BASE_DEC,
+                       NULL, 0, "Duplicate Reply" }},
+               { &hf_rpc_value_follows, {
+                       "Value Follows", "rpc.value_follows", FT_BOOLEAN, BASE_NONE,
+                       &yesno, 0, "Value Follows" }}
        };
        static gint *ett[] = {
                &ett_rpc,
@@ -1188,4 +1712,28 @@ 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);
+
+       /*
+        * Init the hash tables.  Dissectors for RPC protocols must
+        * have a "handoff registration" routine that registers the
+        * protocol with RPC; they must not do it in their protocol
+        * registration routine, as their protocol registration
+        * routine might be called before this routine is called and
+        * thus might be called before the hash tables are initialized,
+        * but it's guaranteed that all protocol registration routines
+        * will be called before any handoff registration routines
+        * are called.
+        */
+       rpc_progs = g_hash_table_new(rpc_prog_hash, rpc_prog_equal);
+       rpc_procs = g_hash_table_new(rpc_proc_hash, rpc_proc_equal);
 }
+
+
+void
+proto_reg_handoff_rpc(void)
+{
+       old_heur_dissector_add("tcp", dissect_rpc);
+       old_heur_dissector_add("udp", dissect_rpc);
+}
+
+