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 /* With MSVC and a libwireshark.dll, we need a special declaration. */
75 WS_VAR_IMPORT FILE *data_out_file;
78 follow_redraw(gpointer data, gpointer user_data _U_)
80 follow_load_text((follow_info_t *)data);
83 /* Redraw the text in all "Follow TCP Stream" windows. */
85 follow_tcp_redraw_all(void)
87 g_list_foreach(follow_infos, follow_redraw, NULL);
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). */
94 follow_tcp_stream_cb(GtkWidget * w, gpointer data _U_)
99 const gchar *previous_filter;
100 int filter_out_filter_len;
101 const char *hostname0, *hostname1;
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;
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.");
119 follow_info = g_new0(follow_info_t, 1);
120 follow_info->follow_type = FOLLOW_TCP;
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.
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
130 tmp_fd = create_tempfile(follow_info->data_out_filename,
131 sizeof follow_info->data_out_filename, "follow");
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));
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));
147 eth_unlink(follow_info->data_out_filename);
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);
157 /* Set the display filter entry accordingly */
158 filter_te = g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY);
160 /* needed in follow_filter_out_stream(), is there a better way? */
161 follow_info->filter_te = filter_te;
163 /* save previous filter, const since we're not supposed to alter */
165 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
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);
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);
177 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
178 "!(%s)", follow_filter);
181 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
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);
187 /* Free the filter string, as we're done with it. */
188 g_free(follow_filter);
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.");
195 eth_unlink(follow_info->data_out_filename);
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
206 * We read the data now, before we pop up a window, in case the
207 * read fails. We use the data later.
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));
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);
225 eth_unlink(follow_info->data_out_filename);
229 fclose(data_out_file);
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). */
235 follow_stats(&stats);
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);
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);
251 follow_info->is_ipv6 = stats.is_ipv6;
253 port0 = get_tcp_port(stats.port[0]);
254 port1 = get_tcp_port(stats.port[1]);
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)",
262 stats.bytes_written[0]);
264 server_to_client_string =
265 g_strdup_printf("%s:%s --> %s:%s (%u bytes)",
268 stats.bytes_written[0]);
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)",
277 stats.bytes_written[1]);
279 client_to_server_string =
280 g_strdup_printf("%s:%s --> %s:%s (%u bytes)",
283 stats.bytes_written[1]);
286 /* Both Stream Directions */
287 both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", stats.bytes_written[0] + stats.bytes_written[1]);
289 follow_stream("Follow TCP Stream", follow_info, both_directions_string,
290 server_to_client_string, client_to_server_string);
292 g_free(both_directions_string);
293 g_free(server_to_client_string);
294 g_free(client_to_server_string);
296 data_out_file = NULL;
299 #define FLT_BUF_SIZE 1024
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
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.
319 follow_read_tcp_stream(follow_info_t *follow_info,
320 gboolean (*print_line)(char *, size_t, gboolean, void *),
325 guint8 client_addr[MAX_IPADDR_LEN];
326 guint16 client_port = 0;
328 guint32 global_client_pos = 0, global_server_pos = 0;
329 guint32 server_packet_count = 0;
330 guint32 client_packet_count = 0;
333 char buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
335 frs_return_t frs_return;
337 iplen = (follow_info->is_ipv6) ? 16 : 4;
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,
344 return FRS_OPEN_ERROR;
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;
358 if (client_port == 0) {
359 memcpy(client_addr, sc.src_addr, iplen);
360 client_port = sc.src_port;
363 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
364 client_port == sc.src_port) {
366 global_pos = &global_client_pos;
367 if (follow_info->show_stream == FROM_SERVER) {
373 global_pos = &global_server_pos;
374 if (follow_info->show_stream == FROM_CLIENT) {
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);
384 /* XXX - if we don't get "bcount" bytes, is that an error? */
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;
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,
406 fclose(data_out_file);
407 data_out_file = NULL;
408 return FRS_READ_ERROR;
411 fclose(data_out_file);
412 data_out_file = NULL;