From 6d3c94a53aa8deac5f524248c3dd89c54a92bf5a Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 10 Sep 2006 14:03:08 +0000 Subject: implement support to reassemble tcp sessions until the end of the session (FIN) add required code to the http (and others) code in req_resp_hdrs.c to signal to tcp when it wants a session to be reassembled to the FIN. This is currently done for all HTTP packets where we have a Content-type in the header but no content-length. svn path=/trunk/; revision=19185 --- epan/dissectors/packet-tcp.c | 81 +++++++++++++++++++++++++++++++++++++------- epan/dissectors/packet-tcp.h | 5 +++ epan/packet_info.h | 12 ++++++- epan/req_resp_hdrs.c | 25 ++++++++++++++ 4 files changed, 110 insertions(+), 13 deletions(-) diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c index e081266f80..dcb8e2bcd0 100644 --- a/epan/dissectors/packet-tcp.c +++ b/epan/dissectors/packet-tcp.c @@ -246,6 +246,7 @@ get_tcp_conversation_data(packet_info *pinfo) tcpd->flow1.nextseqframe=0; tcpd->flow1.window=0; tcpd->flow1.win_scale=-1; + tcpd->flow1.flags=0; tcpd->flow1.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus"); tcpd->flow2.segments=NULL; tcpd->flow2.base_seq=0; @@ -259,6 +260,7 @@ get_tcp_conversation_data(packet_info *pinfo) tcpd->flow2.nextseqframe=0; tcpd->flow2.window=0; tcpd->flow2.win_scale=-1; + tcpd->flow2.flags=0; tcpd->flow2.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus"); tcpd->acked_table=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_analyze_acked_table"); @@ -1291,6 +1293,16 @@ again: } if (must_desegment) { + /* If the dissector requested "reassemble until FIN" + * just set this flag for the flow and let reassembly + * proceed at normal. We will check/pick up these + * reassembled PDUs later down in dissect_tcp() when checking + * for the FIN flag. + */ + if(pinfo->desegment_len==DESEGMENT_UNTIL_FIN){ + tcpd->fwd->flags|=TCP_FLOW_REASSEMBLE_UNTIL_FIN; + } + /* * The sequence number at which the stuff to be desegmented * starts is the sequence number of the byte at an offset @@ -1303,19 +1315,18 @@ again: */ deseg_seq = seq + (deseg_offset - offset); - if ((nxtseq - deseg_seq) <= 1024*1024) { - if(!pinfo->fd->flags.visited){ - msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq, - nxtseq + pinfo->desegment_len, tcpd); - - /* add this segment as the first one for this new pdu */ - fragment_add(tvb, deseg_offset, pinfo, msp->first_frame, - tcp_fragment_table, - 0, - nxtseq - deseg_seq, - LT_SEQ(nxtseq, msp->nxtpdu)); + if( ((nxtseq - deseg_seq) <= 1024*1024) + && (!pinfo->fd->flags.visited) ){ + msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq, + nxtseq + pinfo->desegment_len, tcpd); + + /* add this segment as the first one for this new pdu */ + fragment_add(tvb, deseg_offset, pinfo, msp->first_frame, + tcp_fragment_table, + 0, + nxtseq - deseg_seq, + LT_SEQ(nxtseq, msp->nxtpdu)); } - } } if (!called_dissector || pinfo->desegment_len != 0) { @@ -2465,6 +2476,52 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) } tap_queue_packet(tcp_tap, pinfo, tcph); + + /* A FIN packet might complete reassembly so we need to explicitely + * check for this here. + * If this segment completes reassembly we add the FIN as a final dummy + * byte to the reassembled PDU and check if reassembly completed successfully + */ + if( (tcph->th_flags & TH_FIN) + && (tcpd->fwd->flags&TCP_FLOW_REASSEMBLE_UNTIL_FIN) ){ + struct tcp_multisegment_pdu *msp; + + /* find the most previous PDU starting before this sequence number */ + msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq-1); + if(msp){ + fragment_data *ipfd_head; + + ipfd_head = fragment_add(tvb, offset-1, pinfo, msp->first_frame, + tcp_fragment_table, + tcph->th_seq - msp->seq, + 1, + FALSE ); + if(ipfd_head){ + tvbuff_t *next_tvb; + + /* create a new TVB structure for desegmented data + * datalen-1 to strip the dummy FIN byte off + */ + next_tvb = tvb_new_real_data(ipfd_head->data, ipfd_head->datalen-1, ipfd_head->datalen-1); + + /* add this tvb as a child to the original one */ + tvb_set_child_real_data_tvbuff(tvb, next_tvb); + + /* add desegmented data to the data source list */ + add_new_data_source(pinfo, next_tvb, "Reassembled TCP"); + + /* call the payload dissector + * but make sure we dont offer desegmentation any more + */ + pinfo->can_desegment = 0; + + process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, tcph->th_sport, tcph->th_dport, tcph->th_seq, nxtseq, FALSE, tcpd); + + return; + } + } + } + /* * XXX - what, if any, of this should we do if this is included in an * error packet? It might be nice to see the details of the packet diff --git a/epan/dissectors/packet-tcp.h b/epan/dissectors/packet-tcp.h index 3e75b26e01..bc9f87b136 100644 --- a/epan/dissectors/packet-tcp.h +++ b/epan/dissectors/packet-tcp.h @@ -137,6 +137,11 @@ typedef struct _tcp_flow_t { */ guint32 window; /* last seen window */ gint16 win_scale; /* -1 is we dont know */ +/* This tcp flow/session contains only one single PDU and should + * be reassembled until the final FIN segment. + */ +#define TCP_FLOW_REASSEMBLE_UNTIL_FIN 0x0001 + guint16 flags; guint32 lastsegmentflags; /* This tree is indexed by sequence number and keeps track of all diff --git a/epan/packet_info.h b/epan/packet_info.h index 28b392ee10..21b35306a9 100644 --- a/epan/packet_info.h +++ b/epan/packet_info.h @@ -85,7 +85,17 @@ typedef struct _packet_info { finished setting things up, so the TCP desegmentor can desegment its payload). */ int desegment_offset; /* offset to stuff needing desegmentation */ - guint32 desegment_len; /* requested desegmentation additional length */ +#define DESEGMENT_ONE_MORE_SEGMENT 0x0fffffff +#define DESEGMENT_UNTIL_FIN 0x0ffffffe + guint32 desegment_len; /* requested desegmentation additional length + or + DESEGMENT_ONE_MORE_SEGMENT: + Desegment one more full segment + (not yet implemented) + DESEGMENT_UNTIL_FIN: + Desgment all data for this tcp session + until the FIN segment. + */ guint16 want_pdu_tracking; /* >0 if the subdissector has specified a value in 'bytes_until_next_pdu'. When a dissector detects that the next PDU diff --git a/epan/req_resp_hdrs.c b/epan/req_resp_hdrs.c index cbc861de3e..5696c51558 100644 --- a/epan/req_resp_hdrs.c +++ b/epan/req_resp_hdrs.c @@ -48,6 +48,7 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, int offset, packet_info *pinfo, gchar *header_val; long int content_length; gboolean content_length_found = FALSE; + gboolean content_type_found = FALSE; gboolean chunked_encoding = FALSE; /* @@ -154,6 +155,9 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, int offset, packet_info *pinfo, == 1) content_length_found = TRUE; g_free(header_val); + } else if (tvb_strncaseeql(tvb, next_offset_sav, + "Content-Type:", 13) == 0) { + content_type_found = TRUE; } else if (tvb_strncaseeql(tvb, next_offset_sav, "Transfer-Encoding:", 18) == 0) { @@ -329,6 +333,27 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, int offset, packet_info *pinfo, } } + } else if (content_type_found && pinfo->can_desegment) { + /* We found a content-type but no content-length. + * This is probably a HTTP header for a session with + * only one HTTP PDU and where the content spans + * until the end of the tcp session. + * Set up tcp reassembly until the end of this session. + */ + length_remaining = tvb_length_remaining(tvb, next_offset); + reported_length_remaining = tvb_reported_length_remaining(tvb, next_offset); + if (length_remaining < reported_length_remaining) { + /* + * It's a waste of time asking for more + * data, because that data wasn't captured. + */ + return TRUE; + } + + pinfo->desegment_offset = offset; + pinfo->desegment_len = DESEGMENT_UNTIL_FIN; + + return FALSE; } } -- cgit v1.2.3