3 * $Id: main.c,v 1.48 1999/11/28 14:50:23 gram Exp $
5 * Ethereal - Network traffic analyzer
6 * By Gerald Combs <gerald@zing.org>
7 * Copyright 1998 Gerald Combs
9 * Richard Sharpe, 13-Feb-1999, added support for initializing structures
10 * needed by dissect routines
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 * - Check for end of packet in dissect_* routines.
31 * - Multiple window support
32 * - Add cut/copy/paste
33 * - Create header parsing routines
34 * - Make byte view scrollbars automatic?
35 * - Make byte view selections more fancy?
54 #include <sys/types.h>
61 #ifdef HAVE_NETINET_IN_H
62 #include <netinet/in.h>
67 #ifdef NEED_SNPRINTF_H
73 # include "snprintf.h"
76 #ifdef NEED_STRERROR_H
81 #include "timestamp.h"
87 #include "prefs_dlg.h"
93 #include "proto_draw.h"
97 FILE *data_out_file = NULL;
100 GtkWidget *file_sel, *packet_list, *tree_view, *byte_view, *prog_bar,
102 GdkFont *m_r_font, *m_b_font;
103 guint main_ctx, file_ctx;
104 gchar comp_info_str[256];
105 gchar *ethereal_path = NULL;
106 gchar *medium_font = MONO_MEDIUM_FONT;
107 gchar *bold_font = MONO_BOLD_FONT;
108 gchar *last_open_dir = NULL;
110 ts_type timestamp_type = RELATIVE;
112 GtkStyle *item_style;
114 /* Specifies the field currently selected in the GUI protocol tree */
115 field_info *finfo_selected = NULL;
117 static void follow_destroy_cb(GtkWidget *win, gpointer data);
118 static void follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w);
119 static void follow_load_text(GtkWidget *text, char *filename, gboolean show_ascii);
120 static void follow_print_stream(GtkWidget *w, gpointer parent_w);
121 static char* hfinfo_numeric_format(header_field_info *hfinfo);
123 /* About Ethereal window */
125 about_ethereal( GtkWidget *w, gpointer data ) {
126 simple_dialog(ESD_TYPE_INFO, NULL,
127 "GNU Ethereal - network protocol analyzer\n"
128 "Version %s (C) 1998 Gerald Combs <gerald@zing.org>\n"
129 "Compiled with %s\n\n"
132 "Gilbert Ramirez <gramirez@tivoli.com>\n"
133 "Hannes R. Boehm <hannes@boehm.org>\n"
134 "Mike Hall <mlh@io.com>\n"
135 "Bobo Rajec <bobo@bsp-consulting.sk>\n"
136 "Laurent Deniel <deniel@worldnet.fr>\n"
137 "Don Lafontaine <lafont02@cn.ca>\n"
138 "Guy Harris <guy@alum.mit.edu>\n"
139 "Simon Wilkinson <sxw@dcs.ed.ac.uk>\n"
140 "Joerg Mayer <jmayer@telemation.de>\n"
141 "Martin Maciaszek <fastjack@i-s-o.net>\n"
142 "Didier Jorand <Didier.Jorand@alcatel.fr>\n"
143 "Jun-ichiro itojun Hagino <itojun@iijlab.net>\n"
144 "Richard Sharpe <sharpe@ns.aus.com>\n"
145 "John McDermott <jjm@jkintl.com>\n"
146 "Jeff Jahr <jjahr@shastanets.com>\n"
147 "Brad Robel-Forrest <bradr@watchguard.com>\n"
148 "Ashok Narayanan <ashokn@cisco.com>\n"
149 "Aaron Hillegass <aaron@classmax.com>\n"
150 "Jason Lango <jal@netapp.com>\n"
151 "Johan Feyaerts <Johan.Feyaerts@siemens.atea.be>\n"
152 "Olivier Abad <abad@daba.dhis.org>\n"
153 "Thierry Andry <Thierry.Andry@advalvas.be>\n"
154 "Jeff Foster <jfoste@woodward.com>\n"
155 "Peter Torvals <petertv@xoommail.com>\n"
156 "Christophe Tronche <ch.tronche@computer.org>\n"
157 "Nathan Neulinger <nneul@umr.edu>\n"
158 "Tomislav Vujec <tvujec@carnet.hr>\n"
159 "Kojak <kojak@bigwig.net>\n"
160 "Uwe Girlich <Uwe.Girlich@philosys.de>\n"
161 "Warren Young <tangent@mail.com>\n"
162 "Heikki Vatiainen <hessu@cs.tut.fi>\n"
163 "Greg Hankins <gregh@twoguys.org>\n"
165 "\nSee http://ethereal.zing.org for more information",
166 VERSION, comp_info_str);
169 /* Follow the TCP stream, if any, to which the last packet that we called
170 a dissection routine on belongs (this might be the most recently
171 selected packet, or it might be the last packet in the file). */
173 follow_stream_cb( GtkWidget *w, gpointer data ) {
174 char filename1[128+1];
175 GtkWidget *streamwindow, *box, *text, *vscrollbar, *table,
177 GtkWidget *hbox, *close_bt, *print_bt, *button;
179 gchar *follow_filter;
181 if( pi.ipproto == 6 ) {
182 /* we got tcp so we can follow */
183 /* Create a temporary file into which to dump the reassembled data
184 from the TCP stream, and set "data_out_file" to refer to it, so
185 that the TCP code will write to it.
187 XXX - it might be nicer to just have the TCP code directly
188 append stuff to the text widget for the TCP stream window,
189 if we can arrange that said window not pop up until we're
191 tmp_fd = create_tempfile( filename1, sizeof filename1, "follow");
193 simple_dialog(ESD_TYPE_WARN, NULL,
194 "Could not create temporary file %s: %s", filename1, strerror(errno));
197 data_out_file = fdopen( tmp_fd, "w" );
198 if( data_out_file == NULL ) {
199 simple_dialog(ESD_TYPE_WARN, NULL,
200 "Could not create temporary file %s: %s", filename1, strerror(errno));
206 /* Create a new filter that matches all packets in the TCP stream,
207 and set the display filter entry accordingly */
208 reset_tcp_reassembly();
209 follow_filter = build_follow_filter( &pi );
211 /* set the display filter entry accordingly */
212 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
213 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
215 /* Run the display filter so it goes in effect. */
216 filter_packets(&cf, follow_filter);
218 /* the data_out_file should now be full of the streams information */
219 fclose( data_out_file );
221 /* the filename1 file now has all the text that was in the session */
222 streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL);
223 gtk_widget_set_name( streamwindow, "TCP stream window" );
225 gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event",
226 GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
227 gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
228 GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
230 if( incomplete_tcp_stream ) {
231 gtk_window_set_title( GTK_WINDOW(streamwindow),
232 "Contents of TCP stream (incomplete)" );
234 gtk_window_set_title( GTK_WINDOW(streamwindow),
235 "Contents of TCP stream" );
237 gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT );
238 gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 );
240 /* setup the container */
241 box = gtk_vbox_new( FALSE, 0 );
242 gtk_container_add( GTK_CONTAINER(streamwindow), box );
243 gtk_widget_show( box );
245 /* set up the table we attach to */
246 table = gtk_table_new( 1, 2, FALSE );
247 gtk_table_set_col_spacing( GTK_TABLE(table), 0, 2);
248 gtk_box_pack_start( GTK_BOX(box), table, TRUE, TRUE, 0 );
249 gtk_widget_show( table );
251 /* create a text box */
252 text = gtk_text_new( NULL, NULL );
253 gtk_text_set_editable( GTK_TEXT(text), FALSE);
254 gtk_table_attach( GTK_TABLE(table), text, 0, 1, 0, 1,
255 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
256 GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
257 gtk_widget_show(text);
260 hbox = gtk_hbox_new( FALSE, 1 );
261 gtk_box_pack_end( GTK_BOX(box), hbox, FALSE, FALSE, 0);
262 gtk_widget_show(hbox);
264 #define E_FOLLOW_ASCII_KEY "follow_ascii_key"
266 /* Create Radio Buttons */
267 button = gtk_radio_button_new_with_label(NULL, "ASCII");
268 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
269 gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_ASCII_KEY, button);
270 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
271 gtk_signal_connect(GTK_OBJECT(button), "toggled",
272 GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
273 GTK_OBJECT(streamwindow));
274 gtk_widget_show(button);
276 button = gtk_radio_button_new_with_label(
277 gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
279 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
280 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
281 gtk_widget_show(button);
283 /* Create Close Button */
284 close_bt = gtk_button_new_with_label("Close");
285 gtk_signal_connect_object(GTK_OBJECT(close_bt), "clicked",
286 GTK_SIGNAL_FUNC(gtk_widget_destroy),
287 GTK_OBJECT(streamwindow));
288 gtk_box_pack_end( GTK_BOX(hbox), close_bt, FALSE, FALSE, 0);
289 gtk_widget_show( close_bt );
291 /* Create Print Button */
292 print_bt = gtk_button_new_with_label("Print");
293 gtk_signal_connect(GTK_OBJECT(print_bt), "clicked",
294 GTK_SIGNAL_FUNC(follow_print_stream),
295 GTK_OBJECT(streamwindow));
296 gtk_box_pack_end( GTK_BOX(hbox), print_bt, FALSE, FALSE, 0);
297 gtk_widget_show( print_bt );
299 /* create the scrollbar */
300 vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj );
301 gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1,
302 GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
303 gtk_widget_show( vscrollbar );
304 gtk_widget_realize( text );
306 /* Tuck away the filename and textbox into streamwindow */
307 #define E_FOLLOW_FILENAME_KEY "follow_filename_key"
308 #define E_FOLLOW_TEXT_KEY "follow_text_key"
310 gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_FILENAME_KEY,
311 g_strdup(filename1));
312 gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_TEXT_KEY, text);
314 follow_load_text(text, filename1, TRUE);
316 data_out_file = NULL;
317 gtk_widget_show( streamwindow );
319 simple_dialog(ESD_TYPE_WARN, NULL,
320 "Error following stream. Please make\n"
321 "sure you have a TCP packet selected.");
325 /* The destroy call back has the responsibility of
326 * unlinking the temporary file */
328 follow_destroy_cb(GtkWidget *win, gpointer data)
332 filename = (char*) gtk_object_get_data(GTK_OBJECT(win),
333 E_FOLLOW_FILENAME_KEY);
337 gtk_widget_destroy(win);
340 /* Handles the ASCII/EBCDIC toggling */
342 follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w)
344 gboolean show_ascii = FALSE;
345 GtkWidget *button, *text;
349 button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
351 text = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
353 filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
354 E_FOLLOW_FILENAME_KEY);
360 if (GTK_TOGGLE_BUTTON(button)->active)
363 follow_load_text(text, filename, show_ascii);
366 static void follow_print_stream(GtkWidget *w, gpointer parent_w)
370 char* print_dest = NULL;
373 switch (prefs.pr_dest) {
375 print_dest = prefs.pr_cmd;
380 print_dest = prefs.pr_file;
385 if (print_dest != NULL) {
386 fh = open_print_dest(to_file, print_dest);
392 simple_dialog(ESD_TYPE_WARN, NULL,
393 "Couldn't figure out where to send the print "
394 "job. Check your preferences.");
398 simple_dialog(ESD_TYPE_WARN, NULL,
399 "Couldn't run print command %s.", prefs.pr_cmd);
403 simple_dialog(ESD_TYPE_WARN, NULL,
404 file_write_error_message(errno),
411 filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
412 E_FOLLOW_FILENAME_KEY);
414 if (filename != NULL) {
416 print_file(fh, filename);
418 close_print_dest(to_file, fh);
421 simple_dialog(ESD_TYPE_WARN, NULL, "Could not find data to print.");
425 #define FLT_BUF_SIZE 1024
427 follow_load_text(GtkWidget *text, char *filename, gboolean show_ascii)
429 int bytes_already, bcount;
431 guint32 client_addr = 0;
432 guint16 client_port = 0;
433 GdkColor client = { 0, 16383, 0, 0 };
434 GdkColor server = { 0, 0, 0, 16383 };
436 /* Delete any info already in text box */
437 bytes_already = gtk_text_get_length(GTK_TEXT(text));
438 if (bytes_already > 0) {
439 gtk_text_set_point(GTK_TEXT(text), 0);
440 gtk_text_forward_delete(GTK_TEXT(text), bytes_already);
443 /* stop the updates while we fill the text box */
444 gtk_text_freeze( GTK_TEXT(text) );
445 data_out_file = fopen( filename, "r" );
446 if( data_out_file ) {
447 char buffer[FLT_BUF_SIZE];
449 while(fread(&sc.src_addr, 1, sizeof(sc), data_out_file)) {
450 if (client_addr == 0) {
451 client_addr = sc.src_addr;
452 client_port = sc.src_port;
455 while (sc.dlen > 0) {
456 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
457 nchars = fread( buffer, 1, bcount, data_out_file );
462 /* If our native arch is EBCDIC, call:
463 * ASCII_TO_EBCDIC(buffer, nchars);
467 /* If our native arch is ASCII, call: */
468 EBCDIC_to_ASCII(buffer, nchars);
470 if (client_addr == sc.src_addr && client_port == sc.src_port)
471 gtk_text_insert( GTK_TEXT(text), m_r_font, &client, NULL, buffer, nchars );
473 gtk_text_insert( GTK_TEXT(text), m_r_font, &server, NULL, buffer, nchars );
476 if( ferror( data_out_file ) ) {
477 simple_dialog(ESD_TYPE_WARN, NULL,
478 "Error reading temporary file %s: %s", filename, strerror(errno));
480 fclose( data_out_file );
482 simple_dialog(ESD_TYPE_WARN, NULL,
483 "Could not open temporary file %s: %s", filename, strerror(errno));
485 gtk_text_thaw( GTK_TEXT(text) );
488 /* Match selected byte pattern */
490 match_selected_cb(GtkWidget *w, gpointer data)
493 GtkWidget *filter_te;
494 char *ptr, *format, *stringified;
495 int i, dfilter_len, abbrev_len;
497 header_field_info *hfinfo;
499 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
501 if (!finfo_selected) {
502 simple_dialog(ESD_TYPE_WARN, NULL,
503 "Error determining selected bytes. Please make\n"
504 "sure you have selected a field within the tree\n"
505 "view to be matched.");
509 hfinfo = finfo_selected->hfinfo;
511 abbrev_len = strlen(hfinfo->abbrev);
513 switch(hfinfo->type) {
516 dfilter_len = abbrev_len + 2;
517 buf = g_malloc0(dfilter_len);
518 snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
530 dfilter_len = abbrev_len + 20;
531 buf = g_malloc0(dfilter_len);
532 format = hfinfo_numeric_format(hfinfo);
533 snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
537 dfilter_len = abbrev_len + 4 + 15 + 1;
538 buf = g_malloc0(dfilter_len);
539 snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
540 ipv4_addr_str(&(finfo_selected->value.ipv4)));
544 dfilter_len = abbrev_len + 15;
545 buf = g_malloc0(dfilter_len);
546 snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
547 finfo_selected->value.numeric);
551 stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
552 dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
553 buf = g_malloc0(dfilter_len);
554 snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
559 dfilter_len = abbrev_len + 30;
560 buf = g_malloc0(dfilter_len);
561 snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
562 finfo_selected->value.floating);
566 dfilter_len = abbrev_len + 22;
567 buf = g_malloc0(dfilter_len);
568 snprintf(buf, dfilter_len, "%s == %s",
570 ether_to_str(finfo_selected->value.ether));
574 case FT_ABSOLUTE_TIME:
575 case FT_RELATIVE_TIME:
576 memcpy(&fi->value.time, va_arg(ap, struct timeval*),
577 sizeof(struct timeval));
581 /* This g_strdup'ed memory is freed in proto_tree_free_node() */
582 fi->value.string = g_strdup(va_arg(ap, char*));
590 c = cf.pd + finfo_selected->start;
591 buf = g_malloc0(32 + finfo_selected->length * 3);
594 sprintf(ptr, "frame[%d] == ", finfo_selected->start);
595 ptr = buf+strlen(buf);
597 if (finfo_selected->length == 1) {
598 sprintf(ptr, "0x%02x", *c++);
601 for (i=0;i<finfo_selected->length; i++) {
603 sprintf(ptr, "%02x", *c++);
606 sprintf(ptr, ":%02x", *c++);
608 ptr = buf+strlen(buf);
614 /* create a new one and set the display filter entry accordingly */
615 gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
617 /* Run the display filter so it goes in effect. */
618 filter_packets(&cf, buf);
620 /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
624 hfinfo_numeric_format(header_field_info *hfinfo)
628 /* Pick the proper format string */
629 switch(hfinfo->display) {
632 case BASE_OCT: /* I'm lazy */
633 case BASE_BIN: /* I'm lazy */
634 switch(hfinfo->type) {
648 g_assert_not_reached();
653 switch(hfinfo->type) {
655 format = "%s == 0x%02x";
658 format = "%s == 0x%04x";
661 format = "%s == 0x%06x";
664 format = "%s == 0x%08x";
667 g_assert_not_reached();
672 g_assert_not_reached();
679 /* Run the current display filter on the current packet set, and
682 filter_activate_cb(GtkWidget *w, gpointer data)
684 GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY);
685 GList *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
686 GList *li, *nl = NULL;
687 gboolean add_filter = TRUE;
689 char *s = gtk_entry_get_text(GTK_ENTRY(w));
691 /* GtkCombos don't let us get at their list contents easily, so we maintain
692 our own filter list, and feed it to gtk_combo_set_popdown_strings when
693 a new filter is added. */
694 if (filter_packets(&cf, g_strdup(s))) {
695 li = g_list_first(filter_list);
697 if (li->data && strcmp(s, li->data) == 0)
703 filter_list = g_list_append(filter_list, g_strdup(s));
704 li = g_list_first(filter_list);
706 nl = g_list_append(nl, strdup(li->data));
709 gtk_combo_set_popdown_strings(filter_cm, nl);
710 gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
715 /* redisplay with no display filter */
717 filter_reset_cb(GtkWidget *w, gpointer data)
719 GtkWidget *filter_te = NULL;
721 if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
722 gtk_entry_set_text(GTK_ENTRY(filter_te), "");
725 filter_packets(&cf, NULL);
728 /* What to do when a list item is selected/unselected */
730 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
741 select_packet(&cf, row);
745 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
746 unselect_packet(&cf);
750 tree_view_cb(GtkWidget *w, gpointer data) {
753 int tree_selected_start = -1;
754 int tree_selected_len = -1;
756 if (GTK_TREE(w)->selection) {
758 gtk_object_get_data(GTK_OBJECT(GTK_TREE(w)->selection->data),
759 E_TREEINFO_FIELD_INFO_KEY);
761 finfo_selected = finfo;
762 tree_selected_start = finfo->start;
763 tree_selected_len = finfo->length;
766 gtk_text_freeze(GTK_TEXT(byte_view));
767 gtk_text_set_point(GTK_TEXT(byte_view), 0);
768 gtk_text_forward_delete(GTK_TEXT(byte_view),
769 gtk_text_get_length(GTK_TEXT(byte_view)));
770 packet_hex_print(GTK_TEXT(byte_view), cf.pd, cf.current_frame->cap_len,
771 tree_selected_start, tree_selected_len,
772 cf.current_frame->encoding);
774 gtk_text_thaw(GTK_TEXT(byte_view));
777 void collapse_all_cb(GtkWidget *widget, gpointer data) {
778 if (cf.protocol_tree)
779 collapse_all_tree(cf.protocol_tree, tree_view);
782 void expand_all_cb(GtkWidget *widget, gpointer data) {
783 if (cf.protocol_tree)
784 expand_all_tree(cf.protocol_tree, tree_view);
788 file_quit_cmd_cb (GtkWidget *widget, gpointer data) {
789 if (cf.save_file && !cf.user_saved) {
790 unlink(cf.save_file);
795 /* call initialization routines at program startup time */
797 ethereal_proto_init(void) {
805 ethereal_proto_cleanup(void) {
813 fprintf(stderr, "This is GNU %s %s, compiled with %s\n", PACKAGE,
814 VERSION, comp_info_str);
815 fprintf(stderr, "%s [-vh] [-kQS] [-b <bold font>] [-B <byte view height>] [-c count]\n",
817 fprintf(stderr, " [-f <filter expression>] [-i interface] [-m <medium font>] [-n]\n");
818 fprintf(stderr, " [-P <packet list height>] [-r infile] [-s snaplen]\n");
819 fprintf(stderr, " [-t <time stamp format>] [-T <tree view height>] [-w savefile] \n");
822 /* And now our feature presentation... [ fade to music ] */
824 main(int argc, char *argv[])
836 extern char pcap_version[];
839 int pf_open_errno = 0;
842 gboolean start_capture = FALSE;
843 gchar *save_file = NULL;
845 GtkWidget *window, *main_vbox, *menubar, *u_pane, *l_pane,
846 *bv_table, *bv_hscroll, *bv_vscroll, *stat_hbox,
847 *tv_scrollw, *filter_bt, *filter_cm, *filter_te,
849 GList *filter_list = NULL;
851 GtkAccelGroup *accel;
852 GtkWidget *packet_sw;
853 gint pl_size = 280, tv_size = 95, bv_size = 75;
854 gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
855 dfilter *rfcode = NULL;
856 gboolean rfilter_parse_failed = FALSE;
859 ethereal_path = argv[0];
862 command_name = strrchr(ethereal_path, '/');
863 if (command_name == NULL)
864 command_name = ethereal_path;
867 /* Set "capture_child" to indicate whether this is going to be a child
868 process for a "-S" capture. */
869 capture_child = (strcmp(command_name, CHILD_NAME) == 0);
872 /* If invoked with the "-G" flag, we dump out a glossary of
873 display filter symbols.
875 We must do this before calling "gtk_init()", because "gtk_init()"
876 tries to open an X display, and we don't want to have to do any X
877 stuff just to do a build.
879 Given that we call "gtk_init()" before doing the regular argument
880 list processing, so that it can handle X and GTK+ arguments and
881 remove them from the list at which we look, this means we must do
882 this before doing the regular argument list processing, as well.
886 you must give the "-G" flag as the first flag on the command line;
888 you must give it as "-G", nothing more, nothing less;
890 any arguments after the "-G" flag will not be used. */
891 if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
892 ethereal_proto_init();
893 proto_registrar_dump();
897 /* Let GTK get its args */
898 gtk_init (&argc, &argv);
900 prefs = read_prefs(&pf_path);
901 if (pf_path != NULL) {
902 /* The preferences file exists, but couldn't be opened; "pf_path" is
903 its pathname. Remember "errno", as that says why the attempt
905 pf_open_errno = errno;
908 /* Initialize the capture file struct */
921 cf.save_file_fd = -1;
923 cf.snap = WTAP_MAX_PACKET_SIZE;
925 cf.cinfo.num_cols = prefs->num_cols;
926 cf.cinfo.col_fmt = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
927 cf.cinfo.fmt_matx = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
928 cf.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
929 cf.cinfo.col_title = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
930 cf.cinfo.col_data = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
932 /* Assemble the compile-time options */
933 snprintf(comp_info_str, 256,
934 #ifdef GTK_MAJOR_VERSION
935 "GTK+ %d.%d.%d, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
938 "GTK+ (version unknown), %s%s",
942 "with libpcap ", pcap_version
944 "without libpcap", ""
949 /* Now get our args */
950 while ((opt = getopt(argc, argv, "b:B:c:f:hi:km:nP:Qr:R:Ss:t:T:w:W:v")) != EOF) {
952 case 'b': /* Bold font */
953 bold_font = g_strdup(optarg);
955 case 'B': /* Byte view pane height */
956 bv_size = atoi(optarg);
958 case 'c': /* Capture xxx packets */
959 cf.count = atoi(optarg);
963 cf.cfilter = g_strdup(optarg);
966 case 'h': /* Print help and exit */
970 case 'i': /* Use interface xxx */
971 cf.iface = g_strdup(optarg);
973 case 'm': /* Medium font */
974 medium_font = g_strdup(optarg);
976 case 'n': /* No name resolution */
977 g_resolving_actif = 0;
980 case 'k': /* Start capture immediately */
981 start_capture = TRUE;
984 case 'P': /* Packet list pane height */
985 pl_size = atoi(optarg);
988 case 'Q': /* Quit after capture (just capture to file) */
990 start_capture = TRUE; /*** -Q implies -k !! ***/
993 case 'r': /* Read capture file xxx */
994 cf_name = g_strdup(optarg);
996 case 'R': /* Read file filter */
1000 case 's': /* Set the snapshot (capture) length */
1001 cf.snap = atoi(optarg);
1003 case 'S': /* "Sync" mode: used for following file ala tail -f */
1007 case 't': /* Time stamp type */
1008 if (strcmp(optarg, "r") == 0)
1009 timestamp_type = RELATIVE;
1010 else if (strcmp(optarg, "a") == 0)
1011 timestamp_type = ABSOLUTE;
1012 else if (strcmp(optarg, "d") == 0)
1013 timestamp_type = DELTA;
1015 fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
1017 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
1018 fprintf(stderr, "or \"d\" for delta.\n");
1022 case 'T': /* Tree view pane height */
1023 tv_size = atoi(optarg);
1025 case 'v': /* Show version and exit */
1026 printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
1030 case 'w': /* Write to capture file xxx */
1031 save_file = g_strdup(optarg);
1033 case 'W': /* Write to capture file FD xxx */
1034 cf.save_file_fd = atoi(optarg);
1037 case '?': /* Bad flag - print usage message */
1045 if (start_capture) {
1046 if (cf.iface == NULL) {
1047 fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-i\" flag\n");
1051 if (capture_child) {
1052 if (cf.save_file_fd == -1) {
1053 /* XXX - send this to the standard output as something our parent
1054 should put in an error message box? */
1055 fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
1061 /* Build the column format array */
1062 for (i = 0; i < cf.cinfo.num_cols; i++) {
1063 cf.cinfo.col_fmt[i] = get_column_format(i);
1064 cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
1065 cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
1067 get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
1068 if (cf.cinfo.col_fmt[i] == COL_INFO)
1069 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
1071 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
1075 cf.snap = WTAP_MAX_PACKET_SIZE;
1076 else if (cf.snap < MIN_PACKET_SIZE)
1077 cf.snap = MIN_PACKET_SIZE;
1079 rc_file = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(RC_FILE) + 4);
1080 sprintf(rc_file, "%s/%s", getenv("HOME"), RC_FILE);
1081 gtk_rc_parse(rc_file);
1083 if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
1084 fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
1088 if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
1089 fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
1094 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1095 gtk_widget_set_name(window, "main window");
1096 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1097 GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1098 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1099 GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
1100 gtk_window_set_title(GTK_WINDOW(window), "The Ethereal Network Analyzer");
1101 gtk_widget_set_usize(GTK_WIDGET(window), DEF_WIDTH, -1);
1102 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
1104 /* Container for menu bar, paned windows and progress/info box */
1105 main_vbox = gtk_vbox_new(FALSE, 1);
1106 gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
1107 gtk_container_add(GTK_CONTAINER(window), main_vbox);
1108 gtk_widget_show(main_vbox);
1111 get_main_menu(&menubar, &accel);
1112 gtk_window_add_accel_group(GTK_WINDOW(window), accel);
1113 gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
1114 gtk_widget_show(menubar);
1116 /* Panes for the packet list, tree, and byte view */
1117 u_pane = gtk_vpaned_new();
1118 gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
1119 l_pane = gtk_vpaned_new();
1120 gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
1121 gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
1122 gtk_widget_show(u_pane);
1123 gtk_paned_add2 (GTK_PANED(u_pane), l_pane);
1124 gtk_widget_show(l_pane);
1127 packet_list = gtk_clist_new_with_titles(cf.cinfo.num_cols, cf.cinfo.col_title);
1128 gtk_clist_column_titles_passive(GTK_CLIST(packet_list));
1129 packet_sw = gtk_scrolled_window_new(NULL, NULL);
1130 gtk_widget_show(packet_sw);
1131 gtk_container_add(GTK_CONTAINER(packet_sw), packet_list);
1132 pl_style = gtk_style_new();
1133 gdk_font_unref(pl_style->font);
1134 pl_style->font = m_r_font;
1135 gtk_widget_set_style(packet_list, pl_style);
1136 gtk_widget_set_name(packet_list, "packet list");
1137 gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
1138 GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
1139 gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
1140 GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
1141 for (i = 0; i < cf.cinfo.num_cols; i++) {
1142 if (get_column_resize_type(cf.cinfo.col_fmt[i]) != RESIZE_MANUAL)
1143 gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
1145 /* Right-justify the packet number column. */
1146 if (cf.cinfo.col_fmt[i] == COL_NUMBER)
1147 gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
1150 /* Save static column sizes to use during a "-S" capture, so that
1151 the columns don't resize during a live capture. */
1152 cf.cinfo.col_width[i] = get_column_width(get_column_format(i),
1155 gtk_widget_set_usize(packet_list, -1, pl_size);
1156 gtk_paned_add1(GTK_PANED(u_pane), packet_sw);
1157 gtk_widget_show(packet_list);
1160 tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
1161 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
1162 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1163 gtk_paned_add1(GTK_PANED(l_pane), tv_scrollw);
1164 gtk_widget_set_usize(tv_scrollw, -1, tv_size);
1165 gtk_widget_show(tv_scrollw);
1167 tree_view = gtk_tree_new();
1168 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(tv_scrollw),
1170 gtk_tree_set_selection_mode(GTK_TREE(tree_view), GTK_SELECTION_SINGLE);
1172 /* XXX - what's the difference between the next two lines? */
1173 gtk_tree_set_view_lines(GTK_TREE(tree_view), FALSE);
1174 gtk_tree_set_view_mode(GTK_TREE(tree_view), GTK_TREE_VIEW_ITEM);
1176 gtk_signal_connect(GTK_OBJECT(tree_view), "selection_changed",
1177 GTK_SIGNAL_FUNC(tree_view_cb), NULL);
1178 gtk_widget_show(tree_view);
1180 item_style = gtk_style_new();
1181 gdk_font_unref(item_style->font);
1182 item_style->font = m_r_font;
1185 bv_table = gtk_table_new (2, 2, FALSE);
1186 gtk_paned_add2(GTK_PANED(l_pane), bv_table);
1187 gtk_widget_set_usize(bv_table, -1, bv_size);
1188 gtk_widget_show(bv_table);
1190 byte_view = gtk_text_new(NULL, NULL);
1191 gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
1192 gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
1193 gtk_table_attach (GTK_TABLE (bv_table), byte_view, 0, 1, 0, 1,
1194 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
1195 gtk_widget_show(byte_view);
1197 bv_hscroll = gtk_hscrollbar_new(GTK_TEXT(byte_view)->hadj);
1198 gtk_table_attach(GTK_TABLE(bv_table), bv_hscroll, 0, 1, 1, 2,
1199 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1200 gtk_widget_show (bv_hscroll);
1202 bv_vscroll = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
1203 gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll, 1, 2, 0, 1,
1204 GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
1205 gtk_widget_show(bv_vscroll);
1207 /* Progress/filter/info box */
1208 stat_hbox = gtk_hbox_new(FALSE, 1);
1209 gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
1210 gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
1211 gtk_widget_show(stat_hbox);
1213 prog_bar = gtk_progress_bar_new();
1214 gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 3);
1215 gtk_widget_show(prog_bar);
1217 filter_bt = gtk_button_new_with_label("Filter:");
1218 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
1219 GTK_SIGNAL_FUNC(prefs_cb), (gpointer) E_PR_PG_FILTER);
1220 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
1221 gtk_widget_show(filter_bt);
1223 filter_cm = gtk_combo_new();
1224 filter_list = g_list_append (filter_list, "");
1225 gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
1226 gtk_combo_disable_activate(GTK_COMBO(filter_cm));
1227 filter_te = GTK_COMBO(filter_cm)->entry;
1228 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
1229 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
1230 gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
1231 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
1232 gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
1233 GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
1234 gtk_widget_show(filter_cm);
1236 filter_reset = gtk_button_new_with_label("Reset");
1237 gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
1238 gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
1239 GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
1240 gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
1241 gtk_widget_show(filter_reset);
1243 /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
1244 * of any widget that ends up calling a callback which needs
1245 * that text entry pointer */
1246 set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
1247 set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
1248 set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
1249 set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
1251 info_bar = gtk_statusbar_new();
1252 main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1253 file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1254 gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1255 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1256 gtk_widget_show(info_bar);
1259 Hmmm should we do it here
1262 ethereal_proto_init(); /* Init anything that needs initializing */
1265 /* Is this a "child" ethereal, which is only supposed to pop up a
1266 capture box to let us stop the capture, and run a capture
1267 to a file that our parent will read? */
1268 if (!capture_child) {
1270 /* No. Pop up the main window, and read in a capture file if
1273 gtk_widget_show(window);
1277 /* If we were given the name of a capture file, read it in now;
1278 we defer it until now, so that, if we can't open it, and pop
1279 up an alert box, the alert box is more likely to come up on
1280 top of the main window - but before the preference-file-error
1281 alert box, so, if we get one of those, it's more likely to come
1284 if (rfilter != NULL) {
1285 if (dfilter_compile(rfilter, &rfcode) != 0) {
1286 simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
1287 rfilter_parse_failed = TRUE;
1290 if (!rfilter_parse_failed) {
1291 if ((err = open_cap_file(cf_name, &cf)) == 0) {
1292 /* "open_cap_file()" succeeded, so it closed the previous
1293 capture file, and thus destroyed any previous read filter
1294 attached to "cf". */
1296 err = read_cap_file(&cf);
1297 s = strrchr(cf_name, '/');
1299 last_open_dir = cf_name;
1302 set_menu_sensitivity("/File/Save As...", TRUE);
1304 dfilter_destroy(rfcode);
1313 /* If we failed to open the preferences file, pop up an alert box;
1314 we defer it until now, so that the alert box is more likely to
1315 come up on top of the main window. */
1316 if (pf_path != NULL) {
1317 simple_dialog(ESD_TYPE_WARN, NULL,
1318 "Could not open preferences file\n\"%s\": %s.", pf_path,
1319 strerror(pf_open_errno));
1323 if (capture_child) {
1324 /* This is the child process for a sync mode or fork mode capture,
1325 so just do the low-level work of a capture - don't create
1326 a temporary file and fork off *another* child process (so don't
1327 call "do_capture()"). */
1331 /* The capture is done; there's nothing more for us to do. */
1334 if (start_capture) {
1335 /* "-k" was specified; start a capture. */
1336 do_capture(save_file);
1343 ethereal_proto_cleanup();