aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);