From Pierre Juhen:
[obnox/wireshark/wip.git] / epan / dissectors / packet-tcp.c
index a0db7127370f26592987743e7e9e8f3cecf3c47c..a8998cf5e3a5d5835faa21f0fcb9999d33b09105 100644 (file)
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -112,6 +112,7 @@ static int hf_tcp_analysis_zero_window_probe = -1;
 static int hf_tcp_analysis_zero_window_probe_ack = -1;
 static int hf_tcp_continuation_to = -1;
 static int hf_tcp_pdu_time = -1;
+static int hf_tcp_pdu_size = -1;
 static int hf_tcp_pdu_last_frame = -1;
 static int hf_tcp_reassembled_in = -1;
 static int hf_tcp_segments = -1;
@@ -121,6 +122,7 @@ static int hf_tcp_segment_overlap_conflict = -1;
 static int hf_tcp_segment_multiple_tails = -1;
 static int hf_tcp_segment_too_long_fragment = -1;
 static int hf_tcp_segment_error = -1;
+static int hf_tcp_options = -1;
 static int hf_tcp_option_mss = -1;
 static int hf_tcp_option_mss_val = -1;
 static int hf_tcp_option_wscale = -1;
@@ -246,6 +248,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 +262,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");
 
@@ -308,7 +312,7 @@ scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int o
         struct tcp_multisegment_pdu *msp=NULL;
 
        if(!pinfo->fd->flags.visited){
-               msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+               msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
                if(msp){
                        /* If this segment is completely within a previous PDU
                         * then we just skip this packet
@@ -326,7 +330,7 @@ scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int o
 
                }
        } else {
-               msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+               msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
                if(msp){
                        if(pinfo->fd->num==msp->first_frame){
                                proto_item *item;
@@ -355,7 +359,6 @@ scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int o
                        }
                }
        }
-
        return offset;
 }
 
@@ -633,7 +636,7 @@ printf("REV list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->rev->lastsegmentflag
 
 
 finished_fwd:
-       /* If this was NOT a dupack we must reset the dupack countres */
+       /* If this was NOT a dupack we must reset the dupack counters */
        if( (!tcpd->ta) || !(tcpd->ta->flags&TCP_A_DUPLICATE_ACK) ){
                tcpd->fwd->lastnondupack=pinfo->fd->num;
                tcpd->fwd->dupacknum=0;
@@ -649,7 +652,8 @@ finished_fwd:
         * in the other direction.
         */
        if( tcpd->rev->nextseq
-       &&  GT_SEQ(ack, tcpd->rev->nextseq )){
+       &&  GT_SEQ(ack, tcpd->rev->nextseq )
+       &&  (flags&(TH_ACK))!=0 ){
 /*QQQ tested*/
                if(!tcpd->ta){
                        tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
@@ -775,6 +779,11 @@ finished_checking_retransmission_type:
        /* first we remove all such segments at the head of the list */
        while((ual=tcpd->rev->segments)){
                tcp_unacked_t *tmpual;
+               if(ack==ual->nextseq){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+                       tcpd->ta->frame_acked=ual->frame;
+                       nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &ual->ts);
+               }
                if(GT_SEQ(ual->nextseq,ack)){
                        break;
                }
@@ -804,11 +813,6 @@ finished_checking_retransmission_type:
                ual=ual->next;
        }
 
-#ifdef REMOVED
-               tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
-               tcpd->ta->frame_acked=tcpd->rev->segments->frame;
-               nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &tcpd->rev->segments->ts);
-#endif
 }
 
 static void
@@ -947,8 +951,8 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                        flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_frame,
                                tvb, 0, 0, ta->dupack_frame);
                        PROTO_ITEM_SET_GENERATED(flags_item);
-                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u) to ACK in packet #%u",
-                               ta->dupack_num, ta->dupack_frame);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u)",
+                               ta->dupack_num);
                }
                if( ta->flags&TCP_A_ZERO_WINDOW_PROBE ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_probe, tvb, 0, 0, "This is a TCP zero-window-probe");
@@ -1082,7 +1086,7 @@ again:
        deseg_offset = offset;
 
        /* find the most previous PDU starting before this sequence number */
-       msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+       msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
        if(msp && msp->seq<=seq && msp->nxtpdu>seq){
                int len;
 
@@ -1291,6 +1295,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 +1317,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) {
@@ -1418,6 +1431,7 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
   guint plen;
   guint length;
   tvbuff_t *next_tvb;
+  proto_item *item=NULL;
 
   while (tvb_reported_length_remaining(tvb, offset) != 0) {
     /*
@@ -1467,6 +1481,13 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
       show_reported_bounds_error(tvb, pinfo, tree);
       return;
     }
+    /*
+     * Display the PDU length as a field 
+     */ 
+    item=proto_tree_add_uint(pinfo->tcp_tree, hf_tcp_pdu_size, tvb, 0, 0, plen);
+    PROTO_ITEM_SET_GENERATED(item);
+        
+
 
     /* give a hint to TCP where the next PDU starts
      * so that it can attempt to find it in case it starts
@@ -1536,7 +1557,7 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
       RETHROW;
     }
     CATCH(ReportedBoundsError) {
-      show_reported_bounds_error(tvb, pinfo, tree);
+     show_reported_bounds_error(tvb, pinfo, tree);
     }
     ENDTRY;
 
@@ -1676,7 +1697,7 @@ dissect_tcpopt_timestamp(const ip_tcp_opt *optp, tvbuff_t *tvb,
   proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_time_stamp, tvb,
                                offset, optlen, TRUE);
   proto_tree_add_text(opt_tree, tvb, offset,      optlen,
-    "%s: tsval %u, tsecr %u", optp->name, tsv, tser);
+    "%s: TSval %u, TSecr %u", optp->name, tsv, tser);
   tcp_info_append_uint(pinfo, "TSV", tsv);
   tcp_info_append_uint(pinfo, "TSER", tser);
 }
@@ -1762,7 +1783,7 @@ static const ip_tcp_opt tcpopts[] = {
   },
   {
     TCPOPT_TIMESTAMP,
-    "Time stamp",
+    "Timestamps",
     NULL,
     FIXED_LENGTH,
     TCPOLEN_TIMESTAMP,
@@ -2052,16 +2073,11 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   guint      length_remaining;
   gboolean   desegment_ok;
   struct tcpinfo tcpinfo;
-  static struct tcpheader tcphstruct[4], *tcph;
-  static int tcph_count=0;
+  struct tcpheader *tcph;
   proto_item *tf_syn = NULL, *tf_fin = NULL, *tf_rst = NULL;
   struct tcp_analysis *tcpd=NULL;
 
-  tcph_count++;
-  if(tcph_count>=4){
-     tcph_count=0;
-  }
-  tcph=&tcphstruct[tcph_count];
+  tcph=ep_alloc(sizeof(struct tcpheader));
   SET_ADDRESS(&tcph->ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data);
   SET_ADDRESS(&tcph->ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data);
 
@@ -2089,6 +2105,8 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            ti = proto_tree_add_item(tree, proto_tcp, tvb, 0, -1, FALSE);
     }
     tcp_tree = proto_item_add_subtree(ti, ett_tcp);
+    pinfo->tcp_tree=tcp_tree;
+
     proto_tree_add_uint_format(tcp_tree, hf_tcp_srcport, tvb, offset, 2, tcph->th_sport,
        "Source port: %s (%u)", get_tcp_port(tcph->th_sport), tcph->th_sport);
     proto_tree_add_uint_format(tcp_tree, hf_tcp_dstport, tvb, offset + 2, 2, tcph->th_dport,
@@ -2134,9 +2152,12 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
   if (!pinfo->fragmented && !pinfo->in_error_pkt) {
     if (reported_len < tcph->th_hlen) {
-      proto_tree_add_text(tcp_tree, tvb, offset, 0,
+      proto_item *pi;
+      pi = proto_tree_add_text(tcp_tree, tvb, offset, 0,
         "Short segment. Segment/fragment does not contain a full TCP header"
         " (might be NMAP or someone else deliberately sending unusual packets)");
+      PROTO_ITEM_SET_GENERATED(pi);
+      expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_WARN, "Short segment");
       tcph->th_have_seglen = FALSE;
     } else {
       /* Compute the length of data in this segment. */
@@ -2247,7 +2268,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
        "Header length: %u bytes", tcph->th_hlen);
     tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 13, 1,
-       tcph->th_flags, "Flags: 0x%04x (%s)", tcph->th_flags, flags);
+       tcph->th_flags, "Flags: 0x%02x (%s)", tcph->th_flags, flags);
     field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_ecn, tvb, offset + 13, 1, tcph->th_flags);
@@ -2257,27 +2278,29 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     tf_rst = proto_tree_add_boolean(field_tree, hf_tcp_flags_reset, tvb, offset + 13, 1, tcph->th_flags);
     tf_syn = proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, tcph->th_flags);
     tf_fin = proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, tcph->th_flags);
-    if(tcp_relative_seq && (tcph->th_win!=real_window)){
+    if(tcp_relative_seq 
+    && (tcph->th_win!=real_window) 
+    && !(tcph->th_flags&TH_SYN) ){   /* SYNs are never scaled */
       proto_tree_add_uint_format(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win, "Window size: %u (scaled)", tcph->th_win);
     } else {
-      proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win);
+      proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, real_window);
     }
   }
 
   if(tcph->th_flags & TH_SYN) {
     if(tcph->th_flags & TH_ACK)
-      expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): %s -> %s",
-                             get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+      expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): server port %s",
+                             get_tcp_port(tcph->th_sport));
     else
-      expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): %s -> %s",
-                             get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+      expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): server port %s",
+                             get_tcp_port(tcph->th_dport));
   }
   if(tcph->th_flags & TH_FIN)
-    expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN): %s -> %s",
-                           get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+    /* XXX - find a way to know the server port and output only that one */
+    expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN)");
   if(tcph->th_flags & TH_RST)
-    expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST): %s -> %s",
-                           get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+    /* XXX - find a way to know the server port and output only that one */
+    expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST)");
 
   /* Supply the sequence number of the first byte and of the first byte
      after the segment. */
@@ -2420,8 +2443,9 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     optlen = tcph->th_hlen - TCPH_MIN_LEN; /* length of options, in bytes */
     tvb_ensure_bytes_exist(tvb, offset +  20, optlen);
     if (tcp_tree != NULL) {
-      tf = proto_tree_add_text(tcp_tree, tvb, offset +  20, optlen,
-        "Options: (%u bytes)", optlen);
+      guint8 *p_options = tvb_get_ephemeral_string(tvb, offset + 20, optlen);
+      tf = proto_tree_add_bytes_format(tcp_tree, hf_tcp_options, tvb, offset +  20, 
+        optlen, p_options, "Options: (%u bytes)", optlen);
       field_tree = proto_item_add_subtree(tf, ett_tcp_options);
     } else
       field_tree = NULL;
@@ -2467,6 +2491,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
@@ -2707,6 +2777,10 @@ proto_register_tcp(void)
                { "Reassembled PDU in frame", "tcp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                        "The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
 
+               { &hf_tcp_options,
+                 { "TCP Options", "tcp.options", FT_BYTES,
+                   BASE_HEX, NULL, 0x0, "TCP Options", HFILL }},
+
                { &hf_tcp_option_mss,
                  { "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
@@ -2775,6 +2849,10 @@ proto_register_tcp(void)
                { &hf_tcp_pdu_time,
                  { "Time until the last segment of this PDU", "tcp.pdu.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
                    "How long time has passed until the last frame of this PDU", HFILL}},
+               { &hf_tcp_pdu_size,
+                 { "PDU Size", "tcp.pdu.size", FT_UINT32, BASE_DEC, NULL, 0x0,
+                   "The size of this PDU", HFILL}},
+
                { &hf_tcp_pdu_last_frame,
                  { "Last frame of this PDU", "tcp.pdu.last_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                        "This is the last frame of the PDU starting in this segment", HFILL }},