diff options
-rw-r--r-- | epan/dissectors/packet-rpc.c | 796 |
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); |