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