2 * TCP specific routines for following traffic streams
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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,
44 #include "file_util.h"
48 #include "follow_tcp.h"
49 #include <epan/follow.h>
50 #include "dlg_utils.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>
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"
70 #include <epan/charsets.h>
72 #include "follow_stream.h"
74 /* This is backwards-compatibility code for old versions of GTK+ (2.2.1 and
75 * earlier). It defines the new wrap behavior (unknown in earlier versions)
76 * as the old (slightly buggy) wrap behavior.
78 #ifndef GTK_WRAP_WORD_CHAR
79 #define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD
82 /* With MSVC and a libwireshark.dll, we need a special declaration. */
83 WS_VAR_IMPORT FILE *data_out_file;
86 follow_redraw(gpointer data, gpointer user_data _U_)
88 follow_load_text((follow_info_t *)data);
91 /* Redraw the text in all "Follow TCP Stream" windows. */
93 follow_tcp_redraw_all(void)
95 g_list_foreach(follow_infos, follow_redraw, NULL);
98 /* Follow the TCP stream, if any, to which the last packet that we called
99 a dissection routine on belongs (this might be the most recently
100 selected packet, or it might be the last packet in the file). */
102 follow_tcp_stream_cb(GtkWidget * w, gpointer data _U_)
104 GtkWidget *filter_te;
106 gchar *follow_filter;
107 const gchar *previous_filter;
108 int filter_out_filter_len;
109 const char *hostname0, *hostname1;
111 gchar *server_to_client_string = NULL;
112 gchar *client_to_server_string = NULL;
113 gchar *both_directions_string = NULL;
114 follow_stats_t stats;
115 follow_info_t *follow_info;
119 /* we got tcp so we can follow */
120 if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
121 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
122 "Error following stream. Please make\n"
123 "sure you have a TCP packet selected.");
127 follow_info = g_new0(follow_info_t, 1);
128 follow_info->follow_type = FOLLOW_TCP;
130 /* Create a temporary file into which to dump the reassembled data
131 from the TCP stream, and set "data_out_file" to refer to it, so
132 that the TCP code will write to it.
134 XXX - it might be nicer to just have the TCP code directly
135 append stuff to the text widget for the TCP stream window,
136 if we can arrange that said window not pop up until we're
138 tmp_fd = create_tempfile(follow_info->data_out_filename,
139 sizeof follow_info->data_out_filename, "follow");
142 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
143 "Could not create temporary file %s: %s",
144 follow_info->data_out_filename, strerror(errno));
149 data_out_file = fdopen(tmp_fd, "w+b");
150 if (data_out_file == NULL) {
151 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
152 "Could not create temporary file %s: %s",
153 follow_info->data_out_filename, strerror(errno));
155 eth_unlink(follow_info->data_out_filename);
160 /* Create a new filter that matches all packets in the TCP stream,
161 and set the display filter entry accordingly */
162 reset_tcp_reassembly();
163 follow_filter = build_follow_filter(&cfile.edt->pi);
165 /* Set the display filter entry accordingly */
166 filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
168 /* needed in follow_filter_out_stream(), is there a better way? */
169 follow_info->filter_te = filter_te;
171 /* save previous filter, const since we're not supposed to alter */
173 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
175 /* allocate our new filter. API claims g_malloc terminates program on failure */
176 /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
177 filter_out_filter_len = strlen(follow_filter) + strlen(previous_filter) + 16;
178 follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
180 /* append the negation */
181 if(strlen(previous_filter)) {
182 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
183 "%s and !(%s)", previous_filter, follow_filter);
185 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
186 "!(%s)", follow_filter);
189 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
191 /* Run the display filter so it goes in effect - even if it's the
192 same as the previous display filter. */
193 main_filter_packets(&cfile, follow_filter, TRUE);
195 /* Free the filter string, as we're done with it. */
196 g_free(follow_filter);
198 /* Check whether we got any data written to the file. */
199 if (empty_tcp_stream) {
200 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
201 "The packets in the capture file for that stream have no data.");
203 eth_unlink(follow_info->data_out_filename);
208 /* Go back to the top of the file and read the first tcp_stream_chunk
209 * to ensure that the IP addresses and port numbers in the drop-down
210 * list are tied to the correct lines displayed by follow_read_stream()
211 * later on (which also reads from this file). Close the file when
214 * We read the data now, before we pop up a window, in case the
215 * read fails. We use the data later.
218 rewind(data_out_file);
219 nchars=fread(&sc, 1, sizeof(sc), data_out_file);
220 if (nchars != sizeof(sc)) {
221 if (ferror(data_out_file)) {
222 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
223 "Could not read from temporary file %s: %s",
224 follow_info->data_out_filename, strerror(errno));
226 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
227 "Short read from temporary file %s: expected %lu, got %lu",
228 follow_info->data_out_filename,
229 (unsigned long)sizeof(sc),
230 (unsigned long)nchars);
233 eth_unlink(follow_info->data_out_filename);
237 fclose(data_out_file);
239 /* The data_out_filename file now has all the text that was in the
240 session (this is dumped to file by the TCP dissector). */
243 follow_stats(&stats);
246 struct e_in6_addr ipaddr;
247 memcpy(&ipaddr, stats.ip_address[0], 16);
248 hostname0 = get_hostname6(&ipaddr);
249 memcpy(&ipaddr, stats.ip_address[0], 16);
250 hostname1 = get_hostname6(&ipaddr);
253 memcpy(&ipaddr, stats.ip_address[0], 4);
254 hostname0 = get_hostname(ipaddr);
255 memcpy(&ipaddr, stats.ip_address[1], 4);
256 hostname1 = get_hostname(ipaddr);
259 follow_info->is_ipv6 = stats.is_ipv6;
261 port0 = get_tcp_port(stats.port[0]);
262 port1 = get_tcp_port(stats.port[1]);
264 /* Host 0 --> Host 1 */
265 if(sc.src_port == strtol(port0, NULL, 10)) {
266 server_to_client_string =
267 g_strdup_printf("%s:%s --> %s:%s (%u bytes)",
270 stats.bytes_written[0]);
272 server_to_client_string =
273 g_strdup_printf("%s:%s --> %s:%s (%u bytes)",
276 stats.bytes_written[0]);
279 /* Host 1 --> Host 0 */
280 if(sc.src_port == strtol(port0, NULL, 10)) {
281 client_to_server_string =
282 g_strdup_printf("%s:%s --> %s:%s (%u bytes)",
285 stats.bytes_written[1]);
288 client_to_server_string =
289 g_strdup_printf("%s:%s --> %s:%s (%u bytes)",
292 stats.bytes_written[1]);
295 /* Both Stream Directions */
296 both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", stats.bytes_written[0] + stats.bytes_written[1]);
298 follow_stream("Follow TCP Stream", follow_info, both_directions_string,
299 server_to_client_string, client_to_server_string);
301 g_free(both_directions_string);
302 g_free(server_to_client_string);
303 g_free(client_to_server_string);
305 data_out_file = NULL;
308 #define FLT_BUF_SIZE 1024
311 * XXX - the routine pointed to by "print_line" doesn't get handed lines,
312 * it gets handed bufferfuls. That's fine for "follow_write_raw()"
313 * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
314 * the "print_line()" routine from "print.c", and as that routine might
315 * genuinely expect to be handed a line (if, for example, it's using
316 * some OS or desktop environment's printing API, and that API expects
317 * to be handed lines), "follow_print_text()" should probably accumulate
318 * lines in a buffer and hand them "print_line()". (If there's a
319 * complete line in a buffer - i.e., there's nothing of the line in
320 * the previous buffer or the next buffer - it can just hand that to
321 * "print_line()" after filtering out non-printables, as an
324 * This might or might not be the reason why C arrays display
325 * correctly but get extra blank lines very other line when printed.
328 follow_read_tcp_stream(follow_info_t *follow_info,
329 gboolean (*print_line)(char *, size_t, gboolean, void *),
334 guint8 client_addr[MAX_IPADDR_LEN];
335 guint16 client_port = 0;
337 guint32 global_client_pos = 0, global_server_pos = 0;
340 char buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
342 frs_return_t frs_return;
344 iplen = (follow_info->is_ipv6) ? 16 : 4;
346 data_out_file = eth_fopen(follow_info->data_out_filename, "rb");
347 if (data_out_file == NULL) {
348 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
349 "Could not open temporary file %s: %s", follow_info->data_out_filename,
351 return FRS_OPEN_ERROR;
354 while ((nchars=fread(&sc, 1, sizeof(sc), data_out_file))) {
355 if (nchars != sizeof(sc)) {
356 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
357 "Short read from temporary file %s: expected %lu, got %lu",
358 follow_info->data_out_filename,
359 (unsigned long)sizeof(sc),
360 (unsigned long)nchars);
361 fclose(data_out_file);
362 data_out_file = NULL;
363 return FRS_READ_ERROR;
365 if (client_port == 0) {
366 memcpy(client_addr, sc.src_addr, iplen);
367 client_port = sc.src_port;
370 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
371 client_port == sc.src_port) {
373 global_pos = &global_client_pos;
374 if (follow_info->show_stream == FROM_SERVER) {
380 global_pos = &global_server_pos;
381 if (follow_info->show_stream == FROM_CLIENT) {
386 while (sc.dlen > 0) {
387 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
388 nchars = fread(buffer, 1, bcount, data_out_file);
391 /* XXX - if we don't get "bcount" bytes, is that an error? */
395 frs_return = follow_show(follow_info, print_line, buffer,
396 nchars, is_server, arg,
398 if(frs_return == FRS_PRINT_ERROR) {
399 fclose(data_out_file);
400 data_out_file = NULL;
408 if (ferror(data_out_file)) {
409 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
410 "Error reading temporary file %s: %s", follow_info->data_out_filename,
412 fclose(data_out_file);
413 data_out_file = NULL;
414 return FRS_READ_ERROR;
417 fclose(data_out_file);
418 data_out_file = NULL;