change the signature that asn2wrs generates for functions to marm all parameters...
[obnox/wireshark/wip.git] / epan / follow.c
1 /* follow.c
2  *
3  * $Id$
4  *
5  * Copyright 1998 Mike Hall <mlh@io.com>
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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;
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     buf = g_strdup_printf(
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     buf = g_strdup_printf(
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     return NULL;
109   }
110   memcpy(ip_address[0], pi->net_src.data, len);
111   memcpy(ip_address[1], pi->net_dst.data, len);
112   tcp_port[0] = pi->srcport;
113   tcp_port[1] = pi->destport;
114   return buf;
115 }
116
117 /* here we are going to try and reconstruct the data portion of a TCP
118    session. We will try and handle duplicates, TCP fragments, and out
119    of order packets in a smart way. */
120
121 static tcp_frag *frags[2] = { 0, 0 };
122 static gulong seq[2];
123 static guint8 src_addr[2][MAX_IPADDR_LEN];
124 static guint src_port[2] = { 0, 0 };
125
126 void
127 reassemble_tcp( gulong sequence, gulong length, const char* data,
128                 gulong data_length, int synflag, address *net_src,
129                 address *net_dst, guint srcport, guint dstport) {
130   guint8 srcx[MAX_IPADDR_LEN], dstx[MAX_IPADDR_LEN];
131   int src_index, j, first = 0, len;
132   gulong newseq;
133   tcp_frag *tmp_frag;
134   tcp_stream_chunk sc;
135
136   src_index = -1;
137
138   /* First, check if this packet should be processed. */
139
140   if ((net_src->type != AT_IPv4 && net_src->type != AT_IPv6) ||
141       (net_dst->type != AT_IPv4 && net_dst->type != AT_IPv6))
142     return;
143
144   if (net_src->type == AT_IPv4)
145     len = 4;
146   else
147     len = 16;
148
149   /* Now check if the packet is for this connection. */
150   memcpy(srcx, net_src->data, len);
151   memcpy(dstx, net_dst->data, len);
152   if (
153       ! (
154          memcmp(srcx, ip_address[0], len) == 0 &&
155          memcmp(dstx, ip_address[1], len) == 0 &&
156          srcport == tcp_port[0] &&
157          dstport == tcp_port[1]
158         ) &&
159       ! (
160          memcmp(srcx, ip_address[1], len) == 0 &&
161          memcmp(dstx, ip_address[0], len) == 0 &&
162          srcport == tcp_port[1] &&
163          dstport == tcp_port[0]
164         )
165      )
166     return;
167
168   /* Initialize our stream chunk.  This data gets written to disk. */
169   memcpy(sc.src_addr, srcx, len);
170   sc.src_port = srcport;
171   sc.dlen     = data_length;
172
173   /* Check to see if we have seen this source IP and port before.
174      (Yes, we have to check both source IP and port; the connection
175      might be between two different ports on the same machine.) */
176   for( j=0; j<2; j++ ) {
177     if (memcmp(src_addr[j], srcx, len) == 0 && src_port[j] == srcport ) {
178       src_index = j;
179     }
180   }
181   /* we didn't find it if src_index == -1 */
182   if( src_index < 0 ) {
183     /* assign it to a src_index and get going */
184     for( j=0; j<2; j++ ) {
185       if( src_port[j] == 0 ) {
186         memcpy(src_addr[j], srcx, len);
187         src_port[j] = srcport;
188         src_index = j;
189         first = 1;
190         break;
191       }
192     }
193   }
194   if( src_index < 0 ) {
195     fprintf( stderr, "ERROR in reassemble_tcp: Too many addresses!\n");
196     return;
197   }
198
199   if( data_length < length ) {
200     incomplete_tcp_stream = TRUE;
201   }
202
203   /* now that we have filed away the srcs, lets get the sequence number stuff
204      figured out */
205   if( first ) {
206     /* this is the first time we have seen this src's sequence number */
207     seq[src_index] = sequence + length;
208     if( synflag ) {
209       seq[src_index]++;
210     }
211     /* write out the packet data */
212     write_packet_data( src_index, &sc, data );
213     return;
214   }
215   /* if we are here, we have already seen this src, let's
216      try and figure out if this packet is in the right place */
217   if( sequence < seq[src_index] ) {
218     /* this sequence number seems dated, but
219        check the end to make sure it has no more
220        info than we have already seen */
221     newseq = sequence + length;
222     if( newseq > seq[src_index] ) {
223       gulong new_len;
224
225       /* this one has more than we have seen. let's get the
226          payload that we have not seen. */
227
228       new_len = seq[src_index] - sequence;
229
230       if ( data_length <= new_len ) {
231         data = NULL;
232         data_length = 0;
233         incomplete_tcp_stream = TRUE;
234       } else {
235         data += new_len;
236         data_length -= new_len;
237       }
238       sc.dlen = data_length;
239       sequence = seq[src_index];
240       length = newseq - seq[src_index];
241
242       /* this will now appear to be right on time :) */
243     }
244   }
245   if ( sequence == seq[src_index] ) {
246     /* right on time */
247     seq[src_index] += length;
248     if( synflag ) seq[src_index]++;
249     if( data ) {
250       write_packet_data( src_index, &sc, data );
251     }
252     /* done with the packet, see if it caused a fragment to fit */
253     while( check_fragments( src_index, &sc ) )
254       ;
255   }
256   else {
257     /* out of order packet */
258     if(data_length > 0 && sequence > seq[src_index] ) {
259       tmp_frag = (tcp_frag *)g_malloc( sizeof( tcp_frag ) );
260       tmp_frag->data = (guchar *)g_malloc( data_length );
261       tmp_frag->seq = sequence;
262       tmp_frag->len = length;
263       tmp_frag->data_len = data_length;
264       memcpy( tmp_frag->data, data, data_length );
265       if( frags[src_index] ) {
266         tmp_frag->next = frags[src_index];
267       } else {
268         tmp_frag->next = NULL;
269       }
270       frags[src_index] = tmp_frag;
271     }
272   }
273 } /* end reassemble_tcp */
274
275 /* here we search through all the frag we have collected to see if
276    one fits */
277 static int
278 check_fragments( int index, tcp_stream_chunk *sc ) {
279   tcp_frag *prev = NULL;
280   tcp_frag *current;
281   current = frags[index];
282   while( current ) {
283     if( current->seq == seq[index] ) {
284       /* this fragment fits the stream */
285       if( current->data ) {
286         sc->dlen = current->data_len;
287         write_packet_data( index, sc, current->data );
288       }
289       seq[index] += current->len;
290       if( prev ) {
291         prev->next = current->next;
292       } else {
293         frags[index] = current->next;
294       }
295       g_free( current->data );
296       g_free( current );
297       return 1;
298     }
299     prev = current;
300     current = current->next;
301   }
302   return 0;
303 }
304
305 /* this should always be called before we start to reassemble a stream */
306 void
307 reset_tcp_reassembly() {
308   tcp_frag *current, *next;
309   int i;
310   incomplete_tcp_stream = FALSE;
311   for( i=0; i<2; i++ ) {
312     seq[i] = 0;
313     memset(src_addr[i], '\0', MAX_IPADDR_LEN);
314     src_port[i] = 0;
315     memset(ip_address[i], '\0', MAX_IPADDR_LEN);
316     tcp_port[i] = 0;
317     bytes_written[i] = 0;
318     current = frags[i];
319     while( current ) {
320       next = current->next;
321       g_free( current->data );
322       g_free( current );
323       current = next;
324     }
325     frags[i] = NULL;
326   }
327 }
328
329 static void
330 write_packet_data( int index, tcp_stream_chunk *sc, const char *data )
331 {
332   fwrite( sc, 1, sizeof(tcp_stream_chunk), data_out_file );
333   fwrite( data, 1, sc->dlen, data_out_file );
334   bytes_written[index] += sc->dlen;
335 }