6982291121c354bf90fea1a735600a431251c952
[obnox/wireshark/wip.git] / ui / gtk / follow_tcp.c
1 /* follow_tcp.c
2  * TCP specific routines for following traffic streams
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
23  * USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <string.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include <ctype.h>
38
39 #include <gtk/gtk.h>
40
41 #include <epan/follow.h>
42 #include <epan/dissectors/packet-ipv6.h>
43 #include <epan/prefs.h>
44 #include <epan/addr_resolv.h>
45 #include <epan/charsets.h>
46 #include <epan/epan_dissect.h>
47 #include <epan/filesystem.h>
48 #include <epan/ipproto.h>
49 #include <epan/charsets.h>
50
51 #include "../file.h"
52 #include "ui/alert_box.h"
53 #include "ui/simple_dialog.h"
54 #include "../tempfile.h"
55 #include <wsutil/file_util.h>
56
57 #include "gtkglobals.h"
58 #include "ui/gtk/color_utils.h"
59 #include "ui/gtk/follow_tcp.h"
60 #include "ui/gtk/dlg_utils.h"
61 #include "ui/gtk/file_dlg.h"
62 #include "ui/gtk/keys.h"
63 #include "ui/gtk/main.h"
64 #include "ui/gtk/gui_utils.h"
65 #include "ui/win32/print_win32.h"
66 #include "ui/gtk/font_utils.h"
67 #include "ui/gtk/help_dlg.h"
68 #include "ui/gtk/follow_stream.h"
69 #include "ui/gtk/utf8_entities.h"
70
71 /* With MSVC and a libwireshark.dll, we need a special declaration. */
72 WS_VAR_IMPORT FILE *data_out_file;
73
74 static void
75 follow_redraw(gpointer data, gpointer user_data _U_)
76 {
77         follow_load_text((follow_info_t *)data);
78 }
79
80 /* Redraw the text in all "Follow TCP Stream" windows. */
81 void
82 follow_tcp_redraw_all(void)
83 {
84         g_list_foreach(follow_infos, follow_redraw, NULL);
85 }
86
87 /* Follow the TCP stream, if any, to which the last packet that we called
88    a dissection routine on belongs (this might be the most recently
89    selected packet, or it might be the last packet in the file). */
90 void
91 follow_tcp_stream_cb(GtkWidget * w _U_, gpointer data _U_)
92 {
93         GtkWidget *filter_cm;
94         GtkWidget       *filter_te;
95         int             tmp_fd;
96         gchar           *follow_filter;
97         const gchar     *previous_filter;
98         int             filter_out_filter_len;
99         const char      *hostname0, *hostname1;
100         char            *port0, *port1;
101         gchar           *server_to_client_string = NULL;
102         gchar           *client_to_server_string = NULL;
103         gchar           *both_directions_string = NULL;
104         follow_stats_t stats;
105         follow_info_t   *follow_info;
106         tcp_stream_chunk sc;
107         size_t              nchars;
108         gchar           *data_out_filename;
109
110         /* we got tcp so we can follow */
111         if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
112                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
113                               "Error following stream.  Please make\n"
114                               "sure you have a TCP packet selected.");
115                 return;
116         }
117
118         follow_info = g_new0(follow_info_t, 1);
119         follow_info->follow_type = FOLLOW_TCP;
120
121         /* Create a new filter that matches all packets in the TCP stream,
122            and set the display filter entry accordingly */
123         reset_tcp_reassembly();
124         follow_filter = build_follow_filter(&cfile.edt->pi);
125         if (!follow_filter) {
126                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
127                               "Error creating filter for this stream.\n"
128                               "A transport or network layer header is needed");
129                 g_free(follow_info);
130                 return;
131         }
132
133         /* Create a temporary file into which to dump the reassembled data
134            from the TCP stream, and set "data_out_file" to refer to it, so
135            that the TCP code will write to it.
136
137            XXX - it might be nicer to just have the TCP code directly
138            append stuff to the text widget for the TCP stream window,
139            if we can arrange that said window not pop up until we're
140            done. */
141         tmp_fd = create_tempfile(&data_out_filename, "follow");
142         follow_info->data_out_filename = g_strdup(data_out_filename);
143
144         if (tmp_fd == -1) {
145             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
146                           "Could not create temporary file %s: %s",
147                           follow_info->data_out_filename, g_strerror(errno));
148             g_free(follow_info->data_out_filename);
149             g_free(follow_info);
150             g_free(follow_filter);
151             return;
152         }
153
154         data_out_file = fdopen(tmp_fd, "w+b");
155         if (data_out_file == NULL) {
156             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
157                           "Could not create temporary file %s: %s",
158                           follow_info->data_out_filename, g_strerror(errno));
159             ws_close(tmp_fd);
160             ws_unlink(follow_info->data_out_filename);
161             g_free(follow_info->data_out_filename);
162             g_free(follow_info);
163             g_free(follow_filter);
164             return;
165         }
166
167         /* Set the display filter entry accordingly */
168         filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
169         filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
170
171         /* needed in follow_filter_out_stream(), is there a better way? */
172         follow_info->filter_te = filter_te;
173
174         /* save previous filter, const since we're not supposed to alter */
175         previous_filter =
176             (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
177
178         /* allocate our new filter. API claims g_malloc terminates program on failure */
179         /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
180         filter_out_filter_len = (int)(strlen(follow_filter) + strlen(previous_filter) + 16);
181         follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
182
183         /* append the negation */
184         if(strlen(previous_filter)) {
185             g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
186             "%s and !(%s)", previous_filter, follow_filter);
187         } else {
188             g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
189             "!(%s)", follow_filter);
190         }
191
192         gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
193
194         /* Run the display filter so it goes in effect - even if it's the
195            same as the previous display filter. */
196         main_filter_packets(&cfile, follow_filter, TRUE);
197
198         /* Free the filter string, as we're done with it. */
199         g_free(follow_filter);
200
201         /* Check whether we got any data written to the file. */
202         if (empty_tcp_stream) {
203             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
204                           "The packets in the capture file for that stream have no data.");
205             ws_close(tmp_fd);
206             ws_unlink(follow_info->data_out_filename);
207             g_free(follow_info->data_out_filename);
208             g_free(follow_info->filter_out_filter);
209             g_free(follow_info);
210             return;
211         }
212
213         /* Go back to the top of the file and read the first tcp_stream_chunk
214          * to ensure that the IP addresses and port numbers in the drop-down
215          * list are tied to the correct lines displayed by follow_read_stream()
216          * later on (which also reads from this file).  Close the file when
217          * we're done.
218          *
219          * We read the data now, before we pop up a window, in case the
220          * read fails.  We use the data later.
221          */
222
223         rewind(data_out_file);
224         nchars=fread(&sc, 1, sizeof(sc), data_out_file);
225         if (nchars != sizeof(sc)) {
226             if (ferror(data_out_file)) {
227                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
228                               "Could not read from temporary file %s: %s",
229                               follow_info->data_out_filename, g_strerror(errno));
230             } else {
231                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
232                               "Short read from temporary file %s: expected %lu, got %lu",
233                               follow_info->data_out_filename,
234                               (unsigned long)sizeof(sc),
235                               (unsigned long)nchars);
236             }
237             ws_close(tmp_fd);
238             ws_unlink(follow_info->data_out_filename);
239             g_free(follow_info->data_out_filename);
240             g_free(follow_info->filter_out_filter);
241             g_free(follow_info);
242             return;
243         }
244         fclose(data_out_file);
245
246         /* The data_out_filename file now has all the text that was in the
247            session (this is dumped to file by the TCP dissector). */
248
249         /* Stream to show */
250         follow_stats(&stats);
251
252         if (stats.is_ipv6) {
253                 struct e_in6_addr ipaddr;
254                 memcpy(&ipaddr, stats.ip_address[0], 16);
255                 hostname0 = get_hostname6(&ipaddr);
256                 memcpy(&ipaddr, stats.ip_address[0], 16);
257                 hostname1 = get_hostname6(&ipaddr);
258         } else {
259                 guint32 ipaddr;
260                 memcpy(&ipaddr, stats.ip_address[0], 4);
261                 hostname0 = get_hostname(ipaddr);
262                 memcpy(&ipaddr, stats.ip_address[1], 4);
263                 hostname1 = get_hostname(ipaddr);
264         }
265
266         follow_info->is_ipv6 = stats.is_ipv6;
267
268         port0 = get_tcp_port(stats.port[0]);
269         port1 = get_tcp_port(stats.port[1]);
270
271         /* Host 0 --> Host 1 */
272         if(sc.src_port == stats.port[0]) {
273                 server_to_client_string =
274                         g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
275                                         hostname0, port0,
276                                         hostname1, port1,
277                                         stats.bytes_written[0]);
278         } else {
279                 server_to_client_string =
280                         g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
281                                         hostname1, port1,
282                                         hostname0,port0,
283                                         stats.bytes_written[0]);
284         }
285
286         /* Host 1 --> Host 0 */
287         if(sc.src_port == stats.port[1]) {
288                 client_to_server_string =
289                         g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
290                                         hostname0, port0,
291                                         hostname1, port1,
292                                         stats.bytes_written[1]);
293         } else {
294                 client_to_server_string =
295                         g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
296                                         hostname1, port1,
297                                         hostname0, port0,
298                                         stats.bytes_written[1]);
299         }
300
301         /* Both Stream Directions */
302         both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", stats.bytes_written[0] + stats.bytes_written[1]);
303
304         follow_stream("Follow TCP Stream", follow_info, both_directions_string,
305                       server_to_client_string, client_to_server_string);
306
307         g_free(both_directions_string);
308         g_free(server_to_client_string);
309         g_free(client_to_server_string);
310
311         data_out_file = NULL;
312 }
313
314 #define FLT_BUF_SIZE 1024
315
316 /*
317  * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
318  * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
319  * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
320  * the "print_line()" routine from "print.c", and as that routine might
321  * genuinely expect to be handed a line (if, for example, it's using
322  * some OS or desktop environment's printing API, and that API expects
323  * to be handed lines), "follow_print_text()" should probably accumulate
324  * lines in a buffer and hand them "print_line()".  (If there's a
325  * complete line in a buffer - i.e., there's nothing of the line in
326  * the previous buffer or the next buffer - it can just hand that to
327  * "print_line()" after filtering out non-printables, as an
328  * optimization.)
329  *
330  * This might or might not be the reason why C arrays display
331  * correctly but get extra blank lines very other line when printed.
332  */
333 frs_return_t
334 follow_read_tcp_stream(follow_info_t *follow_info,
335                        gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
336                        void *arg)
337 {
338     tcp_stream_chunk    sc;
339     size_t              bcount;
340     size_t              bytes_read;
341     int                 iplen;
342     guint8              client_addr[MAX_IPADDR_LEN];
343     guint16             client_port = 0;
344     gboolean            is_server;
345     guint32             global_client_pos = 0, global_server_pos = 0;
346     guint32             server_packet_count = 0;
347     guint32             client_packet_count = 0;
348     guint32             *global_pos;
349     gboolean            skip;
350     char                buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
351     size_t              nchars;
352     frs_return_t        frs_return;
353
354     iplen = (follow_info->is_ipv6) ? 16 : 4;
355
356     data_out_file = ws_fopen(follow_info->data_out_filename, "rb");
357     if (data_out_file == NULL) {
358         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
359                       "Could not open temporary file %s: %s", follow_info->data_out_filename,
360                       g_strerror(errno));
361         return FRS_OPEN_ERROR;
362     }
363
364     while ((nchars=fread(&sc, 1, sizeof(sc), data_out_file))) {
365         if (nchars != sizeof(sc)) {
366             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
367                           "Short read from temporary file %s: expected %lu, got %lu",
368                           follow_info->data_out_filename,
369                           (unsigned long)sizeof(sc),
370                           (unsigned long)nchars);
371             fclose(data_out_file);
372             data_out_file = NULL;
373             return FRS_READ_ERROR;
374         }
375         if (client_port == 0) {
376             memcpy(client_addr, sc.src_addr, iplen);
377             client_port = sc.src_port;
378         }
379         skip = FALSE;
380         if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
381             client_port == sc.src_port) {
382             is_server = FALSE;
383             global_pos = &global_client_pos;
384             if (follow_info->show_stream == FROM_SERVER) {
385                 skip = TRUE;
386             }
387         }
388         else {
389             is_server = TRUE;
390             global_pos = &global_server_pos;
391             if (follow_info->show_stream == FROM_CLIENT) {
392                 skip = TRUE;
393             }
394         }
395
396         bytes_read = 0;
397         while (bytes_read < sc.dlen) {
398             bcount = ((sc.dlen-bytes_read) < FLT_BUF_SIZE) ? (sc.dlen-bytes_read) : FLT_BUF_SIZE;
399             nchars = fread(buffer, 1, bcount, data_out_file);
400             if (nchars == 0)
401                 break;
402             /* XXX - if we don't get "bcount" bytes, is that an error? */
403             bytes_read += nchars;
404
405             if (!skip) {
406                     frs_return = follow_show(follow_info, print_line_fcn_p, buffer,
407                                              nchars, is_server, arg, global_pos,
408                                              &server_packet_count,
409                                              &client_packet_count);
410                     if(frs_return == FRS_PRINT_ERROR) {
411                             fclose(data_out_file);
412                             data_out_file = NULL;
413                             return frs_return;
414
415                     }
416             }
417         }
418     }
419
420     if (ferror(data_out_file)) {
421         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
422                       "Error reading temporary file %s: %s", follow_info->data_out_filename,
423                       g_strerror(errno));
424         fclose(data_out_file);
425         data_out_file = NULL;
426         return FRS_READ_ERROR;
427     }
428
429     fclose(data_out_file);
430     data_out_file = NULL;
431     return FRS_OK;
432 }