* 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" };
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;
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;
/***********************************/
/* compare 2 keys */
-gint
+static gint
rpc_proc_equal(gconstpointer k1, gconstpointer k2)
{
rpc_proc_info_key* key1 = (rpc_proc_info_key*) k1;
}
/* calculate a hash key */
-guint
+static guint
rpc_proc_hash(gconstpointer k)
{
rpc_proc_info_key* key = (rpc_proc_info_key*) k;
/*********************************/
/* compare 2 keys */
-gint
+static gint
rpc_prog_equal(gconstpointer k1, gconstpointer k2)
{
rpc_prog_info_key* key1 = (rpc_prog_info_key*) k1;
/* calculate a hash key */
-guint
+static guint
rpc_prog_hash(gconstpointer k)
{
rpc_prog_info_key* key = (rpc_prog_info_key*) k;
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;
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;
/* calculate a hash key */
-guint
+static guint
rpc_call_hash(gconstpointer k)
{
rpc_call_info_key* key = (rpc_call_info_key*) k;
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)
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)
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)
{
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 */
}
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,
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) {
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;
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--) {
}
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;
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);
}
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;
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;
}
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;
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,
}
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;
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:
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)
* 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;
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,
{
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,
/* 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,
}
break;
case RPCSEC_GSS:
- dissect_rpc_data(tvb, pinfo, vtree,
+ dissect_rpc_data(tvb, vtree,
hf_rpc_authgss_checksum, offset+4);
break;
default:
}
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);
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;
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;
}
/* 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->prog = prog;
rpc_call->vers = vers;
rpc_call->proc = proc;
+ rpc_call->private_data = NULL;
/*
* XXX - what about RPCSEC_GSS?
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;
}
/* 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;
}
/* 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;
}
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;
}
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;
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;
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.
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))
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:
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);
"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);
"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,
/* 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;
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,
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 */
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) {
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,
"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
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,
}
}
- 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,
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,
}
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 */
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);
}
* 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;
* 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;
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;
}
else if (gss_svc == RPCSEC_GSS_SVC_PRIVACY) {
offset = dissect_rpc_authgss_priv_data(tvb,
- pinfo, ptree, offset);
+ ptree, offset);
}
break;
/* 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;
}
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:
*
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;
}
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);
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 */
{
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 }},
{ &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,
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
{
heur_dissector_add("tcp", dissect_rpc_tcp_heur, proto_rpc);
heur_dissector_add("udp", dissect_rpc_heur, proto_rpc);
+ data_handle = find_dissector("data");
}