aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-rpc.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2016-01-05 12:14:43 -0800
committerGuy Harris <guy@alum.mit.edu>2016-01-05 20:16:45 +0000
commit8d4422180d13c4d79b37061c57412b4fdd407eff (patch)
tree335a8ee1d435e51fdef2dd893b66f1b2699ce13d /epan/dissectors/packet-rpc.c
parentffb51b3afb7c92b38046a4907ba013cb6c3288b1 (diff)
When dissecting fragments heuristically, check for incomplete RPC messages.
If we're dissecting heuristically, and we don't have the full fragment, but it looks like the beginning of an RPC call or reply, ask for more data rather than rejecting it. That way, we can recognize handle large calls and replies, such as large NFS writes, when transported over TCP. Clean up conversation handling while we're at it. Bug: 11955 Change-Id: I0237255165a277a051d355810f1500cf4228d7c8 Reviewed-on: https://code.wireshark.org/review/13061 Reviewed-by: Guy Harris <guy@alum.mit.edu>
Diffstat (limited to 'epan/dissectors/packet-rpc.c')
-rw-r--r--epan/dissectors/packet-rpc.c796
1 files changed, 443 insertions, 353 deletions
diff --git a/epan/dissectors/packet-rpc.c b/epan/dissectors/packet-rpc.c
index 933da9b6fd..cbab2fdde6 100644
--- a/epan/dissectors/packet-rpc.c
+++ b/epan/dissectors/packet-rpc.c
@@ -1632,6 +1632,147 @@ dissect_rpc_authgss_priv_data(tvbuff_t *tvb, proto_tree *tree, int offset,
return offset;
}
+static address null_address = { AT_NONE, 0, NULL };
+
+/*
+ * Attempt to find a conversation for a call and, if we don't find one,
+ * create a new one.
+ */
+static conversation_t *
+get_conversation_for_call(packet_info *pinfo)
+{
+ conversation_t *conversation;
+
+ /*
+ * If the transport is connection-oriented (TCP or Infiniband),
+ * we use the addresses and ports of both endpoints, because
+ * the addresses and ports of the two endpoints should be
+ * the same for a call and its matching reply. (XXX - what
+ * if the connection is broken and re-established between
+ * the call and the reply? Or will the call be re-made on
+ * the new connection?)
+ *
+ * If the transport is connectionless, we don't worry
+ * about the address to which the call was sent, because
+ * there's no guarantee that the reply will come from the
+ * address to which the call was sent. We also don't
+ * worry about the port *from* which the call was sent,
+ * because some clients (*cough* OS X NFS client *cough*)
+ * might send retransmissions from a different port from
+ * the original request.
+ */
+ if (pinfo->ptype == PT_TCP || pinfo->ptype == PT_IBQP) {
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ } else {
+ /*
+ * XXX - you currently still have to pass a non-null
+ * pointer for the second address argument even
+ * if you use NO_ADDR_B.
+ */
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->src, &null_address, pinfo->ptype,
+ pinfo->destport, 0, NO_ADDR_B|NO_PORT_B);
+ }
+
+ if (conversation == NULL) {
+ if (pinfo->ptype == PT_TCP || pinfo->ptype == PT_IBQP) {
+ conversation = conversation_new(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ } else {
+ conversation = conversation_new(pinfo->fd->num,
+ &pinfo->src, &null_address, pinfo->ptype,
+ pinfo->destport, 0, NO_ADDR2|NO_PORT2);
+ }
+ }
+ return conversation;
+}
+
+static conversation_t *
+find_conversation_for_reply(packet_info *pinfo)
+{
+ conversation_t *conversation;
+
+ /*
+ * If the transport is connection-oriented (TCP or Infiniband),
+ * we use the addresses and ports of both endpoints, because
+ * the addresses and ports of the two endpoints should be
+ * the same for a call and its matching reply. (XXX - what
+ * if the connection is broken and re-established between
+ * the call and the reply? Or will the call be re-made on
+ * the new connection?)
+ *
+ * If the transport is connectionless, we don't worry
+ * about the address from which the reply was sent,
+ * because there's no guarantee that the call was sent
+ * to the address from which the reply came. We also
+ * don't worry about the port *to* which the reply was
+ * sent, because some clients (*cough* OS X NFS client
+ * *cough*) might send call retransmissions from a
+ * different port from the original request, so replies
+ * to the original call and a retransmission of the call
+ * might be sent to different ports.
+ */
+ if (pinfo->ptype == PT_TCP || pinfo->ptype == PT_IBQP) {
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ } else {
+ /*
+ * XXX - you currently still have to pass a non-null
+ * pointer for the second address argument even
+ * if you use NO_ADDR_B.
+ */
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->dst, &null_address, pinfo->ptype,
+ pinfo->srcport, 0, NO_ADDR_B|NO_PORT_B);
+ }
+ return conversation;
+}
+
+static conversation_t *
+new_conversation_for_reply(packet_info *pinfo)
+{
+ conversation_t *conversation;
+
+ if (pinfo->ptype == PT_TCP || pinfo->ptype == PT_IBQP) {
+ conversation = conversation_new(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ } else {
+ conversation = conversation_new(pinfo->fd->num,
+ &pinfo->dst, &null_address, pinfo->ptype,
+ pinfo->srcport, 0, NO_ADDR2|NO_PORT2);
+ }
+ return conversation;
+}
+
+static conversation_t *
+get_conversation_for_tcp(packet_info *pinfo)
+{
+ conversation_t *conversation;
+
+ /*
+ * We know this is running over TCP, so the conversation should
+ * not wildcard either address or port, regardless of whether
+ * this is a call or reply.
+ */
+ conversation = find_conversation(pinfo->fd->num,
+ &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->fd->num,
+ &pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ }
+ return conversation;
+}
+
/*
* Dissect the arguments to an indirect call; used by the portmapper/RPCBIND
* dissector for the CALLIT procedure.
@@ -1639,14 +1780,13 @@ dissect_rpc_authgss_priv_data(tvbuff_t *tvb, proto_tree *tree, int offset,
* Record these in the same table as the direct calls
* so we can find it when dissecting an indirect call reply.
* (There should not be collissions between xid between direct and
- * indirect calls.)
+ * indirect calls.)
*/
int
dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset, int args_id, guint32 prog, guint32 vers, guint32 proc)
{
conversation_t* conversation;
- static address null_address = { AT_NONE, 0, NULL };
rpc_proc_info_key key;
rpc_call_info_value *rpc_call;
dissector_handle_t dissect_function = NULL;
@@ -1657,59 +1797,12 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
key.vers = vers;
key.proc = proc;
if ((dissect_function = dissector_get_custom_table_handle(subdissector_call_table, &key)) != NULL) {
+ /*
+ * Establish a conversation for the call, for use when
+ * matching calls with replies.
+ */
+ conversation = get_conversation_for_call(pinfo);
- /* Keep track of the address whence the call came, and the
- port to which the call is being sent, so that we can
- match up calls with replies.
-
- If the transport is connection-oriented (we check, for
- now, only for "pinfo->ptype" of PT_TCP), we also take
- into account the port from which the call was sent
- and the address to which the call was sent, because
- the addresses and ports of the two endpoints should be
- the same for all calls and replies. (XXX - what if
- the connection is broken and re-established?)
-
- If the transport is connectionless, we don't worry
- about the address to which the call was sent and from
- which the reply was sent, because there's no
- guarantee that the reply will come from the address
- to which the call was sent. We also don't worry about
- the port *from* which the call was sent and *to* which
- the reply was sent, because some clients (*cough* OS X
- NFS client *cough) might send retransmissions from a
- different port from the original request. */
- if (pinfo->ptype == PT_TCP) {
- conversation = find_conversation(pinfo->fd->num, &pinfo->src,
- &pinfo->dst, pinfo->ptype, pinfo->srcport,
- pinfo->destport, 0);
- } else {
- /*
- * XXX - you currently still have to pass a non-null
- * pointer for the second address argument even
- * if you use NO_ADDR_B.
- */
- conversation = find_conversation(pinfo->fd->num, &pinfo->src,
- &null_address, pinfo->ptype, pinfo->destport,
- 0, NO_ADDR_B|NO_PORT_B);
- }
- if (conversation == NULL) {
- /* It's not part of any conversation - create a new
- one.
-
- XXX - this should never happen, as we should've
- created a conversation for it in the RPC
- dissector. */
- if (pinfo->ptype == PT_TCP) {
- conversation = conversation_new(pinfo->fd->num, &pinfo->src,
- &pinfo->dst, pinfo->ptype, pinfo->srcport,
- pinfo->destport, 0);
- } else {
- conversation = conversation_new(pinfo->fd->num, &pinfo->src,
- &null_address, pinfo->ptype, pinfo->destport,
- 0, NO_ADDR2|NO_PORT2);
- }
- }
/*
* Do we already have a state structure for this conv
*/
@@ -1791,7 +1884,6 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset, int result_id, int prog_id, int vers_id, int proc_id)
{
conversation_t* conversation;
- static address null_address = { AT_NONE, 0, NULL };
rpc_call_info_value *rpc_call;
const char *procname=NULL;
dissector_handle_t dissect_function = NULL;
@@ -1799,39 +1891,13 @@ dissect_rpc_indir_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
rpc_proc_info_key key;
guint32 xid;
- /* Look for the matching call in the xid table.
- A reply must match a call that we've seen, and the
- reply must be sent to the same address that the call came
- from, and must come from the port to which the call was sent.
-
- If the transport is connection-oriented (we check, for
- now, only for "pinfo->ptype" of PT_TCP), we take
- into account the port from which the call was sent
- and the address to which the call was sent, because
- the addresses and ports of the two endpoints should be
- the same for all calls and replies.
-
- If the transport is connectionless, we don't worry
- about the address to which the call was sent and from
- which the reply was sent, because there's no
- guarantee that the reply will come from the address
- to which the call was sent. We also don't worry about
- the port *from* which the call was sent and *to* which
- the reply was sent, because some clients (*cough* OS X
- NFS client *cough) might send retransmissions from a
- different port from the original request. */
- if (pinfo->ptype == PT_TCP) {
- conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
- pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
- } else {
- /*
- * XXX - you currently still have to pass a non-null
- * pointer for the second address argument even
- * if you use NO_ADDR_B.
- */
- conversation = find_conversation(pinfo->fd->num, &pinfo->dst, &null_address,
- pinfo->ptype, pinfo->srcport, 0, NO_ADDR_B|NO_PORT_B);
- }
+ /*
+ * Look for the matching call in the xid table.
+ * A reply must match a call that we've seen, and the
+ * reply must be sent to the same address that the call came
+ * from, and must come from the port to which the call was sent.
+ */
+ conversation = find_conversation_for_reply(pinfo);
if (conversation == NULL) {
/* We haven't seen an RPC call for that conversation,
so we can't check for a reply to that call.
@@ -1949,14 +2015,204 @@ dissect_rpc_unknown(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree
return captured_length;
}
+static rpc_prog_info_value *
+looks_like_rpc_call(tvbuff_t *tvb, int offset)
+{
+ guint32 rpc_prog_key;
+ rpc_prog_info_value *rpc_prog;
+
+ if (!tvb_bytes_exist(tvb, offset, 16)) {
+ /* Captured data in packet isn't enough to let us
+ tell. */
+ return NULL;
+ }
+
+ /* XID can be anything, so don't check it.
+ 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 location where it would be.
+
+ Sun's snoop just checks for a message direction of RPC_CALL
+ and a version number of 2, which is a bit of a weak heuristic.
+
+ We could conceivably check for any of the program numbers
+ in the list at
+
+ ftp://ftp.tau.ac.il/pub/users/eilon/rpc/rpc
+
+ and report it as RPC (but not dissect the payload if
+ we don't have a subdissector) if it matches. */
+ rpc_prog_key = tvb_get_ntohl(tvb, offset + 12);
+
+ /* we only dissect RPC version 2 */
+ if (tvb_get_ntohl(tvb, offset + 8) != 2)
+ return NULL;
+
+ /* Do we know this program? */
+ rpc_prog = (rpc_prog_info_value *)g_hash_table_lookup(rpc_progs, GUINT_TO_POINTER(rpc_prog_key));
+ if (rpc_prog == NULL) {
+ guint32 version;
+
+ /*
+ * No.
+ * If the user has specified that he wants to try to
+ * dissect even completely unknown RPC program numbers,
+ * then let him do that.
+ */
+ if (!rpc_dissect_unknown_programs) {
+ /* They didn't, so just fail. */
+ return NULL;
+ }
+
+ /*
+ * The user wants us to try to dissect even
+ * unknown program numbers.
+ *
+ * Use some heuristics to keep from matching any
+ * packet with a 2 in the appropriate location.
+ * We check that the program number is neither
+ * 0 nor -1, and that the version is <= 10, which
+ * is better than nothing.
+ */
+ if (rpc_prog_key == 0 || rpc_prog_key == 0xffffffff)
+ return FALSE;
+ version = tvb_get_ntohl(tvb, offset+16);
+ if (version > 10)
+ return NULL;
+
+ rpc_prog = wmem_new(wmem_packet_scope(), rpc_prog_info_value);
+ rpc_prog->proto = NULL;
+ rpc_prog->proto_id = 0;
+ rpc_prog->ett = ett_rpc_unknown_program;
+ rpc_prog->progname = wmem_strdup_printf(wmem_packet_scope(), "Unknown RPC program %u", rpc_prog_key);
+ }
+
+ return rpc_prog;
+}
+
+static rpc_call_info_value *
+looks_like_rpc_reply(tvbuff_t *tvb, packet_info *pinfo, int offset)
+{
+ unsigned int xid;
+ conversation_t *conversation;
+ rpc_conv_info_t *rpc_conv_info;
+ rpc_call_info_value *rpc_call;
+
+ /* A reply must match a call that we've seen, and the reply
+ must be sent to the same address that the call came from,
+ and must come from the port to which the call was sent.
+
+ If the transport is connection-oriented (we check, for
+ now, only for "pinfo->ptype" of PT_TCP), we take
+ into account the port from which the call was sent
+ and the address to which the call was sent, because
+ the addresses and ports of the two endpoints should be
+ the same for all calls and replies.
+
+ If the transport is connectionless, we don't worry
+ about the address from which the reply was sent,
+ because there's no guarantee that the call was sent
+ to the address from which the reply came. We also
+ don't worry about the port *to* which the reply was
+ sent, because some clients (*cough* OS X NFS client
+ *cough*) might send retransmissions from a
+ different port from the original request, so replies
+ to the original call and a retransmission of the call
+ might be sent to different ports.
+
+ Sun's snoop just checks for a message direction of RPC_REPLY
+ and a status of MSG_ACCEPTED or MSG_DENIED, which is a bit
+ of a weak heuristic. */
+ xid = tvb_get_ntohl(tvb, offset);
+ conversation = find_conversation_for_reply(pinfo);
+ if (conversation != NULL) {
+ /*
+ * We have a conversation; try to find an RPC
+ * state structure for the conversation.
+ */
+ rpc_conv_info = (rpc_conv_info_t *)conversation_get_proto_data(conversation, proto_rpc);
+ if (rpc_conv_info != NULL)
+ rpc_call = (rpc_call_info_value *)wmem_tree_lookup32(rpc_conv_info->xids, xid);
+ else
+ rpc_call = NULL;
+ } else {
+ /*
+ * We don't have a conversation, so we obviously
+ * don't have an RPC state structure for it.
+ */
+ rpc_conv_info = NULL;
+ rpc_call = NULL;
+ }
+
+ if (rpc_call == NULL) {
+ /*
+ * We don't have a conversation, or we have one but
+ * there's no RPC state information for it, or
+ * we have RPC state information but the XID doesn't
+ * match a call from the conversation, so it's
+ * probably not an RPC reply.
+ *
+ * Unless we're permitted to scan for embedded records
+ * and this is a connection-oriented transport,
+ * give up.
+ */
+ if (((! rpc_find_fragment_start) || (pinfo->ptype != PT_TCP)) && (pinfo->ptype != PT_IBQP)) {
+ return NULL;
+ }
+
+ /*
+ * Do we have a conversation to which to attach
+ * that information?
+ */
+ if (conversation == NULL) {
+ /*
+ * It's not part of any conversation - create
+ * a new one.
+ */
+ conversation = new_conversation_for_reply(pinfo);
+ }
+
+ /*
+ * Do we have RPC information for that conversation?
+ */
+ if (rpc_conv_info == NULL) {
+ /*
+ * No. Create a new RPC information structure,
+ * and attach it to the conversation.
+ */
+ rpc_conv_info = wmem_new(wmem_file_scope(), rpc_conv_info_t);
+ rpc_conv_info->xids=wmem_tree_new(wmem_file_scope());
+
+ conversation_add_proto_data(conversation, proto_rpc, rpc_conv_info);
+ }
+
+ /*
+ * Define a dummy call for this reply.
+ */
+ rpc_call = wmem_new0(wmem_file_scope(), rpc_call_info_value);
+ rpc_call->rep_num = pinfo->fd->num;
+ rpc_call->xid = xid;
+ rpc_call->flavor = FLAVOR_NOT_GSSAPI; /* total punt */
+ rpc_call->req_time = pinfo->fd->abs_ts;
+
+ /* store it */
+ wmem_tree_insert32(rpc_conv_info->xids, xid, (void *)rpc_call);
+ }
+
+ /* pass rpc_info to subdissectors */
+ rpc_call->request = FALSE;
+ return rpc_call;
+}
+
static gboolean
dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
tvbuff_t *frag_tvb, fragment_head *ipfd_head, gboolean is_tcp,
guint32 rpc_rm, gboolean first_pdu)
{
guint32 msg_type;
- rpc_call_info_value *rpc_call = NULL;
- rpc_prog_info_value *rpc_prog = NULL;
+ rpc_call_info_value *rpc_call;
+ rpc_prog_info_value *rpc_prog;
guint32 rpc_prog_key;
unsigned int xid;
@@ -1976,9 +2232,9 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
unsigned int accept_state;
unsigned int reject_state;
- const char *msg_type_name = NULL;
- const char *progname = NULL;
- const char *procname = NULL;
+ const char *msg_type_name;
+ const char *progname;
+ const char *procname;
unsigned int vers_low;
unsigned int vers_high;
@@ -1994,10 +2250,9 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
rpc_proc_info_key key;
conversation_t* conversation;
- static address null_address = { AT_NONE, 0, NULL };
nstime_t ns;
- dissector_handle_t dissect_function = NULL;
+ dissector_handle_t dissect_function;
gboolean dissect_rpc_flag = TRUE;
rpc_conv_info_t *rpc_conv_info=NULL;
@@ -2017,159 +2272,19 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
switch (msg_type) {
case RPC_CALL:
- /* check for RPC call */
- if (!tvb_bytes_exist(tvb, offset, 16)) {
- /* Captured data in packet isn't enough to let us
- tell. */
+ /* Check for RPC call. */
+ rpc_prog = looks_like_rpc_call(tvb, offset);
+ if (rpc_prog == NULL)
return FALSE;
- }
-
- /* XID can be anything, so don't check it.
- 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 location where it would be.
-
- XXX - Sun's snoop appears to recognize as RPC even calls
- to stuff it doesn't dissect; does it just look for a 2
- at that location, which seems far to weak a heuristic
- (too many false positives), or does it have some additional
- checks it does?
-
- We could conceivably check for any of the program numbers
- in the list at
-
- ftp://ftp.tau.ac.il/pub/users/eilon/rpc/rpc
-
- and report it as RPC (but not dissect the payload if
- we don't have a subdissector) if it matches. */
- rpc_prog_key = tvb_get_ntohl(tvb, offset + 12);
-
- /* we only dissect version 2 */
- if (tvb_get_ntohl(tvb, offset + 8) != 2 ){
- return FALSE;
- }
- /* Do we know this program? */
- if( (rpc_prog = (rpc_prog_info_value *)g_hash_table_lookup(rpc_progs, GUINT_TO_POINTER(rpc_prog_key))) != NULL) {
- /* Yes. */
- proto = rpc_prog->proto;
- proto_id = rpc_prog->proto_id;
- ett = rpc_prog->ett;
- progname = rpc_prog->progname;
- } else {
- guint32 version;
-
- /* No.
- * If the user has specified that he wants to try to
- * dissect even completely unknown RPC program numbers,
- * then let him do that.
- */
- if(!rpc_dissect_unknown_programs){
- /* They didn't, so just fail. */
- return FALSE;
- }
- /* Yes. Use some heuristics to keep from matching
- * any packet with a 2 in the appropriate location.
- * We check that the program number is neither
- * 0 nor -1, and that the version is <= 10, which
- * is better than nothing.
- */
- if(rpc_prog_key==0 || rpc_prog_key==0xffffffff){
- return FALSE;
- }
- version=tvb_get_ntohl(tvb, offset+16);
- if(version>10){
- return FALSE;
- }
-
- proto = NULL;
- proto_id = 0;
- ett = ett_rpc_unknown_program;
- progname = wmem_strdup_printf(wmem_packet_scope(), "Unknown RPC program %u", rpc_prog_key);
- }
+ rpc_call = NULL;
break;
case RPC_REPLY:
- /* Check for RPC reply. A reply must match a call that
- we've seen, and the reply must be sent to the same
- address that the call came from, and must come from
- the port to which the call was sent.
-
- If the transport is connection-oriented (we check, for
- now, only for "pinfo->ptype" of PT_TCP), we take
- into account the port from which the call was sent
- and the address to which the call was sent, because
- the addresses and ports of the two endpoints should be
- the same for all calls and replies.
-
- If the transport is connectionless, we don't worry
- about the address to which the call was sent and from
- which the reply was sent, because there's no
- guarantee that the reply will come from the address
- to which the call was sent. We also don't worry about
- the port *from* which the call was sent and *to* which
- the reply was sent, because some clients (*cough* OS X
- NFS client *cough) might send retransmissions from a
- different port from the original request. */
- if ((pinfo->ptype == PT_TCP) || (pinfo->ptype == PT_IBQP)) {
- conversation = find_conversation(pinfo->fd->num, &pinfo->src,
- &pinfo->dst, pinfo->ptype, pinfo->srcport,
- pinfo->destport, 0);
- } else {
- /*
- * XXX - you currently still have to pass a non-null
- * pointer for the second address argument even
- * if you use NO_ADDR_B.
- */
- conversation = find_conversation(pinfo->fd->num, &pinfo->dst,
- &null_address, pinfo->ptype, pinfo->srcport,
- 0, NO_ADDR_B|NO_PORT_B);
- }
- if (conversation == NULL) {
- /* We haven't seen an RPC call for that conversation,
- so we can't check for a reply to that call. */
+ /* Check for RPC reply. */
+ rpc_call = looks_like_rpc_reply(tvb, pinfo, offset);
+ if (rpc_call == NULL)
return FALSE;
- }
- /*
- * Do we already have a state structure for this conv
- */
- rpc_conv_info = (rpc_conv_info_t *)conversation_get_proto_data(conversation, proto_rpc);
- if (!rpc_conv_info) {
- /* No. Attach that information to the conversation, and add
- * it to the list of information structures.
- */
- rpc_conv_info = wmem_new(wmem_file_scope(), rpc_conv_info_t);
- rpc_conv_info->xids=wmem_tree_new(wmem_file_scope());
-
- conversation_add_proto_data(conversation, proto_rpc, rpc_conv_info);
- }
-
- /* The XIDs of the call and reply must match. */
- xid = tvb_get_ntohl(tvb, offset);
- rpc_call = (rpc_call_info_value *)wmem_tree_lookup32(rpc_conv_info->xids, xid);
- if (rpc_call == NULL) {
- /* The XID doesn't match a call from that
- conversation, so it's probably not an RPC reply. */
-
- /* unless we're permitted to scan for embedded records
- * and this is a connection-oriented transport, give up */
- if (((! rpc_find_fragment_start) || (pinfo->ptype != PT_TCP)) && (pinfo->ptype != PT_IBQP)) {
- return FALSE;
- }
-
- /* in parse-partials, so define a dummy conversation for this reply */
- rpc_call = wmem_new0(wmem_file_scope(), rpc_call_info_value);
- rpc_call->rep_num = pinfo->fd->num;
- rpc_call->xid = xid;
- rpc_call->flavor = FLAVOR_NOT_GSSAPI; /* total punt */
- rpc_call->req_time = pinfo->fd->abs_ts;
-
- /* store it */
- wmem_tree_insert32(rpc_conv_info->xids, xid, (void *)rpc_call);
- }
-
- /* pass rpc_info to subdissectors */
- rpc_call->request=FALSE;
+ rpc_prog = NULL;
break;
default:
@@ -2224,6 +2339,11 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
switch (msg_type) {
case RPC_CALL:
+ proto = rpc_prog->proto;
+ proto_id = rpc_prog->proto_id;
+ ett = rpc_prog->ett;
+ progname = rpc_prog->progname;
+
rpcvers = tvb_get_ntohl(tvb, offset);
if (rpc_tree) {
proto_tree_add_uint(rpc_tree,
@@ -2336,54 +2456,12 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
col_append_fstr(pinfo->cinfo, COL_INFO,"V%u %s %s",
vers, procname, msg_type_name);
- /* Keep track of the address whence the call came, and the
- port to which the call is being sent, so that we can
- match up calls with replies.
-
- If the transport is connection-oriented (we check, for
- now, only for "pinfo->ptype" of PT_TCP), we also take
- into account the port from which the call was sent
- and the address to which the call was sent, because
- the addresses and ports of the two endpoints should be
- the same for all calls and replies. (XXX - what if
- the connection is broken and re-established?)
-
- If the transport is connectionless, we don't worry
- about the address to which the call was sent and from
- which the reply was sent, because there's no
- guarantee that the reply will come from the address
- to which the call was sent. We also don't worry about
- the port *from* which the call was sent and *to* which
- the reply was sent, because some clients (*cough* OS X
- NFS client *cough) might send retransmissions from a
- different port from the original request. */
- if ((pinfo->ptype == PT_TCP) || (pinfo->ptype == PT_IBQP)) {
- conversation = find_conversation(pinfo->fd->num, &pinfo->src,
- &pinfo->dst, pinfo->ptype, pinfo->srcport,
- pinfo->destport, 0);
- } else {
- /*
- * XXX - you currently still have to pass a non-null
- * pointer for the second address argument even
- * if you use NO_ADDR_B.
- */
- conversation = find_conversation(pinfo->fd->num, &pinfo->src,
- &null_address, pinfo->ptype, pinfo->destport,
- 0, NO_ADDR_B|NO_PORT_B);
- }
- if (conversation == NULL) {
- /* It's not part of any conversation - create a new
- one. */
- if (pinfo->ptype == PT_TCP) {
- conversation = conversation_new(pinfo->fd->num, &pinfo->src,
- &pinfo->dst, pinfo->ptype, pinfo->srcport,
- pinfo->destport, 0);
- } else {
- conversation = conversation_new(pinfo->fd->num, &pinfo->src,
- &null_address, pinfo->ptype, pinfo->destport,
- 0, NO_ADDR2|NO_PORT2);
- }
- }
+ /*
+ * Establish a conversation for the call, for use when
+ * matching calls with replies.
+ */
+ conversation = get_conversation_for_call(pinfo);
+
/*
* Do we already have a state structure for this conv
*/
@@ -3154,10 +3232,10 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
gint32 seglen;
gint tvb_len, tvb_reported_len;
tvbuff_t *frag_tvb;
+ conversation_t *conversation = NULL;
gboolean rpc_succeeded;
gboolean save_fragmented;
rpc_fragment_key old_rfk, *rfk, *new_rfk;
- conversation_t *conversation;
fragment_head *ipfd_head;
tvbuff_t *rec_tvb;
@@ -3192,6 +3270,7 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
*/
if (len > max_rpc_tcp_pdu_size)
return 0; /* pretend it's not valid */
+
if (rpc_desegment) {
seglen = tvb_reported_length_remaining(tvb, offset + 4);
@@ -3201,20 +3280,14 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
* data for this message, but we can do
* reassembly on it.
*
- * If this is a heuristic dissector, just
+ * If this is a heuristic dissector, check
+ * whether it looks like the beginning of
+ * an RPC call or reply. If not, then, 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.
- *
- * XXX - this means that we won't
- * recognize the first fragment of a
- * multi-fragment RPC operation unless
- * we've already identified this
- * conversation as being an RPC
- * conversation (and thus aren't running
- * heuristically) - that would be a problem
- * if, for example, the first segment were
- * the beginning of a large NFS WRITE.
+ * us to misidentify this as valid. Otherwise,
+ * mark this conversation as being for RPC and
+ * try to get more data.
*
* If this isn't a heuristic dissector,
* we've already identified this conversation
@@ -3222,13 +3295,61 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
* 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 -((gint32) pinfo->desegment_len);
+ if (is_heur) {
+ guint32 msg_type;
+
+ if (!tvb_bytes_exist(tvb, offset + 4, 8)) {
+ /*
+ * Captured data in packet isn't
+ * enough to let us tell.
+ */
+ return 0;
+ }
+
+ /* both directions need at least this */
+ msg_type = tvb_get_ntohl(tvb, offset + 4 + 4);
+
+ switch (msg_type) {
+
+ case RPC_CALL:
+ /* Check for RPC call. */
+ if (looks_like_rpc_call(tvb,
+ offset + 4) == NULL) {
+ /* Doesn't look like a call. */
+ return 0;
+ }
+ break;
+
+ case RPC_REPLY:
+ /* Check for RPC reply. */
+ if (looks_like_rpc_reply(tvb, pinfo,
+ offset + 4) == NULL) {
+ /* Doesn't look like a reply. */
+ return 0;
+ }
+ break;
+
+ default:
+ /* The putative message type field
+ contains neither RPC_CALL nor
+ RPC_REPLY, so it's not an RPC
+ call or reply. */
+ return 0;
+ }
+
+ /* Get this conversation, creating it if
+ it doesn't already exist, and make the
+ dissector for it the non-heuristic RPC
+ dissector for RPC-over-TCP. */
+ conversation = get_conversation_for_tcp(pinfo);
+ conversation_set_dissector(conversation,
+ rpc_tcp_handle);
}
+
+ /* Try to get more data. */
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = len - seglen;
+ return -((gint32) pinfo->desegment_len);
}
}
len += 4; /* include record mark */
@@ -3275,19 +3396,12 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
*
* 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.
+ * one, create it.
*/
- conversation = find_conversation(pinfo->fd->num, &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->fd->num, &pinfo->src, &pinfo->dst,
- pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
- }
+ if (conversation == NULL)
+ conversation = get_conversation_for_tcp(pinfo);
old_rfk.conv_id = conversation->index;
old_rfk.seq = seq;
old_rfk.port = pinfo->srcport;
@@ -3696,22 +3810,9 @@ find_and_dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo,
/*
- * Can return:
- *
- * NEED_MORE_DATA, if we don't have enough data to dissect anything;
- *
- * IS_RPC, if we dissected at least one message in its entirety
- * as RPC;
- *
- * IS_NOT_RPC, if we found no RPC message.
+ * Returns TRUE if it looks like ONC RPC, FALSE otherwise.
*/
-typedef enum {
- NEED_MORE_DATA,
- IS_RPC,
- IS_NOT_RPC
-} rpc_tcp_return_t;
-
-static rpc_tcp_return_t
+static gboolean
dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gboolean is_heur, struct tcpinfo* tcpinfo)
{
@@ -3741,10 +3842,11 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
first_pdu = FALSE;
if (len < 0) {
/*
- * We need more data from the TCP stream for
+ * dissect_rpc_fragment() thinks this is ONC RPC,
+ * but we need more data from the TCP stream for
* this fragment.
*/
- return NEED_MORE_DATA;
+ return TRUE;
}
if (len == 0) {
/*
@@ -3778,7 +3880,7 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
offset += len;
saw_rpc = TRUE;
}
- return saw_rpc ? IS_RPC : IS_NOT_RPC;
+ return saw_rpc;
}
static gboolean
@@ -3786,19 +3888,7 @@ dissect_rpc_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *
{
struct tcpinfo* tcpinfo = (struct tcpinfo *)data;
- switch (dissect_rpc_tcp_common(tvb, pinfo, tree, TRUE, tcpinfo)) {
-
- case IS_RPC:
- return TRUE;
-
- case IS_NOT_RPC:
- return FALSE;
-
- default:
- /* "Can't happen" */
- DISSECTOR_ASSERT_NOT_REACHED();
- return FALSE;
- }
+ return dissect_rpc_tcp_common(tvb, pinfo, tree, TRUE, tcpinfo);
}
static int
@@ -3806,7 +3896,7 @@ dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
struct tcpinfo* tcpinfo = (struct tcpinfo *)data;
- if (dissect_rpc_tcp_common(tvb, pinfo, tree, FALSE, tcpinfo) == IS_NOT_RPC)
+ if (!dissect_rpc_tcp_common(tvb, pinfo, tree, FALSE, tcpinfo))
dissect_rpc_continuation(tvb, pinfo, tree);
return tvb_reported_length(tvb);