3 * $Id: tethereal.c,v 1.1 2000/01/14 06:45:52 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"
81 static guint32 firstsec, firstusec;
82 static guint32 prevsec, prevusec;
83 static gchar comp_info_str[256];
84 static gboolean verbose;
85 static int packet_count;
88 typedef struct _loop_data {
96 static int capture(void);
97 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
99 static void capture_cleanup(int);
102 static int load_cap_file(capture_file *);
103 static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
105 static gchar *col_info(frame_data *, gint);
109 FILE *data_out_file = NULL;
110 guint main_ctx, file_ctx;
111 ts_type timestamp_type = RELATIVE;
113 /* call initialization routines at program startup time */
115 ethereal_proto_init(void) {
123 ethereal_proto_cleanup(void) {
131 fprintf(stderr, "This is GNU %s %s, compiled with %s\n", PACKAGE,
132 VERSION, comp_info_str);
133 fprintf(stderr, "t%s [-v] [-h] [-n]\n",
135 fprintf(stderr, " [-r infile] [-t <time stamp format>]\n");
139 main(int argc, char *argv[])
143 gboolean arg_error = FALSE;
145 extern char pcap_version[];
150 gboolean capture_option_specified = FALSE;
152 gchar *cf_name = NULL, *rfilter = NULL;
153 dfilter *rfcode = NULL;
156 /* If invoked with the "-G" flag, we dump out a glossary of
157 display filter symbols.
159 We do this here to mirror what happens in the GTK+ version, although
160 it's not necessary here. */
161 if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
162 ethereal_proto_init();
163 proto_registrar_dump();
167 prefs = read_prefs(&pf_path);
168 if (pf_path != NULL) {
169 fprintf(stderr, "Can't open preferences file \"%s\": %s.\n", pf_path,
173 /* Initialize the capture file struct */
179 cf.user_saved = FALSE;
180 cf.is_tempfile = FALSE;
189 cf.save_file_fd = -1;
190 cf.snap = WTAP_MAX_PACKET_SIZE;
192 cf.cinfo.num_cols = prefs->num_cols;
193 cf.cinfo.col_fmt = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
194 cf.cinfo.fmt_matx = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
195 cf.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
196 cf.cinfo.col_title = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
197 cf.cinfo.col_data = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
199 /* Assemble the compile-time options */
200 snprintf(comp_info_str, 256,
201 #ifdef GTK_MAJOR_VERSION
202 "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
205 "GTK+ (version unknown), %s%s, %s%s, %s%s",
209 "with libpcap ", pcap_version,
211 "without libpcap", "",
216 "with libz ", ZLIB_VERSION,
217 #else /* ZLIB_VERSION */
218 "with libz ", "(version unknown)",
219 #endif /* ZLIB_VERSION */
220 #else /* HAVE_LIBZ */
222 #endif /* HAVE_LIBZ */
224 /* Oh, this is pretty */
225 #if defined(HAVE_UCD_SNMP_SNMP_H)
226 #ifdef HAVE_UCD_SNMP_VERSION_H
227 "with UCD SNMP ", VersionInfo
228 #else /* HAVE_UCD_SNMP_VERSION_H */
229 "with UCD SNMP ", "(version unknown)"
230 #endif /* HAVE_UCD_SNMP_VERSION_H */
231 #elif defined(HAVE_SNMP_SNMP_H)
232 #ifdef HAVE_SNMP_VERSION_H
233 "with CMU SNMP ", snmp_Version()
234 #else /* HAVE_SNMP_VERSION_H */
235 "with CMU SNMP ", "(version unknown)"
236 #endif /* HAVE_SNMP_VERSION_H */
242 /* Now get our args */
243 while ((opt = getopt(argc, argv, "c:f:hi:nr:R:s:t:vw:V")) != EOF) {
245 case 'c': /* Capture xxx packets */
247 packet_count = atoi(optarg);
249 capture_option_specified = TRUE;
255 cf.cfilter = g_strdup(optarg);
257 capture_option_specified = TRUE;
261 case 'h': /* Print help and exit */
265 case 'i': /* Use interface xxx */
267 cf.iface = g_strdup(optarg);
269 capture_option_specified = TRUE;
273 case 'n': /* No name resolution */
274 g_resolving_actif = 0;
276 case 'r': /* Read capture file xxx */
277 cf_name = g_strdup(optarg);
279 case 'R': /* Read file filter */
282 case 's': /* Set the snapshot (capture) length */
284 cf.snap = atoi(optarg);
286 capture_option_specified = TRUE;
290 case 't': /* Time stamp type */
291 if (strcmp(optarg, "r") == 0)
292 timestamp_type = RELATIVE;
293 else if (strcmp(optarg, "a") == 0)
294 timestamp_type = ABSOLUTE;
295 else if (strcmp(optarg, "d") == 0)
296 timestamp_type = DELTA;
298 fprintf(stderr, "tethereal: Invalid time stamp type \"%s\"\n",
300 fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
301 fprintf(stderr, "or \"d\" for delta.\n");
305 case 'v': /* Show version and exit */
306 printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
309 case 'w': /* Write to capture file xxx */
311 cf.save_file = g_strdup(optarg);
313 capture_option_specified = TRUE;
317 case 'V': /* Verbose */
324 if (capture_option_specified)
325 fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
330 /* Build the column format array */
331 for (i = 0; i < cf.cinfo.num_cols; i++) {
332 cf.cinfo.col_fmt[i] = get_column_format(i);
333 cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
334 cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
336 get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
337 if (cf.cinfo.col_fmt[i] == COL_INFO)
338 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
340 cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
344 cf.snap = WTAP_MAX_PACKET_SIZE;
345 else if (cf.snap < MIN_PACKET_SIZE)
346 cf.snap = MIN_PACKET_SIZE;
348 ethereal_proto_init(); /* Init anything that needs initializing */
351 if (rfilter != NULL) {
352 if (dfilter_compile(rfilter, &rfcode) != 0) {
353 fprintf(stderr, "tethereal: %s\n", dfilter_error_msg);
354 ethereal_proto_cleanup();
358 err = open_cap_file(cf_name, FALSE, &cf);
360 ethereal_proto_cleanup();
364 err = load_cap_file(&cf);
366 ethereal_proto_cleanup();
376 ethereal_proto_cleanup();
382 /* Do the low-level work of a capture.
383 Returns TRUE if it succeeds, FALSE otherwise. */
387 gchar err_str[PCAP_ERRBUF_SIZE];
388 bpf_u_int32 netnum, netmask;
389 void (*oldhandler)(int);
393 ld.linktype = WTAP_ENCAP_UNKNOWN;
396 /* Open the network interface to capture from it. */
397 ld.pch = pcap_open_live(cf.iface, cf.snap, 1, 1000, err_str);
399 if (ld.pch == NULL) {
400 /* Well, we couldn't start the capture.
401 If this is a child process that does the capturing in sync
402 mode or fork mode, it shouldn't do any UI stuff until we pop up the
403 capture-progress window, and, since we couldn't start the
404 capture, we haven't popped it up. */
405 snprintf(errmsg, sizeof errmsg,
406 "The capture session could not be initiated (%s).\n"
407 "Please check to make sure you have sufficient permissions, and that\n"
408 "you have the proper interface specified.", err_str);
413 /* A capture filter was specified; set it up. */
414 if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
415 snprintf(errmsg, sizeof errmsg,
416 "Can't use filter: Couldn't obtain netmask info (%s).", err_str);
419 if (pcap_compile(ld.pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
420 snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
421 pcap_geterr(ld.pch));
424 if (pcap_setfilter(ld.pch, &cf.fcode) < 0) {
425 snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
426 pcap_geterr(ld.pch));
431 ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(ld.pch));
432 if (cf.save_file != NULL) {
433 /* Set up to write to the capture file. */
434 if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
435 strcpy(errmsg, "The network you're capturing from is of a type"
436 " that Ethereal doesn't support.");
439 ld.pdh = wtap_dump_open(cf.save_file, WTAP_FILE_PCAP,
440 ld.linktype, pcap_snapshot(ld.pch), &err);
442 if (ld.pdh == NULL) {
443 /* We couldn't set up to write to the capture file. */
446 case WTAP_ERR_CANT_OPEN:
447 strcpy(errmsg, "The file to which the capture would be saved"
448 " couldn't be created for some unknown reason.");
451 case WTAP_ERR_SHORT_WRITE:
452 strcpy(errmsg, "A full header couldn't be written to the file"
453 " to which the capture would be saved.");
458 sprintf(errmsg, "The file to which the capture would be"
459 " saved (\"%s\") could not be opened: Error %d.",
462 sprintf(errmsg, "The file to which the capture would be"
463 " saved (\"%s\") could not be opened: %s.",
464 cf.save_file, strerror(err));
472 /* Catch SIGINT and SIGTERM and, if we get either of them, clean up
474 XXX - deal with signal semantics on various platforms. Or just
475 use "sigaction()" and be done with it? */
476 signal(SIGTERM, capture_cleanup);
477 signal(SIGINT, capture_cleanup);
478 if ((oldhandler = signal(SIGHUP, capture_cleanup)) != SIG_DFL)
479 signal(SIGHUP, oldhandler);
480 inpkts = pcap_loop(ld.pch, packet_count, capture_pcap_cb, (u_char *) &ld);
486 g_free(cf.save_file);
488 fprintf(stderr, "tethereal: %s\n", errmsg);
496 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
499 struct wtap_pkthdr whdr;
500 loop_data *ld = (loop_data *) user;
504 whdr.caplen = phdr->caplen;
505 whdr.len = phdr->len;
506 whdr.pkt_encap = ld->linktype;
509 /* XXX - do something if this fails */
510 wtap_dump(ld->pdh, &whdr, pd, &err);
512 printf("\r%u ", cf.count);
515 wtap_dispatch_cb((u_char *)&cf, &whdr, 0, pd);
520 capture_cleanup(int signum)
527 wtap_dump_close(ld.pdh, &err);
528 /* XXX - complain if this fails */
531 #endif /* HAVE_LIBPCAP */
534 load_cap_file(capture_file *cf)
539 char errmsg_errno[1024+1];
541 success = wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
543 /* Print up a message box noting that the read failed somewhere along
547 case WTAP_ERR_CANT_READ:
548 errmsg = "An attempt to read from the file failed for"
549 " some unknown reason.";
552 case WTAP_ERR_SHORT_READ:
553 errmsg = "The capture file appears to have been cut short"
554 " in the middle of a packet.";
557 case WTAP_ERR_BAD_RECORD:
558 errmsg = "The capture file appears to be damaged or corrupt.";
562 sprintf(errmsg_errno, "An error occurred while reading the"
563 " capture file: %s.", wtap_strerror(err));
564 errmsg = errmsg_errno;
567 fprintf(stderr, "tethereal: %s\n", errmsg);
576 wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
581 capture_file *cf = (capture_file *) user;
582 proto_tree *protocol_tree;
584 print_args_t print_args;
590 fdata.pkt_len = phdr->len;
591 fdata.cap_len = phdr->caplen;
592 fdata.file_off = offset;
593 fdata.lnk_t = phdr->pkt_encap;
594 fdata.abs_secs = phdr->ts.tv_sec;
595 fdata.abs_usecs = phdr->ts.tv_usec;
596 fdata.encoding = CHAR_ASCII;
597 fdata.pseudo_header = phdr->pseudo_header;
600 fdata.num = cf->count;
602 /* If we don't have the time stamp of the first packet in the
603 capture, it's because this is the first packet. Save the time
604 stamp of this packet as the time stamp of the first packet. */
605 if (!firstsec && !firstusec) {
606 firstsec = fdata.abs_secs;
607 firstusec = fdata.abs_usecs;
610 /* Get the time elapsed between the first packet and this packet. */
611 cf->esec = fdata.abs_secs - firstsec;
612 if (firstusec <= fdata.abs_usecs) {
613 cf->eusec = fdata.abs_usecs - firstusec;
615 cf->eusec = (fdata.abs_usecs + 1000000) - firstusec;
619 fdata.cinfo = &cf->cinfo;
620 for (i = 0; i < fdata.cinfo->num_cols; i++) {
621 fdata.cinfo->col_data[i][0] = '\0';
624 /* If we don't have the time stamp of the previous displayed packet,
625 it's because this is the first displayed packet. Save the time
626 stamp of this packet as the time stamp of the previous displayed
628 if (!prevsec && !prevusec) {
629 prevsec = fdata.abs_secs;
630 prevusec = fdata.abs_usecs;
633 /* Get the time elapsed between the first packet and this packet. */
634 fdata.rel_secs = cf->esec;
635 fdata.rel_usecs = cf->eusec;
637 /* Get the time elapsed between the previous displayed packet and
639 fdata.del_secs = fdata.abs_secs - prevsec;
640 if (prevusec <= fdata.abs_usecs) {
641 fdata.del_usecs = fdata.abs_usecs - prevusec;
643 fdata.del_usecs = (fdata.abs_usecs + 1000000) - prevusec;
646 prevsec = fdata.abs_secs;
647 prevusec = fdata.abs_usecs;
650 if (cf->rfcode || verbose)
651 protocol_tree = proto_tree_create_root();
653 protocol_tree = NULL;
654 dissect_packet(buf, &fdata, protocol_tree);
656 passed = dfilter_apply(cf->rfcode, protocol_tree, buf);
658 /* The packet passed the read filter. */
660 /* Print the information in the protocol tree. */
661 print_args.to_file = TRUE;
662 print_args.format = PR_FMT_TEXT;
663 print_args.print_summary = FALSE;
664 print_args.print_hex = FALSE;
665 print_args.expand_all = TRUE;
666 proto_tree_print(FALSE, &print_args, (GNode *)protocol_tree,
667 buf, &fdata, stdout);
670 /* Just fill in the columns. */
671 fill_in_columns(&fdata);
672 if (cf->iface == NULL) {
673 printf("%3s %10s %12s -> %-12s %s %s\n",
674 col_info(&fdata, COL_NUMBER),
675 col_info(&fdata, COL_CLS_TIME),
676 col_info(&fdata, COL_DEF_SRC),
677 col_info(&fdata, COL_DEF_DST),
678 col_info(&fdata, COL_PROTOCOL),
679 col_info(&fdata, COL_INFO));
681 printf("%12s -> %-12s %s %s\n",
682 col_info(&fdata, COL_DEF_SRC),
683 col_info(&fdata, COL_DEF_DST),
684 col_info(&fdata, COL_PROTOCOL),
685 col_info(&fdata, COL_INFO));
690 if (protocol_tree != NULL)
691 proto_tree_free(protocol_tree);
695 file_open_error_message(int err, int for_writing)
698 static char errmsg_errno[1024+1];
702 case WTAP_ERR_NOT_REGULAR_FILE:
703 errmsg = "The file \"%s\" is invalid.";
706 case WTAP_ERR_FILE_UNKNOWN_FORMAT:
707 case WTAP_ERR_UNSUPPORTED:
708 /* Seen only when opening a capture file for reading. */
709 errmsg = "The file \"%s\" is not a capture file in a format Ethereal understands.";
712 case WTAP_ERR_UNSUPPORTED_FILE_TYPE:
713 /* Seen only when opening a capture file for writing. */
714 errmsg = "Ethereal does not support writing capture files in that format.";
717 case WTAP_ERR_UNSUPPORTED_ENCAP:
718 case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
719 /* Seen only when opening a capture file for writing. */
720 errmsg = "Ethereal cannot save this capture in that format.";
723 case WTAP_ERR_BAD_RECORD:
724 errmsg = "The file \"%s\" appears to be damaged or corrupt.";
727 case WTAP_ERR_CANT_OPEN:
729 errmsg = "The file \"%s\" could not be created for some unknown reason.";
731 errmsg = "The file \"%s\" could not be opened for some unknown reason.";
734 case WTAP_ERR_SHORT_READ:
735 errmsg = "The file \"%s\" appears to have been cut short"
736 " in the middle of a packet.";
739 case WTAP_ERR_SHORT_WRITE:
740 errmsg = "A full header couldn't be written to the file \"%s\".";
745 errmsg = "The path to the file \"%s\" does not exist.";
747 errmsg = "The file \"%s\" does not exist.";
752 errmsg = "You do not have permission to create or write to the file \"%s\".";
754 errmsg = "You do not have permission to read the file \"%s\".";
758 sprintf(errmsg_errno, "The file \"%%s\" could not be opened: %s.",
760 errmsg = errmsg_errno;
767 open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
774 char err_msg[2048+1];
776 wth = wtap_open_offline(fname, &err);
780 /* Find the size of the file. */
783 if (fstat(fd, &cf_stat) < 0) {
789 /* The open succeeded. Fill in the information for this file. */
791 /* Initialize the table of conversations. */
794 /* Initialize protocol-specific variables */
795 init_all_protocols();
800 cf->f_len = cf_stat.st_size;
802 /* Set the file name because we need it to set the follow stream filter.
803 XXX - is that still true? We need it for other reasons, though,
805 cf->filename = g_strdup(fname);
807 /* Indicate whether it's a permanent or temporary file. */
808 cf->is_tempfile = is_tempfile;
810 /* If it's a temporary capture buffer file, mark it as not saved. */
811 cf->user_saved = !is_tempfile;
813 cf->cd_t = wtap_file_type(cf->wth);
818 cf->snap = wtap_snapshot_length(cf->wth);
819 cf->update_progbar = FALSE;
820 cf->progbar_quantum = 0;
821 cf->progbar_nextstep = 0;
822 firstsec = 0, firstusec = 0;
823 prevsec = 0, prevusec = 0;
828 snprintf(err_msg, sizeof err_msg, file_open_error_message(err, FALSE), fname);
829 fprintf(stderr, "tethereal: %s\n", err_msg);
833 /* Get the text in a given column */
835 col_info(frame_data *fd, gint el) {
839 for (i = 0; i < fd->cinfo->num_cols; i++) {
840 if (fd->cinfo->fmt_matx[i][el])
841 return fd->cinfo->col_data[i];