3 * $Id: tethereal.c,v 1.10 2000/01/17 08:06:33 guy Exp $
5 * Ethereal - Network traffic analyzer
6 * By Gerald Combs <gerald@zing.org>
7 * Copyright 1998 Gerald Combs
9 * Text-mode variant, by Gilbert Ramirez <gram@verdict.uthscsa.edu>.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 #include <sys/types.h>
45 #ifdef NEED_SNPRINTF_H
51 # include "snprintf.h"
54 #if defined(HAVE_UCD_SNMP_SNMP_H)
55 #ifdef HAVE_UCD_SNMP_VERSION_H
56 #include <ucd-snmp/version.h>
57 #endif /* HAVE_UCD_SNMP_VERSION_H */
58 #elif defined(HAVE_SNMP_SNMP_H)
59 #ifdef HAVE_SNMP_VERSION_H
60 #include <snmp/version.h>
61 #endif /* HAVE_SNMP_VERSION_H */
64 #ifdef NEED_STRERROR_H
69 #include "timestamp.h"
79 #include "conversation.h"
82 static guint32 firstsec, firstusec;
83 static guint32 prevsec, prevusec;
84 static gchar comp_info_str[256];
85 static gboolean verbose;
86 static gboolean print_hex;
89 typedef struct _loop_data {
97 static int capture(int, int);
98 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
100 static void capture_cleanup(int);
103 static int load_cap_file(capture_file *);
104 static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
106 static gchar *col_info(frame_data *, gint);
110 FILE *data_out_file = NULL;
111 guint main_ctx, file_ctx;
112 ts_type timestamp_type = RELATIVE;
114 /* call initialization routines at program startup time */
116 ethereal_proto_init(void) {
127 ethereal_proto_cleanup(void) {
137 fprintf(stderr, "This is GNU t%s %s, compiled with %s\n", PACKAGE,
138 VERSION, comp_info_str);
140 fprintf(stderr, "t%s [ -vVh ] [ -c count ] [ -f <filter expression> ] [ -F <capture type> ]\n", PACKAGE);
141 fprintf(stderr, "\t[ -i iface ] [ -r infile ] [ -R <filter expression> ]\n");
142 fprintf(stderr, "\t[ -s snaplen ] [ -t <time stamp format> ] [ -w savefile ] [ -x ]\n");
143 fprintf(stderr, "Valid file type arguments to the \"-F\" flag:\n");
144 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
145 if (wtap_dump_can_open(i))
146 fprintf(stderr, "\t%s - %s\n",
147 wtap_file_type_short_string(i), wtap_file_type_string(i));
149 fprintf(stderr, "\tdefault is libpcap\n");
153 main(int argc, char *argv[])
157 gboolean arg_error = FALSE;
159 extern char pcap_version[];
164 int packet_count = 0;
166 gchar err_str[PCAP_ERRBUF_SIZE];
168 gboolean capture_option_specified = FALSE;
170 int out_file_type = WTAP_FILE_PCAP;
171 gchar *cf_name = NULL, *rfilter = NULL;
172 dfilter *rfcode = NULL;
175 /* If invoked with the "-G" flag, we dump out a glossary of
176 display filter symbols.
178 We do this here to mirror what happens in the GTK+ version, although
179 it's not necessary here. */
180 if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
181 ethereal_proto_init();
182 proto_registrar_dump();
186 prefs = read_prefs(&pf_path);
187 if (pf_path != NULL) {
188 fprintf(stderr, "Can't open preferences file \"%s\": %s.\n", pf_path,
192 /* Initialize the capture file struct */
198 cf.user_saved = FALSE;
199 cf.is_tempfile = FALSE;
204 cf.cfilter = g_strdup("");
208 cf.save_file_fd = -1;
209 cf.snap = WTAP_MAX_PACKET_SIZE;
211 cf.cinfo.num_cols = prefs->num_cols;
212 cf.cinfo.col_fmt = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
213 cf.cinfo.fmt_matx = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
214 cf.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
215 cf.cinfo.col_title = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
216 cf.cinfo.col_data = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
218 /* Assemble the compile-time options */
219 snprintf(comp_info_str, 256,
220 #ifdef GTK_MAJOR_VERSION
221 "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
224 "GTK+ (version unknown), %s%s, %s%s, %s%s",
228 "with libpcap ", pcap_version,
230 "without libpcap", "",
235 "with libz ", ZLIB_VERSION,
236 #else /* ZLIB_VERSION */
237 "with libz ", "(version unknown)",
238 #endif /* ZLIB_VERSION */
239 #else /* HAVE_LIBZ */
241 #endif /* HAVE_LIBZ */
243 /* Oh, this is pretty */
244 #if defined(HAVE_UCD_SNMP_SNMP_H)
245 #ifdef HAVE_UCD_SNMP_VERSION_H
246 "with UCD SNMP ", VersionInfo
247 #else /* HAVE_UCD_SNMP_VERSION_H */
248 "with UCD SNMP ", "(version unknown)"
249 #endif /* HAVE_UCD_SNMP_VERSION_H */
250 #elif defined(HAVE_SNMP_SNMP_H)
251 #ifdef HAVE_SNMP_VERSION_H
252 "with CMU SNMP ", snmp_Version()
253 #else /* HAVE_SNMP_VERSION_H */
254 "with CMU SNMP ", "(version unknown)"
255 #endif /* HAVE_SNMP_VERSION_H */
261 /* Now get our args */
262 while ((opt = getopt(argc, argv, "c:f:F:hi:nr:R:s:t:vw:Vx")) != EOF) {
264 case 'c': /* Capture xxx packets */
266 packet_count = atoi(optarg);
268 capture_option_specified = TRUE;
274 cf.cfilter = g_strdup(optarg);
276 capture_option_specified = TRUE;
281 out_file_type = wtap_short_string_to_file_type(optarg);
282 if (out_file_type < 0) {
283 fprintf(stderr, "tethereal: \"%s\" is not a valid capture file type\n",
288 case 'h': /* Print help and exit */
292 case 'i': /* Use interface xxx */
294 cf.iface = g_strdup(optarg);
296 capture_option_specified = TRUE;
300 case 'n': /* No name resolution */
301 g_resolving_actif = 0;
303 case 'r': /* Read capture file xxx */
304 cf_name = g_strdup(optarg);
306 case 'R': /* Read file filter */
309 case 's': /* Set the snapshot (capture) length */
311 cf.snap = atoi(optarg);
313 capture_option_specified = TRUE;
317 case 't': /* Time stamp type */
318 if (strcmp(optarg, "r") == 0)
319 timestamp_type = RELATIVE;
320 else if (strcmp(optarg, "a") == 0)
321 timestamp_type = ABSOLUTE;
322 else if (strcmp(optarg, "d") == 0)
323 timestamp_type = DELTA;
325 fprintf(stderr, "tethereal: Invalid time stamp type \"%s\"\n",
327 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
328 fprintf(stderr, "or \"d\" for delta.\n");
332 case 'v': /* Show version and exit */
333 printf("t%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
336 case 'w': /* Write to capture file xxx */
338 cf.save_file = g_strdup(optarg);
340 capture_option_specified = TRUE;
344 case 'V': /* Verbose */
347 case 'x': /* Print packet data in hex (and ASCII) */
354 if (capture_option_specified)
355 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
360 /* Build the column format array */
361 for (i = 0; i < cf.cinfo.num_cols; i++) {
362 cf.cinfo.col_fmt[i] = get_column_format(i);
363 cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
364 cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
366 get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
367 if (cf.cinfo.col_fmt[i] == COL_INFO)
368 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
370 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
374 cf.snap = WTAP_MAX_PACKET_SIZE;
375 else if (cf.snap < MIN_PACKET_SIZE)
376 cf.snap = MIN_PACKET_SIZE;
378 ethereal_proto_init(); /* Init anything that needs initializing */
381 if (rfilter != NULL) {
382 if (dfilter_compile(rfilter, &rfcode) != 0) {
383 fprintf(stderr, "tethereal: %s\n", dfilter_error_msg);
384 ethereal_proto_cleanup();
388 err = open_cap_file(cf_name, FALSE, &cf);
390 ethereal_proto_cleanup();
394 err = load_cap_file(&cf);
396 ethereal_proto_cleanup();
401 /* No capture file specified, so we're supposed to do a live capture;
402 do we have support for live captures? */
404 /* Yes; did the user specify an interface to use? */
405 if (cf.iface == NULL) {
406 /* No - pick the first one from the list of interfaces. */
407 if_list = get_interface_list(&err, err_str);
408 if (if_list == NULL) {
411 case CANT_GET_INTERFACE_LIST:
412 fprintf(stderr, "tethereal: Can't get list of interfaces: %s\n",
416 case NO_INTERFACES_FOUND:
417 fprintf(stderr, "tethereal: There are no interfaces on which a capture can be done\n");
422 cf.iface = g_strdup(if_list->data); /* first interface */
423 free_interface_list(if_list);
425 capture(packet_count, out_file_type);
428 fprintf(stderr, "This version of Tethereal was not built with support for capturing packets.\n");
433 ethereal_proto_cleanup();
439 /* Do the low-level work of a capture.
440 Returns TRUE if it succeeds, FALSE otherwise. */
442 capture(int packet_count, int out_file_type)
444 gchar err_str[PCAP_ERRBUF_SIZE];
445 bpf_u_int32 netnum, netmask;
446 void (*oldhandler)(int);
450 ld.linktype = WTAP_ENCAP_UNKNOWN;
453 /* Open the network interface to capture from it. */
454 ld.pch = pcap_open_live(cf.iface, cf.snap, 1, 1000, err_str);
456 if (ld.pch == NULL) {
457 /* Well, we couldn't start the capture.
458 If this is a child process that does the capturing in sync
459 mode or fork mode, it shouldn't do any UI stuff until we pop up the
460 capture-progress window, and, since we couldn't start the
461 capture, we haven't popped it up. */
462 snprintf(errmsg, sizeof errmsg,
463 "The capture session could not be initiated (%s).\n"
464 "Please check to make sure you have sufficient permissions, and that\n"
465 "you have the proper interface specified.", err_str);
470 /* A capture filter was specified; set it up. */
471 if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
472 snprintf(errmsg, sizeof errmsg,
473 "Can't use filter: Couldn't obtain netmask info (%s).", err_str);
476 if (pcap_compile(ld.pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
477 snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
478 pcap_geterr(ld.pch));
481 if (pcap_setfilter(ld.pch, &cf.fcode) < 0) {
482 snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
483 pcap_geterr(ld.pch));
488 ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(ld.pch));
489 if (cf.save_file != NULL) {
490 /* Set up to write to the capture file. */
491 if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
492 strcpy(errmsg, "The network you're capturing from is of a type"
493 " that Ethereal doesn't support.");
496 ld.pdh = wtap_dump_open(cf.save_file, out_file_type,
497 ld.linktype, pcap_snapshot(ld.pch), &err);
499 if (ld.pdh == NULL) {
500 /* We couldn't set up to write to the capture file. */
503 case WTAP_ERR_UNSUPPORTED_FILE_TYPE:
504 strcpy(errmsg, "Tethereal does not support writing capture files in that format.");
507 case WTAP_ERR_UNSUPPORTED_ENCAP:
508 case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
509 strcpy(errmsg, "Tethereal cannot save this capture in that format.");
512 case WTAP_ERR_CANT_OPEN:
513 strcpy(errmsg, "The file to which the capture would be written"
514 " couldn't be created for some unknown reason.");
517 case WTAP_ERR_SHORT_WRITE:
518 strcpy(errmsg, "A full header couldn't be written to the file"
519 " to which the capture would be written.");
524 sprintf(errmsg, "The file to which the capture would be"
525 " written (\"%s\") could not be opened: Error %d.",
528 sprintf(errmsg, "The file to which the capture would be"
529 " written (\"%s\") could not be opened: %s.",
530 cf.save_file, strerror(err));
538 /* Catch SIGINT and SIGTERM and, if we get either of them, clean up
540 XXX - deal with signal semantics on various platforms. Or just
541 use "sigaction()" and be done with it? */
542 signal(SIGTERM, capture_cleanup);
543 signal(SIGINT, capture_cleanup);
544 if ((oldhandler = signal(SIGHUP, capture_cleanup)) != SIG_DFL)
545 signal(SIGHUP, oldhandler);
547 /* Let the user know what interface was chosen. */
548 printf("Capturing on %s\n", cf.iface);
550 inpkts = pcap_loop(ld.pch, packet_count, capture_pcap_cb, (u_char *) &ld);
556 g_free(cf.save_file);
558 fprintf(stderr, "tethereal: %s\n", errmsg);
566 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
569 struct wtap_pkthdr whdr;
570 loop_data *ld = (loop_data *) user;
574 whdr.caplen = phdr->caplen;
575 whdr.len = phdr->len;
576 whdr.pkt_encap = ld->linktype;
579 /* XXX - do something if this fails */
580 wtap_dump(ld->pdh, &whdr, pd, &err);
582 printf("\r%u ", cf.count);
585 wtap_dispatch_cb((u_char *)&cf, &whdr, 0, pd);
590 capture_cleanup(int signum)
597 wtap_dump_close(ld.pdh, &err);
598 /* XXX - complain if this fails */
601 #endif /* HAVE_LIBPCAP */
604 load_cap_file(capture_file *cf)
609 char errmsg_errno[1024+1];
611 success = wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
613 /* Print up a message box noting that the read failed somewhere along
617 case WTAP_ERR_CANT_READ:
618 errmsg = "An attempt to read from the file failed for"
619 " some unknown reason.";
622 case WTAP_ERR_SHORT_READ:
623 errmsg = "The capture file appears to have been cut short"
624 " in the middle of a packet.";
627 case WTAP_ERR_BAD_RECORD:
628 errmsg = "The capture file appears to be damaged or corrupt.";
632 sprintf(errmsg_errno, "An error occurred while reading the"
633 " capture file: %s.", wtap_strerror(err));
634 errmsg = errmsg_errno;
637 fprintf(stderr, "tethereal: %s\n", errmsg);
646 wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
651 capture_file *cf = (capture_file *) user;
652 proto_tree *protocol_tree;
654 print_args_t print_args;
660 fdata.pkt_len = phdr->len;
661 fdata.cap_len = phdr->caplen;
662 fdata.file_off = offset;
663 fdata.lnk_t = phdr->pkt_encap;
664 fdata.abs_secs = phdr->ts.tv_sec;
665 fdata.abs_usecs = phdr->ts.tv_usec;
666 fdata.encoding = CHAR_ASCII;
667 fdata.pseudo_header = phdr->pseudo_header;
670 fdata.num = cf->count;
672 /* If we don't have the time stamp of the first packet in the
673 capture, it's because this is the first packet. Save the time
674 stamp of this packet as the time stamp of the first packet. */
675 if (!firstsec && !firstusec) {
676 firstsec = fdata.abs_secs;
677 firstusec = fdata.abs_usecs;
680 /* Get the time elapsed between the first packet and this packet. */
681 cf->esec = fdata.abs_secs - firstsec;
682 if (firstusec <= fdata.abs_usecs) {
683 cf->eusec = fdata.abs_usecs - firstusec;
685 cf->eusec = (fdata.abs_usecs + 1000000) - firstusec;
689 fdata.cinfo = &cf->cinfo;
690 for (i = 0; i < fdata.cinfo->num_cols; i++) {
691 fdata.cinfo->col_data[i][0] = '\0';
694 /* If we don't have the time stamp of the previous displayed packet,
695 it's because this is the first displayed packet. Save the time
696 stamp of this packet as the time stamp of the previous displayed
698 if (!prevsec && !prevusec) {
699 prevsec = fdata.abs_secs;
700 prevusec = fdata.abs_usecs;
703 /* Get the time elapsed between the first packet and this packet. */
704 fdata.rel_secs = cf->esec;
705 fdata.rel_usecs = cf->eusec;
707 /* Get the time elapsed between the previous displayed packet and
709 fdata.del_secs = fdata.abs_secs - prevsec;
710 if (prevusec <= fdata.abs_usecs) {
711 fdata.del_usecs = fdata.abs_usecs - prevusec;
713 fdata.del_usecs = (fdata.abs_usecs + 1000000) - prevusec;
716 prevsec = fdata.abs_secs;
717 prevusec = fdata.abs_usecs;
720 if (cf->rfcode || verbose)
721 protocol_tree = proto_tree_create_root();
723 protocol_tree = NULL;
724 dissect_packet(buf, &fdata, protocol_tree);
726 passed = dfilter_apply(cf->rfcode, protocol_tree, buf);
728 /* The packet passed the read filter. */
730 /* Print the information in the protocol tree. */
731 print_args.to_file = TRUE;
732 print_args.format = PR_FMT_TEXT;
733 print_args.print_summary = FALSE;
734 print_args.print_hex = print_hex;
735 print_args.expand_all = TRUE;
736 proto_tree_print(FALSE, &print_args, (GNode *)protocol_tree,
737 buf, &fdata, stdout);
739 /* "print_hex_data()" will put out a leading blank line, as well
740 as a trailing one; print one here, to separate the packets,
741 only if "print_hex_data()" won't be called. */
745 /* Just fill in the columns. */
746 fill_in_columns(&fdata);
747 if (cf->iface == NULL) {
748 printf("%3s %10s %12s -> %-12s %s %s\n",
749 col_info(&fdata, COL_NUMBER),
750 col_info(&fdata, COL_CLS_TIME),
751 col_info(&fdata, COL_DEF_SRC),
752 col_info(&fdata, COL_DEF_DST),
753 col_info(&fdata, COL_PROTOCOL),
754 col_info(&fdata, COL_INFO));
756 printf("%12s -> %-12s %s %s\n",
757 col_info(&fdata, COL_DEF_SRC),
758 col_info(&fdata, COL_DEF_DST),
759 col_info(&fdata, COL_PROTOCOL),
760 col_info(&fdata, COL_INFO));
764 print_hex_data(stdout, print_args.format, buf,
765 fdata.cap_len, fdata.encoding);
770 if (protocol_tree != NULL)
771 proto_tree_free(protocol_tree);
775 file_open_error_message(int err, int for_writing)
778 static char errmsg_errno[1024+1];
782 case WTAP_ERR_NOT_REGULAR_FILE:
783 errmsg = "The file \"%s\" is invalid.";
786 case WTAP_ERR_FILE_UNKNOWN_FORMAT:
787 case WTAP_ERR_UNSUPPORTED:
788 /* Seen only when opening a capture file for reading. */
789 errmsg = "The file \"%s\" is not a capture file in a format Ethereal understands.";
792 case WTAP_ERR_UNSUPPORTED_FILE_TYPE:
793 /* Seen only when opening a capture file for writing. */
794 errmsg = "Ethereal does not support writing capture files in that format.";
797 case WTAP_ERR_UNSUPPORTED_ENCAP:
798 case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
799 /* Seen only when opening a capture file for writing. */
800 errmsg = "Ethereal cannot save this capture in that format.";
803 case WTAP_ERR_BAD_RECORD:
804 errmsg = "The file \"%s\" appears to be damaged or corrupt.";
807 case WTAP_ERR_CANT_OPEN:
809 errmsg = "The file \"%s\" could not be created for some unknown reason.";
811 errmsg = "The file \"%s\" could not be opened for some unknown reason.";
814 case WTAP_ERR_SHORT_READ:
815 errmsg = "The file \"%s\" appears to have been cut short"
816 " in the middle of a packet.";
819 case WTAP_ERR_SHORT_WRITE:
820 errmsg = "A full header couldn't be written to the file \"%s\".";
825 errmsg = "The path to the file \"%s\" does not exist.";
827 errmsg = "The file \"%s\" does not exist.";
832 errmsg = "You do not have permission to create or write to the file \"%s\".";
834 errmsg = "You do not have permission to read the file \"%s\".";
838 sprintf(errmsg_errno, "The file \"%%s\" could not be opened: %s.",
840 errmsg = errmsg_errno;
847 open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
854 char err_msg[2048+1];
856 wth = wtap_open_offline(fname, &err);
860 /* Find the size of the file. */
863 if (fstat(fd, &cf_stat) < 0) {
869 /* The open succeeded. Fill in the information for this file. */
871 /* Initialize the table of conversations. */
874 /* Initialize protocol-specific variables */
875 init_all_protocols();
880 cf->f_len = cf_stat.st_size;
882 /* Set the file name because we need it to set the follow stream filter.
883 XXX - is that still true? We need it for other reasons, though,
885 cf->filename = g_strdup(fname);
887 /* Indicate whether it's a permanent or temporary file. */
888 cf->is_tempfile = is_tempfile;
890 /* If it's a temporary capture buffer file, mark it as not saved. */
891 cf->user_saved = !is_tempfile;
893 cf->cd_t = wtap_file_type(cf->wth);
898 cf->snap = wtap_snapshot_length(cf->wth);
899 cf->update_progbar = FALSE;
900 cf->progbar_quantum = 0;
901 cf->progbar_nextstep = 0;
902 firstsec = 0, firstusec = 0;
903 prevsec = 0, prevusec = 0;
908 snprintf(err_msg, sizeof err_msg, file_open_error_message(err, FALSE), fname);
909 fprintf(stderr, "tethereal: %s\n", err_msg);
913 /* Get the text in a given column */
915 col_info(frame_data *fd, gint el) {
919 for (i = 0; i < fd->cinfo->num_cols; i++) {
920 if (fd->cinfo->fmt_matx[i][el])
921 return fd->cinfo->col_data[i];