Remove a bunch of duplicate semicolons.
[obnox/wireshark/wip.git] / packet-tcp.c
index 1fb096f8bc76e8e12b74e5180d7ff1b1a4889867..6275e5fc8f7238fbdfe4d06269377598dc04c770 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-tcp.c
  * Routines for TCP packet disassembly
  *
- * $Id: packet-tcp.c,v 1.198 2003/07/11 09:30:49 guy Exp $
+ * $Id: packet-tcp.c,v 1.207 2003/09/18 19:19:51 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -94,12 +94,14 @@ static int hf_tcp_analysis_retransmission = -1;
 static int hf_tcp_analysis_lost_packet = -1;
 static int hf_tcp_analysis_ack_lost_packet = -1;
 static int hf_tcp_analysis_keep_alive = -1;
+static int hf_tcp_analysis_keep_alive_ack = -1;
 static int hf_tcp_analysis_duplicate_ack = -1;
 static int hf_tcp_analysis_duplicate_ack_num = -1;
 static int hf_tcp_analysis_duplicate_ack_frame = -1;
 static int hf_tcp_analysis_zero_window = -1;
 static int hf_tcp_analysis_zero_window_probe = -1;
 static int hf_tcp_analysis_zero_window_violation = -1;
+static int hf_tcp_reassembled_in = -1;
 static int hf_tcp_segments = -1;
 static int hf_tcp_segment = -1;
 static int hf_tcp_segment_overlap = -1;
@@ -146,7 +148,7 @@ static const fragment_items tcp_segment_items = {
        &hf_tcp_segment_multiple_tails,
        &hf_tcp_segment_too_long_fragment,
        &hf_tcp_segment_error,
-       NULL,
+       &hf_tcp_reassembled_in,
        "Segments"
 };
 
@@ -180,6 +182,8 @@ struct tcp_unacked {
 
        /* this is to keep track of zero window and zero window probe */
        guint32 window;
+
+       guint32 flags;
 };
 
 /* Idea for gt: either x > y, or y is much bigger (assume wrap) */
@@ -191,18 +195,19 @@ struct tcp_unacked {
 
 static GMemChunk *tcp_acked_chunk = NULL;
 static int tcp_acked_count = 5000;     /* one for almost every other segment in the capture */
-#define TCP_A_RETRANSMISSION           0x01
-#define TCP_A_LOST_PACKET              0x02
-#define TCP_A_ACK_LOST_PACKET          0x04
-#define TCP_A_KEEP_ALIVE               0x08
-#define TCP_A_DUPLICATE_ACK            0x10
-#define TCP_A_ZERO_WINDOW              0x20
-#define TCP_A_ZERO_WINDOW_PROBE                0x40
-#define TCP_A_ZERO_WINDOW_VIOLATION    0x80
+#define TCP_A_RETRANSMISSION           0x0001
+#define TCP_A_LOST_PACKET              0x0002
+#define TCP_A_ACK_LOST_PACKET          0x0004
+#define TCP_A_KEEP_ALIVE               0x0008
+#define TCP_A_DUPLICATE_ACK            0x0010
+#define TCP_A_ZERO_WINDOW              0x0020
+#define TCP_A_ZERO_WINDOW_PROBE                0x0040
+#define TCP_A_ZERO_WINDOW_VIOLATION    0x0080
+#define TCP_A_KEEP_ALIVE_ACK           0x0100
 struct tcp_acked {
        guint32 frame_acked;
        nstime_t ts;
-       guint8 flags;
+       guint16 flags;
        guint32 dupack_num;     /* dup ack number */
        guint32 dupack_frame;   /* dup ack to frame # */
 };
@@ -213,6 +218,7 @@ static int tcp_rel_seq_count = 10000; /* one for each segment in the capture */
 struct tcp_rel_seq {
        guint32 seq_base;
        guint32 ack_base;
+       gint16  win_scale;
 };
 static GHashTable *tcp_rel_seq_table = NULL;
 
@@ -236,6 +242,8 @@ struct tcp_analysis {
        guint32 base_seq1;
        struct tcp_unacked *ual2;       /* UnAcked List 2*/
        guint32 base_seq2;
+       gint16 win_scale1;
+       gint16 win_scale2;
 
        /* these two lists are used to track when PDUs may start
           inside a segment.
@@ -273,8 +281,10 @@ get_tcp_conversation_data(packet_info *pinfo)
                tcpd=g_mem_chunk_alloc(tcp_analysis_chunk);
                tcpd->ual1=NULL;
                tcpd->base_seq1=0;
+               tcpd->win_scale1=-1;
                tcpd->ual2=NULL;
                tcpd->base_seq2=0;
+               tcpd->win_scale2=-1;
 
                tcpd->pdu_seq1=NULL;
                tcpd->pdu_seq2=NULL;
@@ -429,8 +439,32 @@ pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 nxtpdu)
        */
 }
 
+/* if we saw a window scaling option, store it for future reference 
+*/
 static void
-tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
+pdu_store_window_scale_option(packet_info *pinfo, guint8 ws)
+{
+       struct tcp_analysis *tcpd=NULL;
+       int direction;
+
+       /* find(or create if needed) the conversation for this tcp session */
+       tcpd=get_tcp_conversation_data(pinfo);
+
+       /* check direction and get pdu start list */
+       direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
+       /* if the addresses are equal, match the ports instead */
+       if(direction==0) {
+               direction= (pinfo->srcport > pinfo->destport)*2-1;
+       }
+       if(direction>=0){
+               tcpd->win_scale1=ws;
+       } else {
+               tcpd->win_scale2=ws;
+       }
+}
+
+static void
+tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack, guint32 *win)
 {
        struct tcp_rel_seq *trs;
 
@@ -441,6 +475,9 @@ tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
 
        (*seq) -= trs->seq_base;
        (*ack) -= trs->ack_base;
+       if(trs->win_scale!=-1){
+               (*win)<<=trs->win_scale;
+       }
 }
 
 static struct tcp_acked *
@@ -472,6 +509,7 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
        struct tcp_unacked *ual=NULL;
        guint32 base_seq=0;
        guint32 base_ack=0;
+       gint16  win_scale=-1;
        struct tcp_next_pdu **tnp=NULL;
 
        /* find(or create if needed) the conversation for this tcp session */
@@ -488,12 +526,14 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual2=tcpd->ual2;
                tnp=&tcpd->pdu_seq2;
                base_seq=tcpd->base_seq1;
+               win_scale=tcpd->win_scale1;
                base_ack=tcpd->base_seq2;
        } else {
                ual1=tcpd->ual2;
                ual2=tcpd->ual1;
                tnp=&tcpd->pdu_seq1;
                base_seq=tcpd->base_seq2;
+               win_scale=tcpd->win_scale2;
                base_ack=tcpd->base_seq1;
        }
 
@@ -532,6 +572,7 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual1->ts.secs=pinfo->fd->abs_secs;
                ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
                ual1->window=window;
+               ual1->flags=0;
                if(tcp_relative_seq){
                        base_seq=seq;
                        base_ack=ack;
@@ -552,6 +593,7 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual1->ts.secs=pinfo->fd->abs_secs;
                ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
                ual1->window=window;
+               ual1->flags=0;
                if(tcp_relative_seq){
                        base_seq=seq;
                        base_ack=ack;
@@ -581,11 +623,12 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual->ts.secs=pinfo->fd->abs_secs;
                ual->ts.nsecs=pinfo->fd->abs_usecs*1000;
                ual->window=window;
+               ual->flags=0;
                ual1=ual;
                goto seq_finished;
        }
 
-       /* keep-alives are empty semgents with a sequence number -1 of what
+       /* keep-alives are empty segments with a sequence number -1 of what
         * we would expect.
          *
         * Solaris is an exception, Solaris does not really use KeepAlives
@@ -603,11 +646,14 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
         * , brilliant)
         */
        if( (seglen<=1) && EQ_SEQ(seq, (ual1->nextseq-1)) ){
-               struct tcp_acked *ta;
-
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->flags|=TCP_A_KEEP_ALIVE;
-               goto seq_finished;
+               if(!(flags&TH_FIN)){ /* FIN segments are not keepalives */
+                       struct tcp_acked *ta;
+       
+                       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+                       ta->flags|=TCP_A_KEEP_ALIVE;
+                       ual1->flags|=TCP_A_KEEP_ALIVE;
+                       goto seq_finished;
+               }
        }
 
 
@@ -647,6 +693,7 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
        ual->ts.secs=pinfo->fd->abs_secs;
        ual->ts.nsecs=pinfo->fd->abs_usecs*1000;
        ual->window=window;
+       ual->flags=0;
        ual1=ual;
 
 seq_finished:
@@ -765,6 +812,7 @@ ack_finished:
                ual2->ts.secs=0;
                ual2->ts.nsecs=0;
                ual2->window=window;
+               ual2->flags=0;
        }
 
        /* update the ACK counter and check for
@@ -798,13 +846,23 @@ ack_finished:
                                ual->num_acks++;
                        }       
                        
+                       /* is this an ACK to a KeepAlive? */
+                       if( (ual->flags&TCP_A_KEEP_ALIVE)
+                       && (ack==ual->seq) ){
+                               struct tcp_acked *ta;
+                               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+                               ta->flags|=TCP_A_KEEP_ALIVE_ACK;
+                               ual->flags^=TCP_A_KEEP_ALIVE;
+                       } else if(ual->num_acks>1) {
                        /* ok we have found a potential duplicate ack */
-                       if(ual->num_acks>1){
                                struct tcp_acked *ta;
                                ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                               ta->flags|=TCP_A_DUPLICATE_ACK;
-                               ta->dupack_num=ual->num_acks-1;
-                               ta->dupack_frame=ual->ack_frame;
+                               /* keepalives are not dupacks */
+                               if( (!(ta->flags&TCP_A_KEEP_ALIVE)) ){
+                                       ta->flags|=TCP_A_DUPLICATE_ACK;
+                                       ta->dupack_num=ual->num_acks-1;
+                                       ta->dupack_frame=ual->ack_frame;
+                               }
                        }
                }               
 
@@ -874,6 +932,7 @@ ack_finished:
                trs=g_mem_chunk_alloc(tcp_rel_seq_chunk);
                trs->seq_base=base_seq;
                trs->ack_base=base_ack;
+               trs->win_scale=win_scale;
                g_hash_table_insert(tcp_rel_seq_table, (void *)pinfo->fd->num, trs);
        }
 }
@@ -934,6 +993,12 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                                col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
                        }
                }
+               if( ta->flags&TCP_A_KEEP_ALIVE_ACK ){
+                       proto_tree_add_none_format(flags_tree, hf_tcp_analysis_keep_alive_ack, tvb, 0, 0, "This is an ACK to a TCP keep-alive segment");
+                       if(check_col(pinfo->cinfo, COL_INFO)){
+                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive ACK] ");
+                       }
+               }
                if( ta->dupack_num){
                        if( ta->flags&TCP_A_DUPLICATE_ACK ){
                                proto_tree_add_none_format(flags_tree, hf_tcp_analysis_duplicate_ack, tvb, 0, 0, "This is a TCP duplicate ack");
@@ -1012,7 +1077,7 @@ tcp_analyze_seq_init(void)
                tcp_rel_seq_table = NULL;
        }
        if( tcp_pdu_tracking_table ){
-               g_hash_table_foreach_remove(tcp_rel_seq_table,
+               g_hash_table_foreach_remove(tcp_pdu_tracking_table,
                        free_all_acked, NULL);
                g_hash_table_destroy(tcp_pdu_tracking_table);
                tcp_pdu_tracking_table = NULL;
@@ -1566,6 +1631,15 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
        }
 
        if (!called_dissector || pinfo->desegment_len != 0) {
+               if (ipfd_head != NULL && ipfd_head->reassembled_in != 0) {
+                       /*
+                        * We know what frame this PDU is reassembled in;
+                        * let the user know.
+                        */
+                       proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in,
+                           tvb, 0, 0, ipfd_head->reassembled_in);
+               }
+
                /*
                 * Either we didn't call the subdissector at all (i.e.,
                 * this is a segment that contains the middle of a
@@ -1791,6 +1865,9 @@ dissect_tcpopt_wscale(const ip_tcp_opt *optp, tvbuff_t *tvb,
                             offset, optlen, ws, "%s: %u (multiply by %u)", 
                             optp->name, ws, 1 << ws);
   tcp_info_append_uint(pinfo, "WS", ws);
+  if(!pinfo->fd->flags.visited && tcp_analyze_seq && tcp_relative_seq){
+    pdu_store_window_scale_option(pinfo, ws);
+  }
 }
 
 static void
@@ -1995,6 +2072,8 @@ static const ip_tcp_opt tcpopts[] = {
 /* separated into a stand alone routine to other protocol dissectors */
 /* can call to it, ie. socks   */
 
+static gboolean try_heuristic_first = FALSE;
+
 void
 decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
        proto_tree *tree, int src_port, int dst_port, guint32 nxtseq)
@@ -2009,7 +2088,6 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
     offset=scan_for_next_pdu(pinfo, offset, seq, nxtseq);
   }
 
-
   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
 
 /* determine if this packet is part of a conversation and call dissector */
@@ -2019,6 +2097,12 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
                src_port, dst_port, next_tvb, pinfo, tree))
     goto end_decode_tcp_ports;
 
+  if (try_heuristic_first) {
+    /* do lookup with the heuristic subdissector table */
+    if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
+       goto end_decode_tcp_ports;
+  }
+
   /* Do lookups with the subdissector table.
      We try the port number with the lower value first, followed by the
      port number with the higher value.  This means that, for packets
@@ -2044,15 +2128,15 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
   if (low_port != 0 &&
       dissector_try_port(subdissector_table, low_port, next_tvb, pinfo, tree))
     goto end_decode_tcp_ports;
-
   if (high_port != 0 &&
       dissector_try_port(subdissector_table, high_port, next_tvb, pinfo, tree))
     goto end_decode_tcp_ports;
 
-  /* do lookup with the heuristic subdissector table */
-  if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
-    goto end_decode_tcp_ports;
-
+  if (!try_heuristic_first) {
+    /* do lookup with the heuristic subdissector table */
+    if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
+       goto end_decode_tcp_ports;
+  }
 
   /* Oh, well, we don't know this; dissect it as data. */
   call_dissector(data_handle,next_tvb, pinfo, tree);
@@ -2099,8 +2183,8 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      tcph_count=0;
   }
   tcph=&tcphstruct[tcph_count];
-  /* XXX add to ipv6 so this works for that protocol as well */
-  tcph->ip_header=pinfo->private_data;
+  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);
 
   if (check_col(pinfo->cinfo, COL_PROTOCOL))
     col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCP");
@@ -2188,7 +2272,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
               tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win);
           }
           if(tcp_relative_seq){
-              tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack));
+              tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack), &(tcph->th_win));
           }
       }
 
@@ -2540,8 +2624,9 @@ proto_register_tcp(void)
                { "Fin",                        "tcp.flags.fin", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_FIN,
                        "", HFILL }},
 
+               /* 32 bits so we can present some values adjusted to window scaling */
                { &hf_tcp_window_size,
-               { "Window size",                "tcp.window_size", FT_UINT16, BASE_DEC, NULL, 0x0,
+               { "Window size",                "tcp.window_size", FT_UINT32, BASE_DEC, NULL, 0x0,
                        "", HFILL }},
 
                { &hf_tcp_checksum,
@@ -2572,6 +2657,10 @@ proto_register_tcp(void)
                { "Keep Alive",         "tcp.analysis.keep_alive", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This is a keep-alive segment", HFILL }},
 
+               { &hf_tcp_analysis_keep_alive_ack,
+               { "Keep Alive ACK",             "tcp.analysis.keep_alive_ack", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "This is an ACK to a keep-alive segment", HFILL }},
+
                { &hf_tcp_analysis_duplicate_ack,
                { "Duplicate ACK",              "tcp.analysis.duplicate_ack", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This is a duplicate ACK", HFILL }},
@@ -2639,53 +2728,72 @@ proto_register_tcp(void)
                { &hf_tcp_segments,
                { "TCP Segments", "tcp.segments", FT_NONE, BASE_NONE, NULL, 0x0,
                        "TCP Segments", HFILL }},
+
+               { &hf_tcp_reassembled_in,
+               { "Reassembled PDU in frame", "tcp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                       "The PDU that starts but doesn't end in this segment is reassembled in this frame", HFILL }},
+
                { &hf_tcp_option_mss,
                  { "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN, 
                    BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
+
                { &hf_tcp_option_mss_val,
                  { "TCP MSS Option Value", "tcp.options.mss_val", FT_UINT16,
                    BASE_DEC, NULL, 0x0, "TCP MSS Option Value", HFILL}},
+
                { &hf_tcp_option_wscale,
                  { "TCP Window Scale Option", "tcp.options.wscale", 
                    FT_BOOLEAN, 
                    BASE_NONE, NULL, 0x0, "TCP Window Option", HFILL}},
+
                { &hf_tcp_option_wscale_val,
                  { "TCP Windows Scale Option Value", "tcp.options.wscale_val",
                    FT_UINT8, BASE_DEC, NULL, 0x0, "TCP Window Scale Value",
                    HFILL}},
+
                { &hf_tcp_option_sack_perm, 
                  { "TCP Sack Perm Option", "tcp.options.sack_perm", 
                    FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Sack Perm Option", HFILL}},
+
                { &hf_tcp_option_sack,
                  { "TCP Sack Option", "tcp.options.sack", FT_BOOLEAN, 
                    BASE_NONE, NULL, 0x0, "TCP Sack Option", HFILL}},
+
                { &hf_tcp_option_sack_sle,
                  {"TCP Sack Left Edge", "tcp.options.sack_le", FT_UINT32,
                   BASE_DEC, NULL, 0x0, "TCP Sack Left Edge", HFILL}},
+
                { &hf_tcp_option_sack_sre,
                  {"TCP Sack Right Edge", "tcp.options.sack_re", FT_UINT32,
                   BASE_DEC, NULL, 0x0, "TCP Sack Right Edge", HFILL}},
+
                { &hf_tcp_option_echo,
                  { "TCP Echo Option", "tcp.options.echo", FT_BOOLEAN, 
                    BASE_NONE, NULL, 0x0, "TCP Sack Echo", HFILL}},
+
                { &hf_tcp_option_echo_reply,
                  { "TCP Echo Reply Option", "tcp.options.echo_reply", 
                    FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Echo Reply Option", HFILL}},
+
                { &hf_tcp_option_time_stamp,
                  { "TCP Time Stamp Option", "tcp.options.time_stamp", 
                    FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Time Stamp Option", HFILL}},
+
                { &hf_tcp_option_cc,
                  { "TCP CC Option", "tcp.options.cc", FT_BOOLEAN, BASE_NONE,
                    NULL, 0x0, "TCP CC Option", HFILL}},
+
                { &hf_tcp_option_ccnew,
                  { "TCP CC New Option", "tcp.options.ccnew", FT_BOOLEAN, 
                    BASE_NONE, NULL, 0x0, "TCP CC New Option", HFILL}},
+
                { &hf_tcp_option_ccecho,
                  { "TCP CC Echo Option", "tcp.options.ccecho", FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP CC Echo Option", HFILL}},
+
                { &hf_tcp_option_md5,
                  { "TCP MD5 Option", "tcp.options.md5", FT_BOOLEAN, BASE_NONE,
                    NULL, 0x0, "TCP MD5 Option", HFILL}},
@@ -2716,24 +2824,30 @@ proto_register_tcp(void)
        tcp_module = prefs_register_protocol(proto_tcp, NULL);
        prefs_register_bool_preference(tcp_module, "summary_in_tree",
            "Show TCP summary in protocol tree",
-"Whether the TCP summary line should be shown in the protocol tree",
+           "Whether the TCP summary line should be shown in the protocol tree",
            &tcp_summary_in_tree);
        prefs_register_bool_preference(tcp_module, "check_checksum",
            "Check the validity of the TCP checksum when possible",
-"Whether to check the validity of the TCP checksum",
+           "Whether to check the validity of the TCP checksum",
            &tcp_check_checksum);
        prefs_register_bool_preference(tcp_module, "desegment_tcp_streams",
            "Allow subdissector to desegment TCP streams",
-"Whether subdissector can request TCP streams to be desegmented",
+           "Whether subdissector can request TCP streams to be desegmented",
            &tcp_desegment);
        prefs_register_bool_preference(tcp_module, "analyze_sequence_numbers",
            "Analyze TCP sequence numbers",
            "Make the TCP dissector analyze TCP sequence numbers to find and flag segment retransmissions, missing segments and RTT",
            &tcp_analyze_seq);
        prefs_register_bool_preference(tcp_module, "relative_sequence_numbers",
-           "Use relative sequence numbers",
-           "Make the TCP dissector use relative sequence numbers instead of absolute ones. To use this option you must also enable \"Analyze TCP sequence numbers\".",
+           "Relative sequence numbers and window scaling",
+           "Make the TCP dissector use relative sequence numbers instead of absolute ones. "
+           "To use this option you must also enable \"Analyze TCP sequence numbers\". "
+           "This option will also try to track and adjust the window field according to any TCP window scaling options seen.",
            &tcp_relative_seq);
+       prefs_register_bool_preference(tcp_module, "try_heuristic_first",
+           "Try heuristic sub-dissectors first",
+           "Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port",
+           &try_heuristic_first);
 
        register_init_routine(tcp_analyze_seq_init);
        register_init_routine(tcp_desegment_init);