From Jakub Zawadzki via bug 4273:
[obnox/wireshark/wip.git] / 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 "../isprint.h"
52 #include "../color.h"
53 #include "../file.h"
54 #include "../globals.h"
55 #include "../alert_box.h"
56 #include "../simple_dialog.h"
57 #include "../tempfile.h"
58 #include <wsutil/file_util.h>
59
60 #include "gtk/color_utils.h"
61 #include "gtk/follow_tcp.h"
62 #include "gtk/dlg_utils.h"
63 #include "gtk/file_dlg.h"
64 #include "gtk/keys.h"
65 #include "gtk/main.h"
66 #include "gtk/gui_utils.h"
67 #include "gtk/print_win32.h"
68 #include "gtk/font_utils.h"
69 #include "gtk/help_dlg.h"
70 #include "gtk/follow_stream.h"
71
72
73 /* With MSVC and a libwireshark.dll, we need a special declaration. */
74 WS_VAR_IMPORT FILE *data_out_file;
75
76 static void
77 follow_redraw(gpointer data, gpointer user_data _U_)
78 {
79         follow_load_text((follow_info_t *)data);
80 }
81
82 /* Redraw the text in all "Follow TCP Stream" windows. */
83 void
84 follow_tcp_redraw_all(void)
85 {
86         g_list_foreach(follow_infos, follow_redraw, NULL);
87 }
88
89 /* Follow the TCP stream, if any, to which the last packet that we called
90    a dissection routine on belongs (this might be the most recently
91    selected packet, or it might be the last packet in the file). */
92 void
93 follow_tcp_stream_cb(GtkWidget * w, gpointer data _U_)
94 {
95         GtkWidget       *filter_te;
96         int             tmp_fd;
97         gchar           *follow_filter;
98         const gchar     *previous_filter;
99         int             filter_out_filter_len;
100         const char      *hostname0, *hostname1;
101         char            *port0, *port1;
102         gchar           *server_to_client_string = NULL;
103         gchar           *client_to_server_string = NULL;
104         gchar           *both_directions_string = NULL;
105         follow_stats_t stats;
106         follow_info_t   *follow_info;
107         tcp_stream_chunk sc;
108         size_t              nchars;
109         gchar           *data_out_filename;
110
111         /* we got tcp so we can follow */
112         if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
113                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
114                               "Error following stream.  Please make\n"
115                               "sure you have a TCP packet selected.");
116                 return;
117         }
118
119         follow_info = g_new0(follow_info_t, 1);
120         follow_info->follow_type = FOLLOW_TCP;
121
122         /* Create a new filter that matches all packets in the TCP stream,
123            and set the display filter entry accordingly */
124         reset_tcp_reassembly();
125         follow_filter = build_follow_filter(&cfile.edt->pi);
126         if (!follow_filter) {
127                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
128                               "Error creating filter for this stream.\n"
129                               "A transport or network layer header is needed");
130                 g_free(follow_info);
131                 return;
132         }
133
134         /* Create a temporary file into which to dump the reassembled data
135            from the TCP stream, and set "data_out_file" to refer to it, so
136            that the TCP code will write to it.
137
138            XXX - it might be nicer to just have the TCP code directly
139            append stuff to the text widget for the TCP stream window,
140            if we can arrange that said window not pop up until we're
141            done. */
142         tmp_fd = create_tempfile(&data_out_filename, "follow");
143         follow_info->data_out_filename = g_strdup(data_out_filename);
144
145         if (tmp_fd == -1) {
146             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
147                           "Could not create temporary file %s: %s",
148                           follow_info->data_out_filename, strerror(errno));
149             g_free(follow_info->data_out_filename);
150             g_free(follow_info);
151             g_free(follow_filter);
152             return;
153         }
154
155         data_out_file = fdopen(tmp_fd, "w+b");
156         if (data_out_file == NULL) {
157             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
158                           "Could not create temporary file %s: %s",
159                           follow_info->data_out_filename, strerror(errno));
160             ws_close(tmp_fd);
161             ws_unlink(follow_info->data_out_filename);
162             g_free(follow_info->data_out_filename);
163             g_free(follow_info);
164             g_free(follow_filter);
165             return;
166         }
167
168         /* Set the display filter entry accordingly */
169         filter_te = g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY);
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, 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 --> %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 --> %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 --> %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 --> %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     int                 bcount, iplen;
340     guint8              client_addr[MAX_IPADDR_LEN];
341     guint16             client_port = 0;
342     gboolean            is_server;
343     guint32             global_client_pos = 0, global_server_pos = 0;
344     guint32             server_packet_count = 0;
345     guint32             client_packet_count = 0;
346     guint32             *global_pos;
347     gboolean            skip;
348     char                buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
349     size_t              nchars;
350     frs_return_t        frs_return;
351
352     iplen = (follow_info->is_ipv6) ? 16 : 4;
353
354     data_out_file = ws_fopen(follow_info->data_out_filename, "rb");
355     if (data_out_file == NULL) {
356         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
357                       "Could not open temporary file %s: %s", follow_info->data_out_filename,
358                       strerror(errno));
359         return FRS_OPEN_ERROR;
360     }
361
362     while ((nchars=fread(&sc, 1, sizeof(sc), data_out_file))) {
363         if (nchars != sizeof(sc)) {
364             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
365                           "Short read from temporary file %s: expected %lu, got %lu",
366                           follow_info->data_out_filename,
367                           (unsigned long)sizeof(sc),
368                           (unsigned long)nchars);
369             fclose(data_out_file);
370             data_out_file = NULL;
371             return FRS_READ_ERROR;
372         }
373         if (client_port == 0) {
374             memcpy(client_addr, sc.src_addr, iplen);
375             client_port = sc.src_port;
376         }
377         skip = FALSE;
378         if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
379             client_port == sc.src_port) {
380             is_server = FALSE;
381             global_pos = &global_client_pos;
382             if (follow_info->show_stream == FROM_SERVER) {
383                 skip = TRUE;
384             }
385         }
386         else {
387             is_server = TRUE;
388             global_pos = &global_server_pos;
389             if (follow_info->show_stream == FROM_CLIENT) {
390                 skip = TRUE;
391             }
392         }
393
394         while (sc.dlen > 0) {
395             bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
396             nchars = fread(buffer, 1, bcount, data_out_file);
397             if (nchars == 0)
398                 break;
399             /* XXX - if we don't get "bcount" bytes, is that an error? */
400             sc.dlen -= (guint32) nchars;
401
402             if (!skip) {
403                     frs_return = follow_show(follow_info, print_line_fcn_p, buffer,
404                                              nchars, is_server, arg, global_pos,
405                                              &server_packet_count,
406                                              &client_packet_count);
407                     if(frs_return == FRS_PRINT_ERROR) {
408                             fclose(data_out_file);
409                             data_out_file = NULL;
410                             return frs_return;
411
412                     }
413             }
414         }
415     }
416
417     if (ferror(data_out_file)) {
418         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
419                       "Error reading temporary file %s: %s", follow_info->data_out_filename,
420                       strerror(errno));
421         fclose(data_out_file);
422         data_out_file = NULL;
423         return FRS_READ_ERROR;
424     }
425
426     fclose(data_out_file);
427     data_out_file = NULL;
428     return FRS_OK;
429 }