I removed the bit-fields that depended upon gcc's ability to use any type
[obnox/wireshark/wip.git] / packet-tcp.c
1 /* packet-tcp.c
2  * Routines for TCP packet disassembly
3  *
4  * $Id: packet-tcp.c,v 1.14 1999/02/08 20:02:34 gram Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31
32 #include <stdio.h>
33
34 #ifdef NEED_SNPRINTF_H
35 # ifdef HAVE_STDARG_H
36 #  include <stdarg.h>
37 # else
38 #  include <varargs.h>
39 # endif
40 # include "snprintf.h"
41 #endif
42
43 #ifdef HAVE_SYS_TYPES_H
44 # include <sys/types.h>
45 #endif
46
47 #ifdef HAVE_NETINET_IN_H
48 # include <netinet/in.h>
49 #endif
50
51 #include "ethereal.h"
52 #include "packet.h"
53 #include "resolv.h"
54 #include "follow.h"
55 #include "util.h"
56
57 extern FILE* data_out_file;
58 extern packet_info pi;
59
60 static gchar info_str[COL_MAX_LEN];
61 static int   info_len;
62
63 static void
64 tcp_info_append_uint(const char *abbrev, guint32 val) {
65   int add_len = 0;
66   
67   if (info_len > 0)
68   if(info_len > 0)
69     add_len = snprintf(&info_str[info_len], COL_MAX_LEN - info_len, " %s=%u",
70       abbrev, val);
71   if (add_len > 0)
72     info_len += add_len;
73 }
74
75 static void
76 dissect_tcpopt_maxseg(GtkWidget *opt_tree, const char *name, const u_char *opd,
77     int offset, guint optlen)
78 {
79   add_item_to_tree(opt_tree, offset,      optlen,
80     "%s: %u bytes", name, pntohs(opd));
81   tcp_info_append_uint("MSS", pntohs(opd));
82 }
83
84 static void
85 dissect_tcpopt_wscale(GtkWidget *opt_tree, const char *name, const u_char *opd,
86     int offset, guint optlen)
87 {
88   add_item_to_tree(opt_tree, offset,      optlen,
89     "%s: %u bytes", name, *opd);
90   tcp_info_append_uint("WS", *opd);
91 }
92
93 static void
94 dissect_tcpopt_sack(GtkWidget *opt_tree, const char *name, const u_char *opd,
95     int offset, guint optlen)
96 {
97   GtkWidget *field_tree = NULL, *tf;
98   guint leftedge, rightedge;
99
100   tf = add_item_to_tree(opt_tree, offset,      optlen, "%s:", name);
101   offset += 2;  /* skip past type and length */
102   optlen -= 2;  /* subtract size of type and length */
103   while (optlen > 0) {
104     if (field_tree == NULL) {
105       /* Haven't yet made a subtree out of this option.  Do so. */
106       field_tree = gtk_tree_new();
107       add_subtree(tf, field_tree, ETT_TCP_OPTION_SACK);
108     }
109     if (optlen < 4) {
110       add_item_to_tree(field_tree, offset,      optlen,
111         "(suboption would go past end of option)");
112       break;
113     }
114     /* XXX - check whether it goes past end of packet */
115     leftedge = pntohl(opd);
116     opd += 4;
117     optlen -= 4;
118     if (optlen < 4) {
119       add_item_to_tree(field_tree, offset,      optlen,
120         "(suboption would go past end of option)");
121       break;
122     }
123     /* XXX - check whether it goes past end of packet */
124     rightedge = pntohl(opd);
125     opd += 4;
126     optlen -= 4;
127     add_item_to_tree(field_tree, offset,      8,
128         "left edge = %u, right edge = %u", leftedge, rightedge);
129     tcp_info_append_uint("SLE", leftedge);
130     tcp_info_append_uint("SRE", rightedge);
131     offset += 8;
132   }
133 }
134
135 static void
136 dissect_tcpopt_echo(GtkWidget *opt_tree, const char *name, const u_char *opd,
137     int offset, guint optlen)
138 {
139   add_item_to_tree(opt_tree, offset,      optlen,
140     "%s: %u", name, pntohl(opd));
141   tcp_info_append_uint("ECHO", pntohl(opd));
142 }
143
144 static void
145 dissect_tcpopt_timestamp(GtkWidget *opt_tree, const char *name,
146     const u_char *opd, int offset, guint optlen)
147 {
148   add_item_to_tree(opt_tree, offset,      optlen,
149     "%s: tsval %u, tsecr %u", name, pntohl(opd), pntohl(opd + 4));
150   tcp_info_append_uint("TSV", pntohl(opd));
151   tcp_info_append_uint("TSER", pntohl(opd + 4));
152 }
153
154 static void
155 dissect_tcpopt_cc(GtkWidget *opt_tree, const char *name, const u_char *opd,
156     int offset, guint optlen)
157 {
158   add_item_to_tree(opt_tree, offset,      optlen,
159     "%s: %u", name, pntohl(opd));
160   tcp_info_append_uint("CC", pntohl(opd));
161 }
162
163 static ip_tcp_opt tcpopts[] = {
164   {
165     TCPOPT_EOL,
166     "EOL",
167     NO_LENGTH,
168     0,
169     NULL,
170   },
171   {
172     TCPOPT_NOP,
173     "NOP",
174     NO_LENGTH,
175     0,
176     NULL,
177   },
178   {
179     TCPOPT_MSS,
180     "Maximum segment size",
181     FIXED_LENGTH,
182     TCPOLEN_MSS,
183     dissect_tcpopt_maxseg
184   },
185   {
186     TCPOPT_WINDOW,
187     "Window scale",
188     FIXED_LENGTH,
189     TCPOLEN_WINDOW,
190     dissect_tcpopt_wscale
191   },
192   {
193     TCPOPT_SACK_PERM,
194     "SACK permitted",
195     FIXED_LENGTH,
196     TCPOLEN_SACK_PERM,
197     NULL,
198   },
199   {
200     TCPOPT_SACK,
201     "SACK",
202     VARIABLE_LENGTH,
203     TCPOLEN_SACK_MIN,
204     dissect_tcpopt_sack
205   },
206   {
207     TCPOPT_ECHO,
208     "Echo",
209     FIXED_LENGTH,
210     TCPOLEN_ECHO,
211     dissect_tcpopt_echo
212   },
213   {
214     TCPOPT_ECHOREPLY,
215     "Echo reply",
216     FIXED_LENGTH,
217     TCPOLEN_ECHOREPLY,
218     dissect_tcpopt_echo
219   },
220   {
221     TCPOPT_TIMESTAMP,
222     "Time stamp",
223     FIXED_LENGTH,
224     TCPOLEN_TIMESTAMP,
225     dissect_tcpopt_timestamp
226   },
227   {
228     TCPOPT_CC,
229     "CC",
230     FIXED_LENGTH,
231     TCPOLEN_CC,
232     dissect_tcpopt_cc
233   },
234   {
235     TCPOPT_CCNEW,
236     "CC.NEW",
237     FIXED_LENGTH,
238     TCPOPT_CCNEW,
239     dissect_tcpopt_cc
240   },
241   {
242     TCPOPT_CCECHO,
243     "CC.ECHO",
244     FIXED_LENGTH,
245     TCPOLEN_CCECHO,
246     dissect_tcpopt_cc
247   }
248 };
249
250 #define N_TCP_OPTS      (sizeof tcpopts / sizeof tcpopts[0])
251
252 void
253 dissect_tcp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
254   e_tcphdr   th;
255   GtkWidget *tcp_tree = NULL, *ti, *field_tree = NULL, *tf;
256   gchar      flags[64] = "<None>";
257   gchar     *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG"};
258   gint       fpos = 0, i;
259   guint      bpos;
260   guint      hlen;
261   guint      optlen;
262
263   /* To do: Check for {cap len,pkt len} < struct len */
264   /* Avoids alignment problems on many architectures. */
265   memcpy(&th, &pd[offset], sizeof(e_tcphdr));
266   th.th_sport = ntohs(th.th_sport);
267   th.th_dport = ntohs(th.th_dport);
268   th.th_win   = ntohs(th.th_win);
269   th.th_sum   = ntohs(th.th_sum);
270   th.th_urp   = ntohs(th.th_urp);
271   th.th_seq   = ntohl(th.th_seq);
272   th.th_ack   = ntohl(th.th_ack);
273   
274   info_len = 0;
275
276   if (check_col(fd, COL_PROTOCOL) || tree) {  
277     for (i = 0; i < 6; i++) {
278       bpos = 1 << i;
279       if (th.th_flags & bpos) {
280         if (fpos) {
281           strcpy(&flags[fpos], ", ");
282           fpos += 2;
283         }
284         strcpy(&flags[fpos], fstr[i]);
285         fpos += 3;
286       }
287     }
288     flags[fpos] = '\0';
289   }
290   
291   hlen = hi_nibble(th.th_off_x2) * 4;  /* TCP header length, in bytes */
292
293   if (check_col(fd, COL_RES_SRC_PORT))
294     col_add_str(fd, COL_RES_SRC_PORT, get_tcp_port(th.th_sport));
295   if (check_col(fd, COL_UNRES_SRC_PORT))
296     col_add_fstr(fd, COL_UNRES_SRC_PORT, "%u", th.th_sport);
297   if (check_col(fd, COL_RES_DST_PORT))
298     col_add_str(fd, COL_RES_DST_PORT, get_tcp_port(th.th_dport));
299   if (check_col(fd, COL_UNRES_DST_PORT))
300     col_add_fstr(fd, COL_UNRES_DST_PORT, "%u", th.th_dport);
301   if (check_col(fd, COL_PROTOCOL))
302     col_add_str(fd, COL_PROTOCOL, "TCP");
303   if (check_col(fd, COL_INFO)) {
304     /* Copy the data into info_str in case one of the option handling
305        routines needs to append to it. */
306     if (th.th_flags & TH_URG)
307       info_len = snprintf(info_str, COL_MAX_LEN, "%s > %s [%s] Seq=%u Ack=%u Win=%u Urg=%u",
308         get_tcp_port(th.th_sport), get_tcp_port(th.th_dport), flags,
309         th.th_seq, th.th_ack, th.th_win, th.th_urp);
310     else
311       info_len = snprintf(info_str, COL_MAX_LEN, "%s > %s [%s] Seq=%u Ack=%u Win=%u",
312         get_tcp_port(th.th_sport), get_tcp_port(th.th_dport), flags,
313         th.th_seq, th.th_ack, th.th_win);
314     /* The info column is actually written after the options are decoded */
315   }
316   
317   if (tree) {
318     ti = add_item_to_tree(GTK_WIDGET(tree), offset, hlen,
319       "Transmission Control Protocol");
320     tcp_tree = gtk_tree_new();
321     add_subtree(ti, tcp_tree, ETT_TCP);
322     add_item_to_tree(tcp_tree, offset,      2, "Source port: %s (%u)",
323       get_tcp_port(th.th_sport), th.th_sport);
324     add_item_to_tree(tcp_tree, offset +  2, 2, "Destination port: %s (%u)",
325       get_tcp_port(th.th_dport), th.th_dport);
326     add_item_to_tree(tcp_tree, offset +  4, 4, "Sequence number: %u",
327       th.th_seq);
328     if (th.th_flags & TH_ACK)
329       add_item_to_tree(tcp_tree, offset +  8, 4, "Acknowledgement number: %u",
330         th.th_ack);
331     add_item_to_tree(tcp_tree, offset + 12, 1, "Header length: %u bytes", hlen);
332      tf = add_item_to_tree(tcp_tree, offset + 13, 1, "Flags: 0x%x", th.th_flags);
333      field_tree = gtk_tree_new();
334      add_subtree(tf, field_tree, ETT_TCP_FLAGS);
335      add_item_to_tree(field_tree, offset + 13, 1, "%s",
336        decode_boolean_bitfield(th.th_flags, TH_URG, sizeof (th.th_flags)*8,
337                          "Urgent pointer", "No urgent pointer"));
338      add_item_to_tree(field_tree, offset + 13, 1, "%s",
339        decode_boolean_bitfield(th.th_flags, TH_ACK, sizeof (th.th_flags)*8,
340                          "Acknowledgment", "No acknowledgment"));
341      add_item_to_tree(field_tree, offset + 13, 1, "%s",
342        decode_boolean_bitfield(th.th_flags, TH_PUSH, sizeof (th.th_flags)*8,
343                          "Push", "No push"));
344      add_item_to_tree(field_tree, offset + 13, 1, "%s",
345        decode_boolean_bitfield(th.th_flags, TH_RST, sizeof (th.th_flags)*8,
346                          "Reset", "No reset"));
347      add_item_to_tree(field_tree, offset + 13, 1, "%s",
348        decode_boolean_bitfield(th.th_flags, TH_SYN, sizeof (th.th_flags)*8,
349                          "Syn", "No Syn"));
350      add_item_to_tree(field_tree, offset + 13, 1, "%s",
351        decode_boolean_bitfield(th.th_flags, TH_FIN, sizeof (th.th_flags)*8,
352                          "Fin", "No Fin"));
353     add_item_to_tree(tcp_tree, offset + 14, 2, "Window size: %u", th.th_win);
354     add_item_to_tree(tcp_tree, offset + 16, 2, "Checksum: 0x%04x", th.th_sum);
355     if (th.th_flags & TH_URG)
356       add_item_to_tree(tcp_tree, offset + 18, 2, "Urgent pointer: 0x%04x",
357         th.th_urp);
358   }
359
360   /* Decode TCP options, if any. */
361   if (hlen > sizeof (e_tcphdr)) {
362     /* There's more than just the fixed-length header.  Decode the
363        options. */
364     optlen = hlen - sizeof (e_tcphdr); /* length of options, in bytes */
365     if (tree) {
366       tf = add_item_to_tree(tcp_tree, offset +  20, optlen,
367         "Options: (%d bytes)", optlen);
368       field_tree = gtk_tree_new();
369       add_subtree(tf, field_tree, ETT_TCP_OPTIONS);
370     }
371     dissect_ip_tcp_options(field_tree, &pd[offset + 20], offset + 20, optlen,
372        tcpopts, N_TCP_OPTS, TCPOPT_EOL);
373   }
374
375   if (check_col(fd, COL_INFO))
376     col_add_str(fd, COL_INFO, info_str);
377
378   /* Skip over header + options */
379   offset += hlen;
380
381   /* until we decode those options, I'll check the packet length
382   to see if there's more data. -- gilbert */
383   if (fd->cap_len > offset) {
384     switch(MIN(th.th_sport, th.th_dport)) {
385       case TCP_PORT_PRINTER:
386         dissect_lpd(pd, offset, fd, tree);
387         break;
388       default:
389         /* check existence of high level protocols */
390
391         if (memcmp(&pd[offset], "GIOP",  4) == 0) {
392           dissect_giop(pd, offset, fd, tree);
393         }
394         else {
395           dissect_data(pd, offset, fd, tree);
396         }
397     }
398   }
399  
400   pi.srcport = th.th_sport;
401   pi.destport = th.th_dport;
402   
403   if( data_out_file ) {
404     reassemble_tcp( th.th_seq, /* sequence number */
405         ( pi.iplen -( pi.iphdrlen * 4 )-( hi_nibble(th.th_off_x2) * 4 ) ), /* length */
406         ( pd+offset ), /* data */
407         ( th.th_flags & 0x02 ), /* is syn set? */
408         pi.ip_src ); /* src ip */
409   }
410 }