/* main.c * * $Id: main.c,v 1.102 2000/01/29 16:41:28 gram Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Richard Sharpe, 13-Feb-1999, added support for initializing structures * needed by dissect routines * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * To do: * - Graphs * - Check for end of packet in dissect_* routines. * - Playback window * - Multiple window support * - Add cut/copy/paste * - Create header parsing routines * - Make byte view selections more fancy? * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #ifdef HAVE_IO_H #include /* open/close on win32 */ #endif #ifdef HAVE_DIRECT_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include #ifdef NEED_SNPRINTF_H # ifdef HAVE_STDARG_H # include # else # include # endif # include "snprintf.h" #endif #if defined(HAVE_UCD_SNMP_SNMP_H) #ifdef HAVE_UCD_SNMP_VERSION_H #include #endif /* HAVE_UCD_SNMP_VERSION_H */ #elif defined(HAVE_SNMP_SNMP_H) #ifdef HAVE_SNMP_VERSION_H #include #endif /* HAVE_SNMP_VERSION_H */ #endif /* SNMP */ #ifdef NEED_STRERROR_H #include "strerror.h" #endif #include "main.h" #include "timestamp.h" #include "packet.h" #include "capture.h" #include "summary.h" #include "file.h" #include "menu.h" #include "../menu.h" #include "prefs_dlg.h" #include "column.h" #include "print.h" #include "resolv.h" #include "follow.h" #include "util.h" #include "simple_dialog.h" #include "proto_draw.h" #include "dfilter.h" #include "keys.h" #include "gtkglobals.h" #include "plugins.h" FILE *data_out_file = NULL; packet_info pi; capture_file cf; GtkWidget *top_level, *file_sel, *packet_list, *tree_view, *byte_view, *prog_bar, *info_bar, *tv_scrollw, *pkt_scrollw; static GtkWidget *bv_vscroll_left, *bv_vscroll_right; GdkFont *m_r_font, *m_b_font; guint main_ctx, file_ctx; gchar comp_info_str[256]; gchar *ethereal_path = NULL; gchar *medium_font = MONO_MEDIUM_FONT; gchar *bold_font = MONO_BOLD_FONT; gchar *last_open_dir = NULL; ts_type timestamp_type = RELATIVE; GtkStyle *item_style; /* Specifies the field currently selected in the GUI protocol tree */ field_info *finfo_selected = NULL; static void follow_destroy_cb(GtkWidget *win, gpointer data); static void follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w); static void follow_load_text(GtkWidget *text, char *filename, guint8 show_type); static void follow_print_stream(GtkWidget *w, gpointer parent_w); static char* hfinfo_numeric_format(header_field_info *hfinfo); static void create_main_window(gint, gint, gint, e_prefs*); /* About Ethereal window */ void about_ethereal( GtkWidget *w, gpointer data ) { simple_dialog(ESD_TYPE_INFO, NULL, "Ethereal - Network Protocol Analyzer\n" "Version " VERSION " (C) 1998-2000 Gerald Combs \n" "Compiled with %s\n\n" "Check the man page for complete documentation and\n" "for the list of contributors.\n" "\nSee http://ethereal.zing.org/ for more information.", comp_info_str); } /* Follow the TCP stream, if any, to which the last packet that we called a dissection routine on belongs (this might be the most recently selected packet, or it might be the last packet in the file). */ void follow_stream_cb( GtkWidget *w, gpointer data ) { char filename1[128+1]; GtkWidget *streamwindow, *box, *text, *vscrollbar, *table, *filter_te; GtkWidget *hbox, *close_bt, *print_bt; GtkWidget *b_ascii, *b_ebcdic, *b_hexdump; int tmp_fd; gchar *follow_filter; if( pi.ipproto == 6 ) { /* we got tcp so we can follow */ /* Create a temporary file into which to dump the reassembled data from the TCP stream, and set "data_out_file" to refer to it, so that the TCP code will write to it. XXX - it might be nicer to just have the TCP code directly append stuff to the text widget for the TCP stream window, if we can arrange that said window not pop up until we're done. */ tmp_fd = create_tempfile( filename1, sizeof filename1, "follow"); if (tmp_fd == -1) { simple_dialog(ESD_TYPE_WARN, NULL, "Could not create temporary file %s: %s", filename1, strerror(errno)); return; } data_out_file = fdopen( tmp_fd, "w" ); if( data_out_file == NULL ) { simple_dialog(ESD_TYPE_WARN, NULL, "Could not create temporary file %s: %s", filename1, strerror(errno)); close(tmp_fd); unlink(filename1); return; } /* Create a new filter that matches all packets in the TCP stream, and set the display filter entry accordingly */ reset_tcp_reassembly(); follow_filter = build_follow_filter( &pi ); /* set the display filter entry accordingly */ filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY); gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter); /* Run the display filter so it goes in effect. */ filter_packets(&cf, follow_filter); /* the data_out_file should now be full of the streams information */ fclose( data_out_file ); /* the filename1 file now has all the text that was in the session */ streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL); gtk_widget_set_name( streamwindow, "TCP stream window" ); gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event", GTK_SIGNAL_FUNC(follow_destroy_cb), NULL); gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy", GTK_SIGNAL_FUNC(follow_destroy_cb), NULL); if( incomplete_tcp_stream ) { gtk_window_set_title( GTK_WINDOW(streamwindow), "Contents of TCP stream (incomplete)" ); } else { gtk_window_set_title( GTK_WINDOW(streamwindow), "Contents of TCP stream" ); } gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT ); gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 ); /* setup the container */ box = gtk_vbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER(streamwindow), box ); gtk_widget_show( box ); /* set up the table we attach to */ table = gtk_table_new( 1, 2, FALSE ); gtk_table_set_col_spacing( GTK_TABLE(table), 0, 2); gtk_box_pack_start( GTK_BOX(box), table, TRUE, TRUE, 0 ); gtk_widget_show( table ); /* create a text box */ text = gtk_text_new( NULL, NULL ); gtk_text_set_editable( GTK_TEXT(text), FALSE); gtk_table_attach( GTK_TABLE(table), text, 0, 1, 0, 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 ); gtk_widget_show(text); /* Create hbox */ hbox = gtk_hbox_new( FALSE, 1 ); gtk_box_pack_end( GTK_BOX(box), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); #define E_FOLLOW_ASCII_KEY "follow_ascii_key" #define E_FOLLOW_EBCDIC_KEY "follow_ebcdic_key" #define E_FOLLOW_HEXDUMP_KEY "follow_hexdump_key" /* Create Radio Buttons */ b_ascii = gtk_radio_button_new_with_label(NULL, "ASCII"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_ascii), TRUE); gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_ASCII_KEY, b_ascii); gtk_box_pack_start(GTK_BOX(hbox), b_ascii, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(b_ascii), "toggled", GTK_SIGNAL_FUNC(follow_charset_toggle_cb), GTK_OBJECT(streamwindow)); gtk_widget_show(b_ascii); b_ebcdic = gtk_radio_button_new_with_label( gtk_radio_button_group(GTK_RADIO_BUTTON(b_ascii)), "EBCDIC"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_ebcdic), FALSE); gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_EBCDIC_KEY, b_ebcdic); gtk_box_pack_start(GTK_BOX(hbox), b_ebcdic, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(b_ebcdic), "toggled", GTK_SIGNAL_FUNC(follow_charset_toggle_cb), GTK_OBJECT(streamwindow)); gtk_widget_show(b_ebcdic); b_hexdump = gtk_radio_button_new_with_label( gtk_radio_button_group(GTK_RADIO_BUTTON(b_ascii)), "Hex. Dump"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_hexdump), FALSE); gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_HEXDUMP_KEY, b_hexdump); gtk_box_pack_start(GTK_BOX(hbox), b_hexdump, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(b_hexdump), "toggled", GTK_SIGNAL_FUNC(follow_charset_toggle_cb), GTK_OBJECT(streamwindow)); gtk_widget_show(b_hexdump); /* Create Close Button */ close_bt = gtk_button_new_with_label("Close"); gtk_signal_connect_object(GTK_OBJECT(close_bt), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(streamwindow)); gtk_box_pack_end( GTK_BOX(hbox), close_bt, FALSE, FALSE, 0); gtk_widget_show( close_bt ); /* Create Print Button */ print_bt = gtk_button_new_with_label("Print"); gtk_signal_connect(GTK_OBJECT(print_bt), "clicked", GTK_SIGNAL_FUNC(follow_print_stream), GTK_OBJECT(streamwindow)); gtk_box_pack_end( GTK_BOX(hbox), print_bt, FALSE, FALSE, 0); gtk_widget_show( print_bt ); /* create the scrollbar */ vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj ); gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1, GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 ); gtk_widget_show( vscrollbar ); gtk_widget_realize( text ); /* Tuck away the filename and textbox into streamwindow */ #define E_FOLLOW_FILENAME_KEY "follow_filename_key" #define E_FOLLOW_TEXT_KEY "follow_text_key" gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_FILENAME_KEY, g_strdup(filename1)); gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_TEXT_KEY, text); follow_load_text(text, filename1, 0); data_out_file = NULL; gtk_widget_show( streamwindow ); } else { simple_dialog(ESD_TYPE_WARN, NULL, "Error following stream. Please make\n" "sure you have a TCP packet selected."); } } /* The destroy call back has the responsibility of * unlinking the temporary file */ static void follow_destroy_cb(GtkWidget *win, gpointer data) { char *filename; filename = (char*) gtk_object_get_data(GTK_OBJECT(win), E_FOLLOW_FILENAME_KEY); g_assert(filename); unlink(filename); gtk_widget_destroy(win); } #define E_FOLLOW_ASCII_TYPE 0 #define E_FOLLOW_EBCDIC_TYPE 1 #define E_FOLLOW_HEXDUMP_TYPE 2 /* Handles the ASCII/EBCDIC toggling */ static void follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w) { guint8 show_type = E_FOLLOW_ASCII_TYPE; GtkWidget *b_ascii, *b_ebcdic, *b_hexdump, *text; char *filename; b_ascii = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_ASCII_KEY); b_ebcdic = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_EBCDIC_KEY); b_hexdump = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_HEXDUMP_KEY); text = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_TEXT_KEY); filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_FILENAME_KEY); g_assert(b_ascii); g_assert(b_ebcdic); g_assert(b_hexdump); g_assert(text); g_assert(filename); if (GTK_TOGGLE_BUTTON(b_ebcdic)->active) show_type = E_FOLLOW_EBCDIC_TYPE; else if (GTK_TOGGLE_BUTTON(b_hexdump)->active) show_type = E_FOLLOW_HEXDUMP_TYPE; follow_load_text(text, filename, show_type); } #define FLT_BUF_SIZE 1024 static void follow_read_stream(char *filename, guint8 show_type, void (*print_line)(char *, int, gboolean, void *), void *arg) { tcp_stream_chunk sc; int bcount; guint32 client_addr = 0; guint16 client_port = 0; gboolean is_server; guint16 current_pos, global_client_pos = 0, global_server_pos = 0; guint16 *global_pos; data_out_file = fopen( filename, "r" ); if( data_out_file ) { char buffer[FLT_BUF_SIZE]; int nchars; while(fread(&sc.src_addr, 1, sizeof(sc), data_out_file)) { if (client_addr == 0) { client_addr = sc.src_addr; client_port = sc.src_port; } if (client_addr == sc.src_addr && client_port == sc.src_port) { is_server = FALSE; global_pos = &global_client_pos; } else { is_server = TRUE; global_pos = &global_server_pos; } while (sc.dlen > 0) { bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE; nchars = fread( buffer, 1, bcount, data_out_file ); if (nchars == 0) break; sc.dlen -= bcount; switch (show_type) { case E_FOLLOW_EBCDIC_TYPE: /* If our native arch is ASCII, call: */ EBCDIC_to_ASCII(buffer, nchars); case E_FOLLOW_ASCII_TYPE: /* If our native arch is EBCDIC, call: * ASCII_TO_EBCDIC(buffer, nchars); */ (*print_line)( buffer, nchars, is_server, arg ); break; case E_FOLLOW_HEXDUMP_TYPE: current_pos = 0; while (current_pos < nchars) { gchar hexbuf[256]; gchar hexchars[] = "0123456789abcdef"; int i, cur; /* is_server indentation : put 63 spaces at the begenning * of the string */ sprintf(hexbuf, is_server ? " " " %08X " : "%08X ", *global_pos); cur = strlen(hexbuf); for (i=0; i < 16 && current_pos+i < nchars; i++) { hexbuf[cur++] = hexchars[(buffer[current_pos+i] & 0xf0) >> 4]; hexbuf[cur++] = hexchars[buffer[current_pos+i] & 0x0f]; if (i == 7) { hexbuf[cur++] = ' '; hexbuf[cur++] = ' '; } else if (i != 15) hexbuf[cur++] = ' '; } current_pos += i; (*global_pos) += i; hexbuf[cur++] = '\n'; hexbuf[cur] = 0; (*print_line)( hexbuf, strlen(hexbuf), is_server, arg ); } break; } } } if( ferror( data_out_file ) ) { simple_dialog(ESD_TYPE_WARN, NULL, "Error reading temporary file %s: %s", filename, strerror(errno)); } fclose( data_out_file ); data_out_file = NULL; } else { simple_dialog(ESD_TYPE_WARN, NULL, "Could not open temporary file %s: %s", filename, strerror(errno)); } } /* * XXX - for text printing, we probably want to wrap lines at 80 characters; * for PostScript printing, we probably want to wrap them at the appropriate * width, and perhaps put some kind of dingbat (to use the technical term) * to indicate a wrapped line, along the lines of what's done when displaying * this in a window, as per Warren Young's suggestion. * * For now, we support only text printing. */ static void follow_print_text(char *buffer, int nchars, gboolean is_server, void *arg) { FILE *fh = arg; fwrite(buffer, nchars, 1, fh); } static void follow_print_stream(GtkWidget *w, gpointer parent_w) { FILE *fh; gboolean to_file; char* print_dest; char* filename; guint8 show_type = E_FOLLOW_ASCII_TYPE; GtkWidget *button; switch (prefs.pr_dest) { case PR_DEST_CMD: print_dest = prefs.pr_cmd; to_file = FALSE; break; case PR_DEST_FILE: print_dest = prefs.pr_file; to_file = TRUE; break; default: /* "Can't happen" */ simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't figure out where to send the print " "job. Check your preferences."); return; } fh = open_print_dest(to_file, print_dest); if (fh == NULL) { switch (to_file) { case FALSE: simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't run print command %s.", prefs.pr_cmd); break; case TRUE: simple_dialog(ESD_TYPE_WARN, NULL, file_write_error_message(errno), prefs.pr_file); break; } return; } button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_EBCDIC_KEY); if (GTK_TOGGLE_BUTTON(button)->active) show_type = E_FOLLOW_EBCDIC_TYPE; button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_HEXDUMP_KEY); if (GTK_TOGGLE_BUTTON(button)->active) show_type = E_FOLLOW_HEXDUMP_TYPE; filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w), E_FOLLOW_FILENAME_KEY); if (filename != NULL) { print_preamble(fh, PR_FMT_TEXT); follow_read_stream(filename, show_type, follow_print_text, fh); print_finale(fh, PR_FMT_TEXT); close_print_dest(to_file, fh); } else { simple_dialog(ESD_TYPE_WARN, NULL, "Could not find data to print."); } } static void follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server, void *arg) { GtkWidget *text = arg; if (is_server) gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_server_fg, &prefs.st_server_bg, buffer, nchars ); else gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_client_fg, &prefs.st_client_bg, buffer, nchars ); } static void follow_load_text(GtkWidget *text, char *filename, guint8 show_type) { int bytes_already; /* Delete any info already in text box */ bytes_already = gtk_text_get_length(GTK_TEXT(text)); if (bytes_already > 0) { gtk_text_set_point(GTK_TEXT(text), 0); gtk_text_forward_delete(GTK_TEXT(text), bytes_already); } /* stop the updates while we fill the text box */ gtk_text_freeze( GTK_TEXT(text) ); follow_read_stream(filename, show_type, follow_add_to_gtk_text, text); gtk_text_thaw( GTK_TEXT(text) ); } /* Match selected byte pattern */ void match_selected_cb(GtkWidget *w, gpointer data) { char *buf; GtkWidget *filter_te; char *ptr, *format, *stringified; int i, dfilter_len, abbrev_len; guint8 *c; header_field_info *hfinfo; filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY); if (!finfo_selected) { simple_dialog(ESD_TYPE_WARN, NULL, "Error determining selected bytes. Please make\n" "sure you have selected a field within the tree\n" "view to be matched."); return; } hfinfo = finfo_selected->hfinfo; g_assert(hfinfo); abbrev_len = strlen(hfinfo->abbrev); switch(hfinfo->type) { case FT_BOOLEAN: dfilter_len = abbrev_len + 2; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!", hfinfo->abbrev); break; case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: case FT_INT8: case FT_INT16: case FT_INT24: case FT_INT32: dfilter_len = abbrev_len + 20; buf = g_malloc0(dfilter_len); format = hfinfo_numeric_format(hfinfo); snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric); break; case FT_IPv4: dfilter_len = abbrev_len + 4 + 15 + 1; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev, ipv4_addr_str(&(finfo_selected->value.ipv4))); break; case FT_IPXNET: dfilter_len = abbrev_len + 15; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev, finfo_selected->value.numeric); break; case FT_IPv6: stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6)); dfilter_len = abbrev_len + 4 + strlen(stringified) + 1; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev, stringified); break; case FT_DOUBLE: dfilter_len = abbrev_len + 30; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev, finfo_selected->value.floating); break; case FT_ETHER: dfilter_len = abbrev_len + 22; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev, ether_to_str(finfo_selected->value.ether)); break; #if 0 case FT_ABSOLUTE_TIME: case FT_RELATIVE_TIME: memcpy(&fi->value.time, va_arg(ap, struct timeval*), sizeof(struct timeval)); break; case FT_STRING: /* This g_strdup'ed memory is freed in proto_tree_free_node() */ fi->value.string = g_strdup(va_arg(ap, char*)); break; case FT_TEXT_ONLY: ; /* nothing */ break; #endif default: c = cf.pd + finfo_selected->start; buf = g_malloc0(32 + finfo_selected->length * 3); ptr = buf; sprintf(ptr, "frame[%d] == ", finfo_selected->start); ptr = buf+strlen(buf); if (finfo_selected->length == 1) { sprintf(ptr, "0x%02x", *c++); } else { for (i=0;ilength; i++) { if (i == 0 ) { sprintf(ptr, "%02x", *c++); } else { sprintf(ptr, ":%02x", *c++); } ptr = buf+strlen(buf); } } break; } /* create a new one and set the display filter entry accordingly */ gtk_entry_set_text(GTK_ENTRY(filter_te), buf); /* Run the display filter so it goes in effect. */ filter_packets(&cf, buf); /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */ } static char* hfinfo_numeric_format(header_field_info *hfinfo) { char *format = NULL; /* Pick the proper format string */ switch(hfinfo->display) { case BASE_DEC: case BASE_NONE: case BASE_OCT: /* I'm lazy */ case BASE_BIN: /* I'm lazy */ switch(hfinfo->type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: format = "%s == %u"; break; case FT_INT8: case FT_INT16: case FT_INT24: case FT_INT32: format = "%s == %d"; break; default: g_assert_not_reached(); ; } break; case BASE_HEX: switch(hfinfo->type) { case FT_UINT8: format = "%s == 0x%02x"; break; case FT_UINT16: format = "%s == 0x%04x"; break; case FT_UINT24: format = "%s == 0x%06x"; break; case FT_UINT32: format = "%s == 0x%08x"; break; default: g_assert_not_reached(); ; } break; default: g_assert_not_reached(); ; } return format; } /* Run the current display filter on the current packet set, and redisplay. */ static void filter_activate_cb(GtkWidget *w, gpointer data) { GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY); GList *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY); GList *li, *nl = NULL; gboolean add_filter = TRUE; char *s = gtk_entry_get_text(GTK_ENTRY(w)); /* GtkCombos don't let us get at their list contents easily, so we maintain our own filter list, and feed it to gtk_combo_set_popdown_strings when a new filter is added. */ if (filter_packets(&cf, g_strdup(s))) { li = g_list_first(filter_list); while (li) { if (li->data && strcmp(s, li->data) == 0) add_filter = FALSE; li = li->next; } if (add_filter) { filter_list = g_list_append(filter_list, g_strdup(s)); li = g_list_first(filter_list); while (li) { nl = g_list_append(nl, strdup(li->data)); li = li->next; } gtk_combo_set_popdown_strings(filter_cm, nl); gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data); } } } /* redisplay with no display filter */ static void filter_reset_cb(GtkWidget *w, gpointer data) { GtkWidget *filter_te = NULL; if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) { gtk_entry_set_text(GTK_ENTRY(filter_te), ""); } filter_packets(&cf, NULL); } /* What to do when a list item is selected/unselected */ static void packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) { #ifdef HAVE_LIBPCAP if (!sync_mode) { #endif if (cf.wth) return; #ifdef HAVE_LIBPCAP } #endif blank_packetinfo(); select_packet(&cf, row); } static void packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) { unselect_packet(&cf); } static void tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data) { field_info *finfo; int tree_selected_start = -1; int tree_selected_len = -1; g_assert(node); finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) ); if (!finfo) return; finfo_selected = finfo; tree_selected_start = finfo->start; tree_selected_len = finfo->length; packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len, tree_selected_start, tree_selected_len, cf.current_frame->encoding); } static void tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data) { finfo_selected = NULL; packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len, -1, -1, cf.current_frame->encoding); } void collapse_all_cb(GtkWidget *widget, gpointer data) { if (cf.protocol_tree) collapse_all_tree(cf.protocol_tree, tree_view); } void expand_all_cb(GtkWidget *widget, gpointer data) { if (cf.protocol_tree) expand_all_tree(cf.protocol_tree, tree_view); } void set_scrollbar_placement(int pos) /* 0=left, 1=right */ { if (pos) { gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(pkt_scrollw), GTK_CORNER_TOP_LEFT ); gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(tv_scrollw), GTK_CORNER_TOP_LEFT ); gtk_widget_hide(bv_vscroll_left); gtk_widget_show(bv_vscroll_right); } else { gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(pkt_scrollw), GTK_CORNER_TOP_RIGHT ); gtk_scrolled_window_set_placement( GTK_SCROLLED_WINDOW(tv_scrollw), GTK_CORNER_TOP_RIGHT ); gtk_widget_hide(bv_vscroll_right); gtk_widget_show(bv_vscroll_left); } } void set_plist_sel_browse(gboolean val) { if (finfo_selected) unselect_packet(&cf); /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */ if (val) { gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE); } else { gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE); } } void set_ptree_sel_browse(gboolean val) { /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */ if (val) { gtk_clist_set_selection_mode(GTK_CLIST(tree_view), GTK_SELECTION_SINGLE); } else { gtk_clist_set_selection_mode(GTK_CLIST(tree_view), GTK_SELECTION_BROWSE); } } void set_ptree_line_style(gint style) { /* I'm using an assert here since the preferences code limits * the user input, both in the GUI and when reading the preferences file. * If the value is incorrect, it's a program error, not a user-initiated error. */ g_assert(style >= GTK_CTREE_LINES_NONE && style <= GTK_CTREE_LINES_TABBED); gtk_ctree_set_line_style( GTK_CTREE(tree_view), style ); } void set_ptree_expander_style(gint style) { /* I'm using an assert here since the preferences code limits * the user input, both in the GUI and when reading the preferences file. * If the value is incorrect, it's a program error, not a user-initiated error. */ g_assert(style >= GTK_CTREE_EXPANDER_NONE && style <= GTK_CTREE_EXPANDER_CIRCULAR); gtk_ctree_set_expander_style( GTK_CTREE(tree_view), style ); } void file_quit_cmd_cb (GtkWidget *widget, gpointer data) { /* If we have a capture file open, and it's a temporary file, unlink it. */ if (cf.filename != NULL && cf.is_tempfile) unlink(cf.filename); gtk_exit(0); } /* call initialization routines at program startup time */ static void ethereal_proto_init(void) { init_dissect_rpc(); proto_init(); init_dissect_udp(); dfilter_init(); #ifdef HAVE_PLUGINS init_plugins(); #endif } static void ethereal_proto_cleanup(void) { proto_cleanup(); dfilter_cleanup(); } static void print_usage(void) { fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n", comp_info_str); #ifdef HAVE_LIBPCAP fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b ] [ -B ]\n", PACKAGE); fprintf(stderr, "\t[ -c count ] [ -D ] [ -f ] [ -i interface ]\n"); fprintf(stderr, "\t[ -m ] [ -n ] [ -P ] [ -r infile ]\n"); fprintf(stderr, "\t[ -R ] [ -s snaplen ] [ -t