Added just enough fields to TCP to support "Follow TCP Stream". It works now.
[obnox/wireshark/wip.git] / follow.c
1 /* follow.c
2  *
3  * $Id: follow.c,v 1.12 1999/07/17 04:19:02 gram Exp $
4  *
5  * Copyright 1998 Mike Hall <mlh@io.com>
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  *
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <gtk/gtk.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
42 #endif
43
44 #include <glib.h>
45 #include "ethereal.h"
46 #include "packet.h"
47 #include "follow.h"
48
49 extern FILE* data_out_file;
50
51 gboolean incomplete_tcp_stream = FALSE;
52
53 static int check_fragments( int );
54 static void write_packet_data( const u_char *, int );
55
56 /* this will build libpcap filter text that will only 
57    pass the packets related to the stream. There is a 
58    chance that two streams could intersect, but not a 
59    very good one */
60 char* 
61 build_follow_filter( packet_info *pi ) {
62   char* buf = malloc(1024);
63   if( pi->ipproto == 6 ) {
64     /* TCP */
65     sprintf( buf, "(ip.addr eq %s and ip.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
66                 pi->srcip, pi->destip, pi->srcport, pi->destport );
67   }
68   else { 
69     free( buf );
70     return NULL;
71   }
72   return buf;
73 }
74
75 /* here we are going to try and reconstruct the data portion of a TCP
76    session. We will try and handle duplicates, TCP fragments, and out 
77    of order packets in a smart way. */
78
79 static tcp_frag *frags[2] = { 0, 0};
80 static u_long seq[2];
81 static u_long src[2] = { 0, 0 };
82
83 void 
84 reassemble_tcp( u_long sequence, u_long length, const char* data, u_long data_length, int synflag, u_long srcx ) {
85   int src_index, j, first = 0;
86   u_long newseq;
87   tcp_frag *tmp_frag;
88   src_index = -1;
89   /* first we check to see if we have seen this src ip before. */
90   for( j=0; j<2; j++ ) {
91     if( src[j] == srcx ) {
92       src_index = j;
93     }
94   }
95   /* we didn't find it if src_index == -1 */
96   if( src_index < 0 ) {
97     /* assign it to a src_index and get going */
98     for( j=0; j<2; j++ ) {
99       if( src[j] == 0 ) {
100         src[j] = srcx;
101         src_index = j;
102         first = 1;
103         break;
104       }
105     }
106   }
107   if( src_index < 0 ) {
108     fprintf( stderr, "ERROR in reassemble_tcp: Too many addresses!\n");
109     return;
110   }
111
112   if( data_length < length ) {
113     incomplete_tcp_stream = TRUE;
114   }
115
116   /* now that we have filed away the srcs, lets get the sequence number stuff 
117      figured out */
118   if( first ) {
119     /* this is the first time we have seen this src's sequence number */
120     seq[src_index] = sequence + length;
121     if( synflag ) {
122       seq[src_index]++;
123     }
124     /* write out the packet data */
125     write_packet_data( data, data_length );
126     return;
127   }
128   /* if we are here, we have already seen this src, let's
129      try and figure out if this packet is in the right place */
130   if( sequence < seq[src_index] ) {
131     /* this sequence number seems dated, but 
132        check the end to make sure it has no more
133        info than we have already seen */
134     newseq = sequence + length;
135     if( newseq > seq[src_index] ) {
136       u_long new_len;
137
138       /* this one has more than we have seen. let's get the 
139          payload that we have not seen. */
140
141       new_len = seq[src_index] - sequence;
142
143       if ( data_length <= new_len ) {
144         data = NULL;
145         data_length = 0;
146         incomplete_tcp_stream = TRUE;
147       } else {
148         data += new_len;
149         data_length -= new_len;
150       }
151       sequence = seq[src_index];
152       length = newseq - seq[src_index];
153       
154       /* this will now appear to be right on time :) */
155     }
156   }
157   if ( sequence == seq[src_index] ) {
158     /* right on time */
159     seq[src_index] += length;
160     if( synflag ) seq[src_index]++;
161     if( data ) {
162       write_packet_data( data, data_length );
163     }
164     /* done with the packet, see if it caused a fragment to fit */
165     while( check_fragments( src_index ) )
166       ;
167   }
168   else {
169     /* out of order packet */
170     if( sequence > seq[src_index] ) {
171       tmp_frag = (tcp_frag *)malloc( sizeof( tcp_frag ) );
172       tmp_frag->data = (u_char *)malloc( data_length );
173       tmp_frag->seq = sequence;
174       tmp_frag->len = length;
175       tmp_frag->data_len = data_length;
176       memcpy( tmp_frag->data, data, data_length );
177       if( frags[src_index] ) {
178         tmp_frag->next = frags[src_index];
179       } else {
180         tmp_frag->next = NULL;
181       }
182       frags[src_index] = tmp_frag;
183     }
184   }
185 } /* end reassemble_tcp */
186
187 /* here we search through all the frag we have collected to see if
188    one fits */
189 static int 
190 check_fragments( int index ) {
191   tcp_frag *prev = NULL;
192   tcp_frag *current;
193   current = frags[index];
194   while( current ) {
195     if( current->seq == seq[index] ) {
196       /* this fragment fits the stream */
197       if( current->data ) {
198         write_packet_data( current->data, current->data_len );
199       }
200       seq[index] += current->len;
201       if( prev ) {
202         prev->next = current->next;
203       } else {
204         frags[index] = current->next;
205       }
206       free( current->data );
207       free( current );
208       return 1;
209     }
210     prev = current;
211     current = current->next;
212   }
213   return 0;
214 }
215
216 /* this should always be called before we start to reassemble a stream */
217 void 
218 reset_tcp_reassembly() {
219   tcp_frag *current, *next;
220   int i;
221   incomplete_tcp_stream = FALSE;
222   for( i=0; i<2; i++ ) {
223     seq[i] = 0;
224     src[i] = 0;
225     current = frags[i];
226     while( current ) {
227       next = current->next;
228       free( current->data ); 
229       free( current );
230       current = next;
231     }
232     frags[i] = NULL;
233   }
234 }
235
236 static void 
237 write_packet_data( const u_char* data, int length ) {
238   fwrite( data, 1, length, data_out_file );
239 }
240