diff --git a/doc/README.developer b/doc/README.developer index 15edab5e90..b7e564b333 100644 --- a/doc/README.developer +++ b/doc/README.developer @@ -2462,6 +2462,13 @@ packet-msproxy.c, a conversation can install a dissector to handle the secondary protocol dissection. After the conversation is created for the negotiated ports use the conversation_set_dissector to define the dissection routine. +Before we create these conversations or assign a dissector to them we should +first check that the conversation does not already exist and if it exists +whether it is registered to our protocol or not. +We should do this because is uncommon but it does happen that multiple +different protocols can use the same socketpair during different stages of +an application cycle. By keeping track of the frame number a conversation +was started in ethereal can still tell these different protocols apart. The second argument to conversation_set_dissector is a dissector handle, which is created with a call to create_dissector_handle or @@ -2489,16 +2496,30 @@ static void sub_dissector( tvbuff_t *tvb, packet_info *pinfo, /* if conversation has a data field, create it and load structure */ - new_conv_info = g_mem_chunk_alloc( new_conv_vals); - new_conv_info->data1 = value1; +/* First check if a conversation already exists for this + socketpair +*/ + conversation = find_conversation(pinfo->fd->num, + &pinfo->src, &pinfo->dst, protocol, + src_port, dst_port, new_conv_info, 0); + +/* If there is no such conversation, or if there is one but for + someone elses protocol then we just create a new conversation + and assign our protocol to it. +*/ + if( (conversation==NULL) + || (conversation->dissector_handle!=sub_dissector_handle) ){ + new_conv_info = g_mem_chunk_alloc( new_conv_vals); + new_conv_info->data1 = value1; /* create the conversation for the dynamic port */ - conversation = conversation_new( &pinfo->src, &pinfo->dst, protocol, - src_port, dst_port, new_conv_info, 0); + conversation = conversation_new(pinfo->fd->num, + &pinfo->src, &pinfo->dst, protocol, + src_port, dst_port, new_conv_info, 0); /* set the dissector for the new conversation */ - conversation_set_dissector(conversation, sub_dissector_handle); - + conversation_set_dissector(conversation, sub_dissector_handle); + } ... void @@ -2525,6 +2546,19 @@ the server port and address. The key is to create the new conversation with the second address and port set to the "accept any" values. +Some server applications can use the same port for different protocols during +different stages of a transaction. For example it might initially use SNMP +to perform some discovery and later switch to use TFTP using the same port. +In order to handle this properly we must first check whether such a +conversation already exists or not and if it exists we also check whether the +registered dissector_handle for that conversation is "our" dissector or not. +If not we create a new conversation ontop of the previous one and set this new +conversation to use our protocol. +Since ethereal keeps track of the frame number where a conversation started +ethereal will still be able to keep the packets apart eventhough they do use +the same socketpair. + (See packet-tftp.c and packet-snmp.c for examples of this) + There are two support routines that will allow the second port and/or address to be set latter. @@ -2556,12 +2590,25 @@ static dissector_handle_t sub_dissector_handle; /* NOTE: The second address and port values don't matter because the */ /* NO_ADDR2 and NO_PORT2 options are set. */ - conversation = conversation_new( &server_src_addr, 0, protocol, +/* First check if a conversation already exists for this + IP/protocol/port +*/ + conversation = find_conversation(pinfo->fd->num, + &server_src_addr, 0, protocol, + server_src_port, 0, NO_ADDR2 | NO_PORT_B); +/* If there is no such conversation, or if there is one but for + someone elses protocol then we just create a new conversation + and assign our protocol to it. +*/ + if( (conversation==NULL) + || (conversation->dissector_handle!=sub_dissector_handle) ){ + conversation = conversation_new(pinfo->fd->num, + &server_src_addr, 0, protocol, server_src_port, 0, new_conv_info, NO_ADDR2 | NO_PORT2); /* set the dissector for the new conversation */ - conversation_set_dissector(conversation, sub_dissector_handle); - + conversation_set_dissector(conversation, sub_dissector_handle); + } 2.5 Per packet information diff --git a/epan/conversation.c b/epan/conversation.c index 17fb491aef..3911b9e682 100644 --- a/epan/conversation.c +++ b/epan/conversation.c @@ -1119,8 +1119,7 @@ conversation_delete_proto_data(conversation_t *conv, int proto) } void -conversation_set_dissector(conversation_t *conversation, - dissector_handle_t handle) +conversation_set_dissector(conversation_t *conversation, dissector_handle_t handle) { conversation->dissector_handle = handle; } @@ -1129,6 +1128,11 @@ conversation_set_dissector(conversation_t *conversation, * Given two address/port pairs for a packet, search for a matching * conversation and, if found and it has a conversation dissector, * call that dissector and return TRUE, otherwise return FALSE. + * + * This helper uses call_dissector_only which will NOT call the default + * "data" dissector if the packet was rejected. + * Our caller is responsible to call the data dissector explicitely in case + * this function returns FALSE. */ gboolean try_conversation_dissector(address *addr_a, address *addr_b, port_type ptype, @@ -1141,10 +1145,18 @@ try_conversation_dissector(address *addr_a, address *addr_b, port_type ptype, port_b, 0); if (conversation != NULL) { + int ret; if (conversation->dissector_handle == NULL) return FALSE; - call_dissector(conversation->dissector_handle, tvb, pinfo, + ret=call_dissector_only(conversation->dissector_handle, tvb, pinfo, tree); + if(!ret) { + /* this packet was rejected by the dissector + * so return FALSE in case our caller wants + * to do some cleaning up. + */ + return FALSE; + } return TRUE; } return FALSE; diff --git a/epan/dissectors/packet-snmp.c b/epan/dissectors/packet-snmp.c index 9df48e2d69..2364bf3fa6 100644 --- a/epan/dissectors/packet-snmp.c +++ b/epan/dissectors/packet-snmp.c @@ -2361,7 +2361,7 @@ dissect_snmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) if (pinfo->destport == UDP_PORT_SNMP) { conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP, pinfo->srcport, 0, NO_PORT_B); - if (conversation == NULL) { + if( (conversation == NULL) || (conversation->dissector_handle!=snmp_handle) ){ conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP, pinfo->srcport, 0, NO_PORT2); conversation_set_dissector(conversation, snmp_handle); diff --git a/epan/dissectors/packet-tftp.c b/epan/dissectors/packet-tftp.c index 321b306b5c..dfebc77b23 100644 --- a/epan/dissectors/packet-tftp.c +++ b/epan/dissectors/packet-tftp.c @@ -117,10 +117,10 @@ dissect_tftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) if (pinfo->destport == UDP_PORT_TFTP) { conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP, pinfo->srcport, 0, NO_PORT_B); - if (conversation == NULL) { + if( (conversation == NULL) || (conversation->dissector_handle!=tftp_handle) ){ conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP, pinfo->srcport, 0, NO_PORT2); - conversation_set_dissector(conversation, tftp_handle); + conversation_set_dissector(conversation, tftp_handle); } } diff --git a/epan/dissectors/packet-udp.c b/epan/dissectors/packet-udp.c index 2a6a104d70..462ad15850 100644 --- a/epan/dissectors/packet-udp.c +++ b/epan/dissectors/packet-udp.c @@ -98,8 +98,9 @@ decode_udp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo, /* for the conversation if available */ if (try_conversation_dissector(&pinfo->src, &pinfo->dst, PT_UDP, - uh_sport, uh_dport, next_tvb, pinfo, tree)) + uh_sport, uh_dport, next_tvb, pinfo, tree)){ return; + } if (try_heuristic_first) { /* do lookup with the heuristic subdissector table */ diff --git a/epan/packet.c b/epan/packet.c index 9f2ca913fb..a012ef7c2d 100644 --- a/epan/packet.c +++ b/epan/packet.c @@ -365,6 +365,16 @@ struct dissector_handle { protocol_t *protocol; }; +/* This function will return + * old style dissector : + * length of the payload or 1 of the payload is empty + * new dissector : + * >0 this protocol was successfully dissected and this was this protocol. + * 0 this packet did not match this protocol. + * + * The only time this function will return 0 is if it is a new style dissector + * and if the dissector rejected the packet. + */ static int call_dissector_through_handle(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) @@ -379,9 +389,9 @@ call_dissector_through_handle(dissector_handle_t handle, tvbuff_t *tvb, proto_get_protocol_short_name(handle->protocol); } - if (handle->is_new) + if (handle->is_new) { ret = (*handle->dissector.new)(tvb, pinfo, tree); - else { + } else { (*handle->dissector.old)(tvb, pinfo, tree); ret = tvb_length(tvb); if (ret == 0) { @@ -1691,7 +1701,9 @@ new_register_dissector(const char *name, new_dissector_t dissector, int proto) (gpointer) handle); } -/* Call a dissector through a handle. */ +/* Call a dissector through a handle and if this fails call the "data" + * dissector. + */ int call_dissector(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) @@ -1712,6 +1724,19 @@ call_dissector(dissector_handle_t handle, tvbuff_t *tvb, return ret; } +/* Call a dissector through a handle but if the dissector rejected it + * return 0 instead of using the default "data" dissector. + */ +int +call_dissector_only(dissector_handle_t handle, tvbuff_t *tvb, + packet_info *pinfo, proto_tree *tree) +{ + int ret; + + ret = call_dissector_work(handle, tvb, pinfo, tree); + return ret; +} + /* * Dumps the "layer type"/"decode as" associations to stdout, similar * to the proto_registrar_dump_*() routines. diff --git a/epan/packet.h b/epan/packet.h index cdc2f8ff9e..eed4554b53 100644 --- a/epan/packet.h +++ b/epan/packet.h @@ -298,9 +298,16 @@ extern dissector_handle_t create_dissector_handle(dissector_t dissector, extern dissector_handle_t new_create_dissector_handle(new_dissector_t dissector, int proto); -/* Call a dissector through a handle. */ +/* Call a dissector through a handle and if no dissector was found + * pass if over to the "data" dissector instead. + */ extern int call_dissector(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +/* Call a dissector through a handle but if no dissector was found + * just return 0 and do not call the "data" dissector instead. + */ +extern int call_dissector_only(dissector_handle_t handle, tvbuff_t *tvb, + packet_info *pinfo, proto_tree *tree); /* Do all one-time initialization. */ extern void dissect_init(void);