fix doxygen generation
[obnox/wireshark/wip.git] / follow.c
1 /* follow.c
2  *
3  * $Id$
4  *
5  * Copyright 1998 Mike Hall <mlh@io.com>
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
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
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <glib.h>
39 #include <epan/packet.h>
40 #include "follow.h"
41
42 #define MAX_IPADDR_LEN  16
43
44 typedef struct _tcp_frag {
45   gulong              seq;
46   gulong              len;
47   gulong              data_len;
48   gchar              *data;
49   struct _tcp_frag   *next;
50 } tcp_frag;
51
52 FILE* data_out_file = NULL;
53
54 gboolean incomplete_tcp_stream = FALSE;
55
56 static guint8  ip_address[2][MAX_IPADDR_LEN];
57 static guint   tcp_port[2];
58 static guint   bytes_written[2];
59 static gboolean is_ipv6 = FALSE;
60
61 static int check_fragments( int, tcp_stream_chunk * );
62 static void write_packet_data( int, tcp_stream_chunk *, const char * );
63
64 void
65 follow_tcp_stats(follow_tcp_stats_t* stats)
66 {
67         int i;
68
69         for (i = 0; i < 2 ; i++) {
70                 memcpy(stats->ip_address[i], ip_address[i], MAX_IPADDR_LEN);
71                 stats->tcp_port[i] = tcp_port[i];
72                 stats->bytes_written[i] = bytes_written[i];
73                 stats->is_ipv6 = is_ipv6;
74         }
75 }
76
77 /* this will build libpcap filter text that will only
78    pass the packets related to the stream. There is a
79    chance that two streams could intersect, but not a
80    very good one */
81 char*
82 build_follow_filter( packet_info *pi ) {
83   char* buf = g_malloc(1024);
84   int len;
85   if( pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4
86         && pi->ipproto == 6 ) {
87     /* TCP over IPv4 */
88     sprintf( buf,
89              "(ip.addr eq %s and ip.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
90              ip_to_str( pi->net_src.data),
91              ip_to_str( pi->net_dst.data),
92              pi->srcport, pi->destport );
93     len = 4;
94     is_ipv6 = FALSE;
95   }
96   else if( pi->net_src.type == AT_IPv6 && pi->net_dst.type == AT_IPv6
97         && pi->ipproto == 6 ) {
98     /* TCP over IPv6 */
99     sprintf( buf,
100              "(ipv6.addr eq %s and ipv6.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
101              ip6_to_str((const struct e_in6_addr *)pi->net_src.data),
102              ip6_to_str((const struct e_in6_addr *)pi->net_dst.data),
103              pi->srcport, pi->destport );
104     len = 16;
105     is_ipv6 = TRUE;
106   }
107   else {
108     g_free( buf );
109     return NULL;
110   }
111   memcpy(ip_address[0], pi->net_src.data, len);
112   memcpy(ip_address[1], pi->net_dst.data, len);
113   tcp_port[0] = pi->srcport;
114   tcp_port[1] = pi->destport;
115   return buf;
116 }
117
118 /* here we are going to try and reconstruct the data portion of a TCP
119    session. We will try and handle duplicates, TCP fragments, and out
120    of order packets in a smart way. */
121
122 static tcp_frag *frags[2] = { 0, 0 };
123 static gulong seq[2];
124 static guint8 src_addr[2][MAX_IPADDR_LEN];
125 static guint src_port[2] = { 0, 0 };
126
127 void
128 reassemble_tcp( gulong sequence, gulong length, const char* data,
129                 gulong data_length, int synflag, address *net_src,
130                 address *net_dst, guint srcport, guint dstport) {
131   guint8 srcx[MAX_IPADDR_LEN], dstx[MAX_IPADDR_LEN];
132   int src_index, j, first = 0, len;
133   gulong newseq;
134   tcp_frag *tmp_frag;
135   tcp_stream_chunk sc;
136
137   src_index = -1;
138
139   /* First, check if this packet should be processed. */
140
141   if ((net_src->type != AT_IPv4 && net_src->type != AT_IPv6) ||
142       (net_dst->type != AT_IPv4 && net_dst->type != AT_IPv6))
143     return;
144
145   if (net_src->type == AT_IPv4)
146     len = 4;
147   else
148     len = 16;
149
150   /* Now check if the packet is for this connection. */
151   memcpy(srcx, net_src->data, len);
152   memcpy(dstx, net_dst->data, len);
153   if (
154       ! (
155          memcmp(srcx, ip_address[0], len) == 0 &&
156          memcmp(dstx, ip_address[1], len) == 0 &&
157          srcport == tcp_port[0] &&
158          dstport == tcp_port[1]
159         ) &&
160       ! (
161          memcmp(srcx, ip_address[1], len) == 0 &&
162          memcmp(dstx, ip_address[0], len) == 0 &&
163          srcport == tcp_port[1] &&
164          dstport == tcp_port[0]
165         )
166      )
167     return;
168
169   /* Initialize our stream chunk.  This data gets written to disk. */
170   memcpy(sc.src_addr, srcx, len);
171   sc.src_port = srcport;
172   sc.dlen     = data_length;
173
174   /* Check to see if we have seen this source IP and port before.
175      (Yes, we have to check both source IP and port; the connection
176      might be between two different ports on the same machine.) */
177   for( j=0; j<2; j++ ) {
178     if (memcmp(src_addr[j], srcx, len) == 0 && src_port[j] == srcport ) {
179       src_index = j;
180     }
181   }
182   /* we didn't find it if src_index == -1 */
183   if( src_index < 0 ) {
184     /* assign it to a src_index and get going */
185     for( j=0; j<2; j++ ) {
186       if( src_port[j] == 0 ) {
187         memcpy(src_addr[j], srcx, len);
188         src_port[j] = srcport;
189         src_index = j;
190         first = 1;
191         break;
192       }
193     }
194   }
195   if( src_index < 0 ) {
196     fprintf( stderr, "ERROR in reassemble_tcp: Too many addresses!\n");
197     return;
198   }
199
200   if( data_length < length ) {
201     incomplete_tcp_stream = TRUE;
202   }
203
204   /* now that we have filed away the srcs, lets get the sequence number stuff
205      figured out */
206   if( first ) {
207     /* this is the first time we have seen this src's sequence number */
208     seq[src_index] = sequence + length;
209     if( synflag ) {
210       seq[src_index]++;
211     }
212     /* write out the packet data */
213     write_packet_data( src_index, &sc, data );
214     return;
215   }
216   /* if we are here, we have already seen this src, let's
217      try and figure out if this packet is in the right place */
218   if( sequence < seq[src_index] ) {
219     /* this sequence number seems dated, but
220        check the end to make sure it has no more
221        info than we have already seen */
222     newseq = sequence + length;
223     if( newseq > seq[src_index] ) {
224       gulong new_len;
225
226       /* this one has more than we have seen. let's get the
227          payload that we have not seen. */
228
229       new_len = seq[src_index] - sequence;
230
231       if ( data_length <= new_len ) {
232         data = NULL;
233         data_length = 0;
234         incomplete_tcp_stream = TRUE;
235       } else {
236         data += new_len;
237         data_length -= new_len;
238       }
239       sc.dlen = data_length;
240       sequence = seq[src_index];
241       length = newseq - seq[src_index];
242
243       /* this will now appear to be right on time :) */
244     }
245   }
246   if ( sequence == seq[src_index] ) {
247     /* right on time */
248     seq[src_index] += length;
249     if( synflag ) seq[src_index]++;
250     if( data ) {
251       write_packet_data( src_index, &sc, data );
252     }
253     /* done with the packet, see if it caused a fragment to fit */
254     while( check_fragments( src_index, &sc ) )
255       ;
256   }
257   else {
258     /* out of order packet */
259     if(data_length > 0 && sequence > seq[src_index] ) {
260       tmp_frag = (tcp_frag *)malloc( sizeof( tcp_frag ) );
261       tmp_frag->data = (guchar *)malloc( data_length );
262       tmp_frag->seq = sequence;
263       tmp_frag->len = length;
264       tmp_frag->data_len = data_length;
265       memcpy( tmp_frag->data, data, data_length );
266       if( frags[src_index] ) {
267         tmp_frag->next = frags[src_index];
268       } else {
269         tmp_frag->next = NULL;
270       }
271       frags[src_index] = tmp_frag;
272     }
273   }
274 } /* end reassemble_tcp */
275
276 /* here we search through all the frag we have collected to see if
277    one fits */
278 static int
279 check_fragments( int index, tcp_stream_chunk *sc ) {
280   tcp_frag *prev = NULL;
281   tcp_frag *current;
282   current = frags[index];
283   while( current ) {
284     if( current->seq == seq[index] ) {
285       /* this fragment fits the stream */
286       if( current->data ) {
287         sc->dlen = current->data_len;
288         write_packet_data( index, sc, current->data );
289       }
290       seq[index] += current->len;
291       if( prev ) {
292         prev->next = current->next;
293       } else {
294         frags[index] = current->next;
295       }
296       free( current->data );
297       free( current );
298       return 1;
299     }
300     prev = current;
301     current = current->next;
302   }
303   return 0;
304 }
305
306 /* this should always be called before we start to reassemble a stream */
307 void
308 reset_tcp_reassembly() {
309   tcp_frag *current, *next;
310   int i;
311   incomplete_tcp_stream = FALSE;
312   for( i=0; i<2; i++ ) {
313     seq[i] = 0;
314     memset(src_addr[i], '\0', MAX_IPADDR_LEN);
315     src_port[i] = 0;
316     memset(ip_address[i], '\0', MAX_IPADDR_LEN);
317     tcp_port[i] = 0;
318     bytes_written[i] = 0;
319     current = frags[i];
320     while( current ) {
321       next = current->next;
322       free( current->data );
323       free( current );
324       current = next;
325     }
326     frags[i] = NULL;
327   }
328 }
329
330 static void
331 write_packet_data( int index, tcp_stream_chunk *sc, const char *data )
332 {
333   fwrite( sc, 1, sizeof(tcp_stream_chunk), data_out_file );
334   fwrite( data, 1, sc->dlen, data_out_file );
335   bytes_written[index] += sc->dlen;
336 }