X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/a86405d6ceea66d00755ad5fc63d1510a88ff536..4fe8841dce9ba05e8b36aa97b02190ac81ccafd5:/tcpdump.c diff --git a/tcpdump.c b/tcpdump.c index c560bff1..5598ec28 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -26,15 +26,13 @@ */ #ifndef lint -static const char copyright[] = +static const char copyright[] _U_ = "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\ The Regents of the University of California. All rights reserved.\n"; -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/tcpdump/tcpdump.c,v 1.204 2003-03-08 08:55:34 guy Exp $ (LBL)"; #endif /* - * tcpdump - monitor tcp/ip traffic on an ethernet. + * tcpdump - dump traffic on a network * * First written in 1987 by Van Jacobson, Lawrence Berkeley Laboratory. * Mercilessly hacked and occasionally improved since then via the @@ -45,199 +43,273 @@ static const char rcsid[] = #include "config.h" #endif -#include +/* + * Mac OS X may ship pcap.h from libpcap 0.6 with a libpcap based on + * 0.8. That means it has pcap_findalldevs() but the header doesn't + * define pcap_if_t, meaning that we can't actually *use* pcap_findalldevs(). + */ +#ifdef HAVE_PCAP_FINDALLDEVS +#ifndef HAVE_PCAP_IF_T +#undef HAVE_PCAP_FINDALLDEVS +#endif +#endif -#ifdef WIN32 -#include "getopt.h" -#include "w32_fzs.h" -extern int strcasecmp (const char *__s1, const char *__s2); -extern int SIZE_BUF; -#define off_t long -#define uint UINT -#endif /* WIN32 */ +#include +#ifdef USE_LIBSMI +#include +#endif + +#ifdef HAVE_LIBCRYPTO +#include +#endif + +#ifdef HAVE_GETOPT_LONG +#include +#else +#include "getopt_long.h" +#endif +/* Capsicum-specific code requires macros from , which will fail + * to compile if has already been included; including the headers + * in the opposite order works fine. + */ +#ifdef HAVE_CAPSICUM +#include +#include +#include +#include +#include +#endif /* HAVE_CAPSICUM */ #include #include #include #include #include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#endif /* _WIN32 */ + +/* capabilities convenience library */ +/* If a code depends on HAVE_LIBCAP_NG, it depends also on HAVE_CAP_NG_H. + * If HAVE_CAP_NG_H is not defined, undefine HAVE_LIBCAP_NG. + * Thus, the later tests are done only on HAVE_LIBCAP_NG. + */ +#ifdef HAVE_LIBCAP_NG +#ifdef HAVE_CAP_NG_H +#include +#else +#undef HAVE_LIBCAP_NG +#endif /* HAVE_CAP_NG_H */ +#endif /* HAVE_LIBCAP_NG */ +#include "netdissect.h" #include "interface.h" #include "addrtoname.h" #include "machdep.h" #include "setsignal.h" #include "gmt2local.h" #include "pcap-missing.h" +#include "ascii_strcasecmp.h" -int aflag; /* translate network and broadcast addresses */ -int dflag; /* print filter code */ -int eflag; /* print ethernet header */ -int fflag; /* don't translate "foreign" IP address */ -int Lflag; /* list available data link types and exit */ -int nflag; /* leave addresses as numbers */ -int Nflag; /* remove domains from printed host names */ -int Oflag = 1; /* run filter code optimizer */ -int pflag; /* don't go promiscuous */ -int qflag; /* quick (shorter) output */ -int Rflag = 1; /* print sequence # field in AH/ESP*/ -int sflag = 0; /* use the libsmi to translate OIDs */ -int Sflag; /* print raw TCP sequence numbers */ -int tflag = 1; /* print packet arrival time */ -int Uflag = 0; /* "unbuffered" output of dump files */ -int uflag = 0; /* Print undecoded NFS handles */ -int vflag; /* verbose */ -int xflag; /* print packet in hex */ -int Xflag; /* print packet in ascii as well as hex */ -off_t Cflag = 0; /* rotate dump files after this many bytes */ -int Aflag = 0; /* print packet only in ascii observing LF, CR, TAB, SPACE */ -int dlt = -1; /* if != -1, ask libpcap for the DLT it names */ - -const char *dlt_name = NULL; - -char *espsecret = NULL; /* ESP secret key */ - -int packettype; +#include "print.h" + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#ifdef SIGINFO +#define SIGNAL_REQ_INFO SIGINFO +#elif SIGUSR1 +#define SIGNAL_REQ_INFO SIGUSR1 +#endif + +static int Cflag; /* rotate dump files after this many bytes */ +static int Cflag_count; /* Keep track of which file number we're writing */ +static int Dflag; /* list available devices and exit */ +/* + * This is exported because, in some versions of libpcap, if libpcap + * is built with optimizer debugging code (which is *NOT* the default + * configuration!), the library *imports*(!) a variable named dflag, + * under the expectation that tcpdump is exporting it, to govern + * how much debugging information to print when optimizing + * the generated BPF code. + * + * This is a horrible hack; newer versions of libpcap don't import + * dflag but, instead, *if* built with optimizer debugging code, + * *export* a routine to set that flag. + */ +int dflag; /* print filter code */ +static int Gflag; /* rotate dump files after this many seconds */ +static int Gflag_count; /* number of files created with Gflag rotation */ +static time_t Gflag_time; /* The last time_t the dump file was rotated. */ +static int Lflag; /* list available data link types and exit */ +static int Iflag; /* rfmon (monitor) mode */ +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +static int Jflag; /* list available time stamp types */ +#endif +#ifdef HAVE_PCAP_SETDIRECTION +int Qflag = -1; /* restrict captured packet by send/receive direction */ +#endif +static int Uflag; /* "unbuffered" output of dump files */ +static int Wflag; /* recycle output files after this number of files */ +static int WflagChars; +static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */ static int infodelay; static int infoprint; char *program_name; -int32_t thiszone; /* seconds offset from gmt to local time */ - /* Forwards */ static RETSIGTYPE cleanup(int); -static void usage(void) __attribute__((noreturn)); -static void show_dlts_and_exit(pcap_t *pd) __attribute__((noreturn)); +static RETSIGTYPE child_cleanup(int); +static void print_version(void); +static void print_usage(void); +static void show_tstamp_types_and_exit(const char *device) __attribute__((noreturn)); +static void show_dlts_and_exit(const char *device) __attribute__((noreturn)); static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); +static void droproot(const char *, const char *); -#ifdef SIGINFO +#ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int); #endif -static void info(int); - -/* Length of saved portion of packet. */ -int snaplen = DEFAULT_SNAPLEN; - -typedef u_int (*if_printer)(const struct pcap_pkthdr *, const u_char *); +#if defined(USE_WIN32_MM_TIMER) + #include + static UINT timer_id; + static void CALLBACK verbose_stats_dump(UINT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR); +#elif defined(HAVE_ALARM) + static void verbose_stats_dump(int sig); +#endif -struct printer { - if_printer f; - int type; -}; +static void info(int); +static u_int packets_captured; -static struct printer printers[] = { - { arcnet_if_print, DLT_ARCNET }, -#ifdef DLT_ARCNET_LINUX - { arcnet_linux_if_print, DLT_ARCNET_LINUX }, -#endif - { ether_if_print, DLT_EN10MB }, - { token_if_print, DLT_IEEE802 }, -#ifdef DLT_LANE8023 - { lane_if_print, DLT_LANE8023 }, -#endif -#ifdef DLT_CIP - { cip_if_print, DLT_CIP }, -#endif -#ifdef DLT_ATM_CLIP - { cip_if_print, DLT_ATM_CLIP }, -#endif - { sl_if_print, DLT_SLIP }, - { sl_bsdos_if_print, DLT_SLIP_BSDOS }, - { ppp_if_print, DLT_PPP }, - { ppp_bsdos_if_print, DLT_PPP_BSDOS }, - { fddi_if_print, DLT_FDDI }, - { null_if_print, DLT_NULL }, -#ifdef DLT_LOOP - { null_if_print, DLT_LOOP }, -#endif - { raw_if_print, DLT_RAW }, - { atm_if_print, DLT_ATM_RFC1483 }, -#ifdef DLT_C_HDLC - { chdlc_if_print, DLT_C_HDLC }, -#endif -#ifdef DLT_HDLC - { chdlc_if_print, DLT_HDLC }, -#endif -#ifdef DLT_PPP_SERIAL - { ppp_hdlc_if_print, DLT_PPP_SERIAL }, -#endif -#ifdef DLT_PPP_ETHER - { pppoe_if_print, DLT_PPP_ETHER }, -#endif -#ifdef DLT_LINUX_SLL - { sll_if_print, DLT_LINUX_SLL }, -#endif -#ifdef DLT_IEEE802_11 - { ieee802_11_if_print, DLT_IEEE802_11}, +static const struct tok status_flags[] = { +#ifdef PCAP_IF_UP + { PCAP_IF_UP, "Up" }, #endif -#ifdef DLT_LTALK - { ltalk_if_print, DLT_LTALK }, +#ifdef PCAP_IF_RUNNING + { PCAP_IF_RUNNING, "Running" }, #endif -#ifdef DLT_PFLOG - { pflog_if_print, DLT_PFLOG }, -#endif -#ifdef DLT_FR - { fr_if_print, DLT_FR }, -#endif -#ifdef DLT_FRELAY - { fr_if_print, DLT_FRELAY }, -#endif -#ifdef DLT_SUNATM - { sunatm_if_print, DLT_SUNATM }, -#endif -#ifdef DLT_IP_OVER_FC - { ipfc_if_print, DLT_IP_OVER_FC }, -#endif -#ifdef DLT_PRISM_HEADER - { prism_if_print, DLT_PRISM_HEADER }, -#endif -#ifdef DLT_IEEE802_11_RADIO - { ieee802_11_radio_if_print, DLT_IEEE802_11_RADIO }, -#endif -#ifdef DLT_ENC - { enc_if_print, DLT_ENC }, -#endif - { NULL, 0 }, + { PCAP_IF_LOOPBACK, "Loopback" }, + { 0, NULL } }; -static if_printer -lookup_printer(int type) -{ - struct printer *p; - - for (p = printers; p->f; ++p) - if (type == p->type) - return p->f; - - return NULL; - /* NOTREACHED */ -} - static pcap_t *pd; +static int supports_monitor_mode; + extern int optind; extern int opterr; extern char *optarg; -struct print_info { - if_printer printer; -}; - struct dump_info { char *WFileName; + char *CurrentFileName; pcap_t *pd; pcap_dumper_t *p; +#ifdef HAVE_CAPSICUM + int dirfd; +#endif }; +#if defined(HAVE_PCAP_SET_PARSER_DEBUG) +/* + * We have pcap_set_parser_debug() in libpcap; declare it (it's not declared + * by any libpcap header, because it's a special hack, only available if + * libpcap was configured to include it, and only intended for use by + * libpcap developers trying to debug the parser for filter expressions). + */ +#ifdef _WIN32 +__declspec(dllimport) +#else /* _WIN32 */ +extern +#endif /* _WIN32 */ +void pcap_set_parser_debug(int); +#elif defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG) +/* + * We don't have pcap_set_parser_debug() in libpcap, but we do have + * pcap_debug or yydebug. Make a local version of pcap_set_parser_debug() + * to set the flag, and define HAVE_PCAP_SET_PARSER_DEBUG. + */ +static void +pcap_set_parser_debug(int value) +{ +#ifdef HAVE_PCAP_DEBUG + extern int pcap_debug; + + pcap_debug = value; +#else /* HAVE_PCAP_DEBUG */ + extern int yydebug; + + yydebug = value; +#endif /* HAVE_PCAP_DEBUG */ +} + +#define HAVE_PCAP_SET_PARSER_DEBUG +#endif + +#if defined(HAVE_PCAP_SET_OPTIMIZER_DEBUG) +/* + * We have pcap_set_optimizer_debug() in libpcap; declare it (it's not declared + * by any libpcap header, because it's a special hack, only available if + * libpcap was configured to include it, and only intended for use by + * libpcap developers trying to debug the optimizer for filter expressions). + */ +#ifdef _WIN32 +__declspec(dllimport) +#else /* _WIN32 */ +extern +#endif /* _WIN32 */ +void pcap_set_optimizer_debug(int); +#endif + +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +static void +show_tstamp_types_and_exit(const char *device) +{ + int n_tstamp_types; + int *tstamp_types = 0; + const char *tstamp_type_name; + int i; + + n_tstamp_types = pcap_list_tstamp_types(pd, &tstamp_types); + if (n_tstamp_types < 0) + error("%s", pcap_geterr(pd)); + + if (n_tstamp_types == 0) { + fprintf(stderr, "Time stamp type cannot be set for %s\n", + device); + exit(0); + } + fprintf(stderr, "Time stamp types for %s (use option -j to set):\n", + device); + for (i = 0; i < n_tstamp_types; i++) { + tstamp_type_name = pcap_tstamp_type_val_to_name(tstamp_types[i]); + if (tstamp_type_name != NULL) { + (void) fprintf(stderr, " %s (%s)\n", tstamp_type_name, + pcap_tstamp_type_val_to_description(tstamp_types[i])); + } else { + (void) fprintf(stderr, " %d\n", tstamp_types[i]); + } + } + pcap_free_tstamp_types(tstamp_types); + exit(0); +} +#endif + static void -show_dlts_and_exit(pcap_t *pd) +show_dlts_and_exit(const char *device) { - int n_dlts; + int n_dlts, i; int *dlts = 0; const char *dlt_name; @@ -247,39 +319,125 @@ show_dlts_and_exit(pcap_t *pd) else if (n_dlts == 0 || !dlts) error("No data link types."); - (void) fprintf(stderr, "Data link types (use option -y to set):\n"); + /* + * If the interface is known to support monitor mode, indicate + * whether these are the data link types available when not in + * monitor mode, if -I wasn't specified, or when in monitor mode, + * when -I was specified (the link-layer types available in + * monitor mode might be different from the ones available when + * not in monitor mode). + */ + if (supports_monitor_mode) + (void) fprintf(stderr, "Data link types for %s %s (use option -y to set):\n", + device, + Iflag ? "when in monitor mode" : "when not in monitor mode"); + else + (void) fprintf(stderr, "Data link types for %s (use option -y to set):\n", + device); - while (--n_dlts >= 0) { - dlt_name = pcap_datalink_val_to_name(dlts[n_dlts]); + for (i = 0; i < n_dlts; i++) { + dlt_name = pcap_datalink_val_to_name(dlts[i]); if (dlt_name != NULL) { - (void) fprintf(stderr, " %s", dlt_name); + (void) fprintf(stderr, " %s (%s)", dlt_name, + pcap_datalink_val_to_description(dlts[i])); /* * OK, does tcpdump handle that type? */ - if (lookup_printer(dlts[n_dlts]) == NULL) - (void) fprintf(stderr, " (not supported)"); - putchar('\n'); + if (!has_printer(dlts[i])) + (void) fprintf(stderr, " (printing not supported)"); + fprintf(stderr, "\n"); } else { - (void) fprintf(stderr, " DLT %d (not supported)\n", - dlts[n_dlts]); + (void) fprintf(stderr, " DLT %d (printing not supported)\n", + dlts[i]); } } - free(dlts); +#ifdef HAVE_PCAP_FREE_DATALINKS + pcap_free_datalinks(dlts); +#endif + exit(0); +} + +#ifdef HAVE_PCAP_FINDALLDEVS +static void +show_devices_and_exit (void) +{ + pcap_if_t *dev, *devlist; + char ebuf[PCAP_ERRBUF_SIZE]; + int i; + + if (pcap_findalldevs(&devlist, ebuf) < 0) + error("%s", ebuf); + for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) { + printf("%d.%s", i+1, dev->name); + if (dev->description != NULL) + printf(" (%s)", dev->description); + if (dev->flags != 0) + printf(" [%s]", bittok2str(status_flags, "none", dev->flags)); + printf("\n"); + } + pcap_freealldevs(devlist); exit(0); } +#endif /* HAVE_PCAP_FINDALLDEVS */ + +/* + * Short options. + * + * Note that there we use all letters for short options except for g, k, + * o, and P, and those are used by other versions of tcpdump, and we should + * only use them for the same purposes that the other versions of tcpdump + * use them: + * + * OS X tcpdump uses -g to force non--v output for IP to be on one + * line, making it more "g"repable; + * + * OS X tcpdump uses -k tospecify that packet comments in pcap-ng files + * should be printed; + * + * OpenBSD tcpdump uses -o to indicate that OS fingerprinting should be done + * for hosts sending TCP SYN packets; + * + * OS X tcpdump uses -P to indicate that -w should write pcap-ng rather + * than pcap files. + * + * OS X tcpdump also uses -Q to specify expressions that match packet + * metadata, including but not limited to the packet direction. + * The expression syntax is different from a simple "in|out|inout", + * and those expressions aren't accepted by OS X tcpdump, but the + * equivalents would be "in" = "dir=in", "out" = "dir=out", and + * "inout" = "dir=in or dir=out", and the parser could conceivably + * special-case "in", "out", and "inout" as expressions for backwards + * compatibility, so all is not (yet) lost. + */ /* * Set up flags that might or might not be supported depending on the * version of libpcap we're using. */ -#ifdef WIN32 +#if defined(HAVE_PCAP_CREATE) || defined(_WIN32) #define B_FLAG "B:" #define B_FLAG_USAGE " [ -B size ]" -#else /* WIN32 */ +#else /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ #define B_FLAG #define B_FLAG_USAGE -#endif /* WIN32 */ +#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ + +#ifdef HAVE_PCAP_CREATE +#define I_FLAG "I" +#else /* HAVE_PCAP_CREATE */ +#define I_FLAG +#endif /* HAVE_PCAP_CREATE */ + +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +#define j_FLAG "j:" +#define j_FLAG_USAGE " [ -j tstamptype ]" +#define J_FLAG "J" +#else /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ +#define j_FLAG +#define j_FLAG_USAGE +#define J_FLAG +#endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ #ifdef HAVE_PCAP_FINDALLDEVS #define D_FLAG "D" @@ -293,77 +451,410 @@ show_dlts_and_exit(pcap_t *pd) #define U_FLAG #endif +#ifdef HAVE_PCAP_SETDIRECTION +#define Q_FLAG "Q:" +#else +#define Q_FLAG +#endif + +#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#" + +/* + * Long options. + * + * We do not currently have long options corresponding to all short + * options; we should probably pick appropriate option names for them. + * + * However, the short options where the number of times the option is + * specified matters, such as -v and -d and -t, should probably not + * just map to a long option, as saying + * + * tcpdump --verbose --verbose + * + * doesn't make sense; it should be --verbosity={N} or something such + * as that. + * + * For long options with no corresponding short options, we define values + * outside the range of ASCII graphic characters, make that the last + * component of the entry for the long option, and have a case for that + * option in the switch statement. + */ +#define OPTION_VERSION 128 +#define OPTION_TSTAMP_PRECISION 129 +#define OPTION_IMMEDIATE_MODE 130 + +static const struct option longopts[] = { +#if defined(HAVE_PCAP_CREATE) || defined(_WIN32) + { "buffer-size", required_argument, NULL, 'B' }, +#endif + { "list-interfaces", no_argument, NULL, 'D' }, + { "help", no_argument, NULL, 'h' }, + { "interface", required_argument, NULL, 'i' }, +#ifdef HAVE_PCAP_CREATE + { "monitor-mode", no_argument, NULL, 'I' }, +#endif +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + { "time-stamp-type", required_argument, NULL, 'j' }, + { "list-time-stamp-types", no_argument, NULL, 'J' }, +#endif +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + { "time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION}, +#endif + { "dont-verify-checksums", no_argument, NULL, 'K' }, + { "list-data-link-types", no_argument, NULL, 'L' }, + { "no-optimize", no_argument, NULL, 'O' }, + { "no-promiscuous-mode", no_argument, NULL, 'p' }, +#ifdef HAVE_PCAP_SETDIRECTION + { "direction", required_argument, NULL, 'Q' }, +#endif + { "snapshot-length", required_argument, NULL, 's' }, + { "absolute-tcp-sequence-numbers", no_argument, NULL, 'S' }, +#ifdef HAVE_PCAP_DUMP_FLUSH + { "packet-buffered", no_argument, NULL, 'U' }, +#endif + { "linktype", required_argument, NULL, 'y' }, +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE }, +#endif +#ifdef HAVE_PCAP_SET_PARSER_DEBUG + { "debug-filter-parser", no_argument, NULL, 'Y' }, +#endif + { "relinquish-privileges", required_argument, NULL, 'Z' }, + { "number", no_argument, NULL, '#' }, + { "version", no_argument, NULL, OPTION_VERSION }, + { NULL, 0, NULL, 0 } +}; + +#ifndef _WIN32 +/* Drop root privileges and chroot if necessary */ +static void +droproot(const char *username, const char *chroot_dir) +{ + struct passwd *pw = NULL; + + if (chroot_dir && !username) { + fprintf(stderr, "%s: Chroot without dropping root is insecure\n", + program_name); + exit(1); + } + + pw = getpwnam(username); + if (pw) { + if (chroot_dir) { + if (chroot(chroot_dir) != 0 || chdir ("/") != 0) { + fprintf(stderr, "%s: Couldn't chroot/chdir to '%.64s': %s\n", + program_name, chroot_dir, pcap_strerror(errno)); + exit(1); + } + } +#ifdef HAVE_LIBCAP_NG + { + int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG); + if (ret < 0) { + fprintf(stderr, "error : ret %d\n", ret); + } else { + fprintf(stderr, "dropped privs to %s\n", username); + } + } +#else + if (initgroups(pw->pw_name, pw->pw_gid) != 0 || + setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { + fprintf(stderr, "%s: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n", + program_name, username, + (unsigned long)pw->pw_uid, + (unsigned long)pw->pw_gid, + pcap_strerror(errno)); + exit(1); + } + else { + fprintf(stderr, "dropped privs to %s\n", username); + } +#endif /* HAVE_LIBCAP_NG */ + } + else { + fprintf(stderr, "%s: Couldn't find user '%.32s'\n", + program_name, username); + exit(1); + } +#ifdef HAVE_LIBCAP_NG + /* We don't need CAP_SETUID and CAP_SETGID any more. */ + capng_updatev( + CAPNG_DROP, + CAPNG_EFFECTIVE | CAPNG_PERMITTED, + CAP_SETUID, + CAP_SETGID, + -1); + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ + +} +#endif /* _WIN32 */ + +static int +getWflagChars(int x) +{ + int c = 0; + + x -= 1; + while (x > 0) { + c += 1; + x /= 10; + } + + return c; +} + + +static void +MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) +{ + char *filename = malloc(PATH_MAX + 1); + if (filename == NULL) + error("Makefilename: malloc"); + + /* Process with strftime if Gflag is set. */ + if (Gflag != 0) { + struct tm *local_tm; + + /* Convert Gflag_time to a usable format */ + if ((local_tm = localtime(&Gflag_time)) == NULL) { + error("MakeTimedFilename: localtime"); + } + + /* There's no good way to detect an error in strftime since a return + * value of 0 isn't necessarily failure. + */ + strftime(filename, PATH_MAX, orig_name, local_tm); + } else { + strncpy(filename, orig_name, PATH_MAX); + } + + if (cnt == 0 && max_chars == 0) + strncpy(buffer, filename, PATH_MAX + 1); + else + if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX) + /* Report an error if the filename is too large */ + error("too many output files or filename is too long (> %d)", PATH_MAX); + free(filename); +} + +static char * +get_next_file(FILE *VFile, char *ptr) +{ + char *ret; + + ret = fgets(ptr, PATH_MAX, VFile); + if (!ret) + return NULL; + + if (ptr[strlen(ptr) - 1] == '\n') + ptr[strlen(ptr) - 1] = '\0'; + + return ret; +} + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION +static int +tstamp_precision_from_string(const char *precision) +{ + if (strncmp(precision, "nano", strlen("nano")) == 0) + return PCAP_TSTAMP_PRECISION_NANO; + + if (strncmp(precision, "micro", strlen("micro")) == 0) + return PCAP_TSTAMP_PRECISION_MICRO; + + return -EINVAL; +} + +static const char * +tstamp_precision_to_string(int precision) +{ + switch (precision) { + + case PCAP_TSTAMP_PRECISION_MICRO: + return "micro"; + + case PCAP_TSTAMP_PRECISION_NANO: + return "nano"; + + default: + return "unknown"; + } +} +#endif + +#ifdef HAVE_CAPSICUM +/* + * Ensure that, on a dump file's descriptor, we have all the rights + * necessary to make the standard I/O library work with an fdopen()ed + * FILE * from that descriptor. + * + * A long time ago, in a galaxy far far away, AT&T decided that, instead + * of providing separate APIs for getting and setting the FD_ flags on a + * descriptor, getting and setting the O_ flags on a descriptor, and + * locking files, they'd throw them all into a kitchen-sink fcntl() call + * along the lines of ioctl(), the fact that ioctl() operations are + * largely specific to particular character devices but fcntl() operations + * are either generic to all descriptors or generic to all descriptors for + * regular files nonwithstanding. + * + * The Capsicum people decided that fine-grained control of descriptor + * operations was required, so that you need to grant permission for + * reading, writing, seeking, and fcntl-ing. The latter, courtesy of + * AT&T's decision, means that "fcntl-ing" isn't a thing, but a motley + * collection of things, so there are *individual* fcntls for which + * permission needs to be granted. + * + * The FreeBSD standard I/O people implemented some optimizations that + * requires that the standard I/O routines be able to determine whether + * the descriptor for the FILE * is open append-only or not; as that + * descriptor could have come from an open() rather than an fopen(), + * that requires that it be able to do an F_GETFL fcntl() to read + * the O_ flags. + * + * Tcpdump uses ftell() to determine how much data has been written + * to a file in order to, when used with -C, determine when it's time + * to rotate capture files. ftell() therefore needs to do an lseek() + * to find out the file offset and must, thanks to the aforementioned + * optimization, also know whether the descriptor is open append-only + * or not. + * + * The net result of all the above is that we need to grant CAP_SEEK, + * CAP_WRITE, and CAP_FCNTL with the CAP_FCNTL_GETFL subcapability. + * + * Perhaps this is the universe's way of saying that either + * + * 1) there needs to be an fopenat() call and a pcap_dump_openat() call + * using it, so that Capsicum-capable tcpdump wouldn't need to do + * an fdopen() + * + * or + * + * 2) there needs to be a cap_fdopen() call in the FreeBSD standard + * I/O library that knows what rights are needed by the standard + * I/O library, based on the open mode, and assigns them, perhaps + * with an additional argument indicating, for example, whether + * seeking should be allowed, so that tcpdump doesn't need to know + * what the standard I/O library happens to require this week. + */ +static void +set_dumper_capsicum_rights(pcap_dumper_t *p) +{ + int fd = fileno(pcap_dump_file(p)); + cap_rights_t rights; + + cap_rights_init(&rights, CAP_SEEK, CAP_WRITE, CAP_FCNTL); + if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) { + error("unable to limit dump descriptor"); + } + if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) { + error("unable to limit dump descriptor fcntls"); + } +} +#endif + int main(int argc, char **argv) { register int cnt, op, i; - bpf_u_int32 localnet, netmask; - register char *cp, *infile, *cmdbuf, *device, *RFileName, *WFileName; + bpf_u_int32 localnet =0 , netmask = 0; + int timezone_offset = 0; + register char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName; pcap_handler callback; - int type; + int dlt; + const char *dlt_name; struct bpf_program fcode; -#ifndef WIN32 +#ifndef _WIN32 RETSIGTYPE (*oldhandler)(int); #endif - struct print_info printinfo; struct dump_info dumpinfo; u_char *pcap_userdata; char ebuf[PCAP_ERRBUF_SIZE]; + char VFileLine[PATH_MAX + 1]; + char *username = NULL; + char *chroot_dir = NULL; + char *ret = NULL; + char *end; #ifdef HAVE_PCAP_FINDALLDEVS - pcap_if_t *devpointer; + pcap_if_t *dev, *devlist; int devnum; #endif -#ifdef WIN32 - DWORD dwVersion; - DWORD dwWindowsMajorVersion; - u_int UserBufferSize=1000000; -#endif - -#ifdef WIN32 - dwVersion=GetVersion(); /* get the OS version */ - dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); - if(wsockinit()!=0) return 1; -#endif /* WIN32 */ + int status; + FILE *VFile; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + int cansandbox; +#endif /* HAVE_CAPSICUM */ + int Bflag = 0; /* buffer size */ + int jflag = -1; /* packet time stamp source */ + int Oflag = 1; /* run filter code optimizer */ + int pflag = 0; /* don't go promiscuous */ + int yflag_dlt = -1; + const char *yflag_dlt_name = NULL; + + netdissect_options Ndo; + netdissect_options *ndo = &Ndo; + int immediate_mode = 0; + + memset(ndo, 0, sizeof(*ndo)); + ndo_set_function_pointers(ndo); + ndo->ndo_snaplen = DEFAULT_SNAPLEN; cnt = -1; device = NULL; infile = NULL; RFileName = NULL; + VFileName = NULL; + VFile = NULL; WFileName = NULL; + dlt = -1; if ((cp = strrchr(argv[0], '/')) != NULL) - program_name = cp + 1; + ndo->program_name = program_name = cp + 1; else - program_name = argv[0]; + ndo->program_name = program_name = argv[0]; + +#ifdef _WIN32 + if (pcap_wsockinit() != 0) + error("Attempting to initialize Winsock failed"); +#endif /* _WIN32 */ + /* + * On platforms where the CPU doesn't support unaligned loads, + * force unaligned accesses to abort with SIGBUS, rather than + * being fixed up (slowly) by the OS kernel; on those platforms, + * misaligned accesses are bugs, and we want tcpdump to crash so + * that the bugs are reported. + */ if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0) error("%s", ebuf); -#ifdef LIBSMI +#ifdef USE_LIBSMI smiInit("tcpdump"); #endif - opterr = 0; while ( - (op = getopt(argc, argv, "aA" B_FLAG "c:C:d" D_FLAG "eE:fF:i:lLm:nNOpqr:Rs:StT:u" U_FLAG "vw:xXy:Y")) != -1) + (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) switch (op) { case 'a': - ++aflag; + /* compatibility for old -a */ break; case 'A': - ++xflag; - ++Xflag; - ++Aflag; + ++ndo->ndo_Aflag; break; -#ifdef WIN32 + case 'b': + ++ndo->ndo_bflag; + break; + +#if defined(HAVE_PCAP_CREATE) || defined(_WIN32) case 'B': - UserBufferSize = atoi(optarg)*1024; - if (UserBufferSize < 0) + Bflag = atoi(optarg)*1024; + if (Bflag <= 0) error("invalid packet buffer size %s", optarg); break; -#endif /* WIN32 */ +#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ case 'c': cnt = atoi(optarg); @@ -373,7 +864,7 @@ main(int argc, char **argv) case 'C': Cflag = atoi(optarg) * 1000000; - if (Cflag < 0) + if (Cflag <= 0) error("invalid file size %s", optarg); break; @@ -381,49 +872,61 @@ main(int argc, char **argv) ++dflag; break; -#ifdef HAVE_PCAP_FINDALLDEVS case 'D': - if (pcap_findalldevs(&devpointer, ebuf) < 0) - error("%s", ebuf); - else { - for (i = 0; devpointer != 0; i++) { - printf("%d.%s", i+1, devpointer->name); - if (devpointer->description != NULL) - printf(" (%s)", devpointer->description); - printf("\n"); - devpointer = devpointer->next; - } - } - return 0; -#endif /* HAVE_PCAP_FINDALLDEVS */ + Dflag++; + break; case 'L': Lflag++; break; case 'e': - ++eflag; + ++ndo->ndo_eflag; break; case 'E': #ifndef HAVE_LIBCRYPTO warning("crypto code not compiled in"); #endif - espsecret = optarg; + ndo->ndo_espsecret = optarg; break; case 'f': - ++fflag; + ++ndo->ndo_fflag; break; case 'F': infile = optarg; break; + case 'G': + Gflag = atoi(optarg); + if (Gflag < 0) + error("invalid number of seconds %s", optarg); + + /* We will create one file initially. */ + Gflag_count = 0; + + /* Grab the current time for rotation use. */ + if ((Gflag_time = time(NULL)) == (time_t)-1) { + error("main: can't get current time: %s", + pcap_strerror(errno)); + } + break; + + case 'h': + print_usage(); + exit(0); + break; + + case 'H': + ++ndo->ndo_Hflag; + break; + case 'i': if (optarg[0] == '0' && optarg[1] == 0) error("Invalid adapter index"); - + #ifdef HAVE_PCAP_FINDALLDEVS /* * If the argument is a number, treat it as @@ -435,53 +938,102 @@ main(int argc, char **argv) * It can be useful on Windows, where more than * one interface can have the same name. */ - if ((devnum = atoi(optarg)) != 0) { + devnum = strtol(optarg, &end, 10); + if (optarg != end && *end == '\0') { if (devnum < 0) error("Invalid adapter index"); - if (pcap_findalldevs(&devpointer, ebuf) < 0) + if (pcap_findalldevs(&devlist, ebuf) < 0) error("%s", ebuf); - else { - for (i = 0; i < devnum-1; i++){ - devpointer = devpointer->next; - if (devpointer == NULL) - error("Invalid adapter index"); - } - } - device = devpointer->name; + /* + * Look for the devnum-th entry in the + * list of devices (1-based). + */ + for (i = 0, dev = devlist; + i < devnum-1 && dev != NULL; + i++, dev = dev->next) + ; + if (dev == NULL) + error("Invalid adapter index"); + device = strdup(dev->name); + pcap_freealldevs(devlist); break; } #endif /* HAVE_PCAP_FINDALLDEVS */ device = optarg; break; +#ifdef HAVE_PCAP_CREATE + case 'I': + ++Iflag; + break; +#endif /* HAVE_PCAP_CREATE */ + +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + case 'j': + jflag = pcap_tstamp_type_name_to_val(optarg); + if (jflag < 0) + error("invalid time stamp type %s", optarg); + break; + + case 'J': + Jflag++; + break; +#endif + case 'l': +#ifdef _WIN32 + /* + * _IOLBF is the same as _IOFBF in Microsoft's C + * libraries; the only alternative they offer + * is _IONBF. + * + * XXX - this should really be checking for MSVC++, + * not _WIN32, if, for example, MinGW has its own + * C library that is more UNIX-compatible. + */ + setvbuf(stdout, NULL, _IONBF, 0); +#else /* _WIN32 */ #ifdef HAVE_SETLINEBUF setlinebuf(stdout); #else setvbuf(stdout, NULL, _IOLBF, 0); #endif +#endif /* _WIN32 */ break; - case 'n': - ++nflag; - break; - - case 'N': - ++Nflag; + case 'K': + ++ndo->ndo_Kflag; break; case 'm': -#ifdef LIBSMI - if (smiLoadModule(optarg) == 0) { +#ifdef USE_LIBSMI + if (smiLoadModule(optarg) == 0) { error("could not load MIB module %s", optarg); - } - sflag = 1; + } + ndo->ndo_mflag = 1; #else (void)fprintf(stderr, "%s: ignoring option `-m %s' ", program_name, optarg); (void)fprintf(stderr, "(no libsmi support)\n"); #endif + break; + + case 'M': + /* TCP-MD5 shared secret */ +#ifndef HAVE_LIBCRYPTO + warning("crypto code not compiled in"); +#endif + ndo->ndo_sigsecret = optarg; + break; + + case 'n': + ++ndo->ndo_nflag; + break; + + case 'N': + ++ndo->ndo_Nflag; + break; case 'O': Oflag = 0; @@ -492,58 +1044,85 @@ main(int argc, char **argv) break; case 'q': - ++qflag; + ++ndo->ndo_qflag; + ++ndo->ndo_suppress_default_print; break; - case 'r': - RFileName = optarg; +#ifdef HAVE_PCAP_SETDIRECTION + case 'Q': + if (ascii_strcasecmp(optarg, "in") == 0) + Qflag = PCAP_D_IN; + else if (ascii_strcasecmp(optarg, "out") == 0) + Qflag = PCAP_D_OUT; + else if (ascii_strcasecmp(optarg, "inout") == 0) + Qflag = PCAP_D_INOUT; + else + error("unknown capture direction `%s'", optarg); break; +#endif /* HAVE_PCAP_SETDIRECTION */ - case 'R': - Rflag = 0; + case 'r': + RFileName = optarg; break; - case 's': { - char *end; - - snaplen = strtol(optarg, &end, 0); + case 's': + ndo->ndo_snaplen = strtol(optarg, &end, 0); if (optarg == end || *end != '\0' - || snaplen < 0 || snaplen > 65535) + || ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); - else if (snaplen == 0) - snaplen = 65535; + else if (ndo->ndo_snaplen == 0) + ndo->ndo_snaplen = MAXIMUM_SNAPLEN; break; - } case 'S': - ++Sflag; + ++ndo->ndo_Sflag; break; case 't': - --tflag; + ++ndo->ndo_tflag; break; case 'T': - if (strcasecmp(optarg, "vat") == 0) - packettype = PT_VAT; - else if (strcasecmp(optarg, "wb") == 0) - packettype = PT_WB; - else if (strcasecmp(optarg, "rpc") == 0) - packettype = PT_RPC; - else if (strcasecmp(optarg, "rtp") == 0) - packettype = PT_RTP; - else if (strcasecmp(optarg, "rtcp") == 0) - packettype = PT_RTCP; - else if (strcasecmp(optarg, "snmp") == 0) - packettype = PT_SNMP; - else if (strcasecmp(optarg, "cnfp") == 0) - packettype = PT_CNFP; + if (ascii_strcasecmp(optarg, "vat") == 0) + ndo->ndo_packettype = PT_VAT; + else if (ascii_strcasecmp(optarg, "wb") == 0) + ndo->ndo_packettype = PT_WB; + else if (ascii_strcasecmp(optarg, "rpc") == 0) + ndo->ndo_packettype = PT_RPC; + else if (ascii_strcasecmp(optarg, "rtp") == 0) + ndo->ndo_packettype = PT_RTP; + else if (ascii_strcasecmp(optarg, "rtcp") == 0) + ndo->ndo_packettype = PT_RTCP; + else if (ascii_strcasecmp(optarg, "snmp") == 0) + ndo->ndo_packettype = PT_SNMP; + else if (ascii_strcasecmp(optarg, "cnfp") == 0) + ndo->ndo_packettype = PT_CNFP; + else if (ascii_strcasecmp(optarg, "tftp") == 0) + ndo->ndo_packettype = PT_TFTP; + else if (ascii_strcasecmp(optarg, "aodv") == 0) + ndo->ndo_packettype = PT_AODV; + else if (ascii_strcasecmp(optarg, "carp") == 0) + ndo->ndo_packettype = PT_CARP; + else if (ascii_strcasecmp(optarg, "radius") == 0) + ndo->ndo_packettype = PT_RADIUS; + else if (ascii_strcasecmp(optarg, "zmtp1") == 0) + ndo->ndo_packettype = PT_ZMTP1; + else if (ascii_strcasecmp(optarg, "vxlan") == 0) + ndo->ndo_packettype = PT_VXLAN; + else if (ascii_strcasecmp(optarg, "pgm") == 0) + ndo->ndo_packettype = PT_PGM; + else if (ascii_strcasecmp(optarg, "pgm_zmtp1") == 0) + ndo->ndo_packettype = PT_PGM_ZMTP1; + else if (ascii_strcasecmp(optarg, "lmp") == 0) + ndo->ndo_packettype = PT_LMP; + else if (ascii_strcasecmp(optarg, "resp") == 0) + ndo->ndo_packettype = PT_RESP; else error("unknown packet type `%s'", optarg); break; case 'u': - ++uflag; + ++ndo->ndo_uflag; break; #ifdef HAVE_PCAP_DUMP_FLUSH @@ -553,97 +1132,375 @@ main(int argc, char **argv) #endif case 'v': - ++vflag; + ++ndo->ndo_vflag; + break; + + case 'V': + VFileName = optarg; break; case 'w': WFileName = optarg; break; + case 'W': + Wflag = atoi(optarg); + if (Wflag <= 0) + error("invalid number of output files %s", optarg); + WflagChars = getWflagChars(Wflag); + break; + case 'x': - ++xflag; + ++ndo->ndo_xflag; + ++ndo->ndo_suppress_default_print; break; case 'X': - ++xflag; - ++Xflag; + ++ndo->ndo_Xflag; + ++ndo->ndo_suppress_default_print; break; case 'y': - dlt_name = optarg; - dlt = pcap_datalink_name_to_val(dlt_name); - if (dlt < 0) - error("invalid data link type %s", dlt_name); + yflag_dlt_name = optarg; + yflag_dlt = + pcap_datalink_name_to_val(yflag_dlt_name); + if (yflag_dlt < 0) + error("invalid data link type %s", yflag_dlt_name); break; -#if defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG) +#ifdef HAVE_PCAP_SET_PARSER_DEBUG case 'Y': { /* Undocumented flag */ -#ifdef HAVE_PCAP_DEBUG - extern int pcap_debug; - pcap_debug = 1; -#else - extern int yydebug; - yydebug = 1; -#endif + pcap_set_parser_debug(1); } break; #endif + case 'z': + zflag = optarg; + break; + + case 'Z': + username = optarg; + break; + + case '#': + ndo->ndo_packet_number = 1; + break; + + case OPTION_VERSION: + print_version(); + exit(0); + break; + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + case OPTION_TSTAMP_PRECISION: + ndo->ndo_tstamp_precision = tstamp_precision_from_string(optarg); + if (ndo->ndo_tstamp_precision < 0) + error("unsupported time stamp precision"); + break; +#endif + +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + case OPTION_IMMEDIATE_MODE: + immediate_mode = 1; + break; +#endif + default: - usage(); + print_usage(); + exit(1); /* NOTREACHED */ } - if (aflag && nflag) - error("-a and -n options are incompatible"); +#ifdef HAVE_PCAP_FINDALLDEVS + if (Dflag) + show_devices_and_exit(); +#endif + + switch (ndo->ndo_tflag) { + + case 0: /* Default */ + case 4: /* Default + Date*/ + timezone_offset = gmt2local(0); + break; + + case 1: /* No time stamp */ + case 2: /* Unix timeval style */ + case 3: /* Microseconds since previous packet */ + case 5: /* Microseconds since first packet */ + break; + + default: /* Not supported */ + error("only -t, -tt, -ttt, -tttt and -ttttt are supported"); + break; + } + + if (ndo->ndo_fflag != 0 && (VFileName != NULL || RFileName != NULL)) + error("-f can not be used with -V or -r"); - if (tflag > 0) - thiszone = gmt2local(0); + if (VFileName != NULL && RFileName != NULL) + error("-V and -r are mutually exclusive."); - if (RFileName != NULL) { +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + /* + * If we're printing dissected packets to the standard output + * rather than saving raw packets to a file, and the standard + * output is a terminal, use immediate mode, as the user's + * probably expecting to see packets pop up immediately. + */ + if (WFileName == NULL && isatty(1)) + immediate_mode = 1; +#endif + +#ifdef WITH_CHROOT + /* if run as root, prepare for chrooting */ + if (getuid() == 0 || geteuid() == 0) { + /* future extensibility for cmd-line arguments */ + if (!chroot_dir) + chroot_dir = WITH_CHROOT; + } +#endif + +#ifdef WITH_USER + /* if run as root, prepare for dropping root privileges */ + if (getuid() == 0 || geteuid() == 0) { + /* Run with '-Z root' to restore old behaviour */ + if (!username) + username = WITH_USER; + } +#endif + + if (RFileName != NULL || VFileName != NULL) { + /* + * If RFileName is non-null, it's the pathname of a + * savefile to read. If VFileName is non-null, it's + * the pathname of a file containing a list of pathnames + * (one per line) of savefiles to read. + * + * In either case, we're reading a savefile, not doing + * a live capture. + */ +#ifndef _WIN32 + /* + * We don't need network access, so relinquish any set-UID + * or set-GID privileges we have (if any). + * + * We do *not* want set-UID privileges when opening a + * trace file, as that might let the user read other + * people's trace files (especially if we're set-UID + * root). + */ + if (setgid(getgid()) != 0 || setuid(getuid()) != 0 ) + fprintf(stderr, "Warning: setgid/setuid failed !\n"); +#endif /* _WIN32 */ + if (VFileName != NULL) { + if (VFileName[0] == '-' && VFileName[1] == '\0') + VFile = stdin; + else + VFile = fopen(VFileName, "r"); + + if (VFile == NULL) + error("Unable to open file: %s\n", pcap_strerror(errno)); + + ret = get_next_file(VFile, VFileLine); + if (!ret) + error("Nothing in %s\n", VFileName); + RFileName = VFileLine; + } + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + pd = pcap_open_offline_with_tstamp_precision(RFileName, + ndo->ndo_tstamp_precision, ebuf); +#else + pd = pcap_open_offline(RFileName, ebuf); +#endif + + if (pd == NULL) + error("%s", ebuf); +#ifdef HAVE_CAPSICUM + cap_rights_init(&rights, CAP_READ); + if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 && + errno != ENOSYS) { + error("unable to limit pcap descriptor"); + } +#endif + dlt = pcap_datalink(pd); + dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name == NULL) { + fprintf(stderr, "reading from file %s, link-type %u\n", + RFileName, dlt); + } else { + fprintf(stderr, + "reading from file %s, link-type %s (%s)\n", + RFileName, dlt_name, + pcap_datalink_val_to_description(dlt)); + } + } else { /* - * We don't need network access, so set it back to the user id. - * Also, this prevents the user from reading anyone's - * trace file. + * We're doing a live capture. */ -#ifndef WIN32 - setuid(getuid()); -#endif /* WIN32 */ + if (device == NULL) { +#ifdef HAVE_PCAP_FINDALLDEVS + if (pcap_findalldevs(&devlist, ebuf) >= 0 && + devlist != NULL) { + device = strdup(devlist->name); + pcap_freealldevs(devlist); + } +#else /* HAVE_PCAP_FINDALLDEVS */ + device = pcap_lookupdev(ebuf); +#endif + if (device == NULL) + error("%s", ebuf); + } +#ifdef _WIN32 + /* + * Print a message to the standard error on Windows. + * XXX - why do it here, with a different message? + */ + if(strlen(device) == 1) /* we assume that an ASCII string is always longer than 1 char */ + { /* a Unicode string has a \0 as second byte (so strlen() is 1) */ + fprintf(stderr, "%s: listening on %ws\n", program_name, device); + } + else + { + fprintf(stderr, "%s: listening on %s\n", program_name, device); + } - pd = pcap_open_offline(RFileName, ebuf); + fflush(stderr); +#endif /* _WIN32 */ +#ifdef HAVE_PCAP_CREATE + pd = pcap_create(device, ebuf); if (pd == NULL) error("%s", ebuf); - localnet = 0; - netmask = 0; - if (fflag != 0) - error("-f and -r options are incompatible"); - } else { - if (device == NULL) { - device = pcap_lookupdev(ebuf); - if (device == NULL) - error("%s", ebuf); +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + if (Jflag) + show_tstamp_types_and_exit(device); +#endif +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + status = pcap_set_tstamp_precision(pd, ndo->ndo_tstamp_precision); + if (status != 0) + error("%s: Can't set %ssecond time stamp precision: %s", + device, + tstamp_precision_to_string(ndo->ndo_tstamp_precision), + pcap_statustostr(status)); +#endif + +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + if (immediate_mode) { + status = pcap_set_immediate_mode(pd, 1); + if (status != 0) + error("%s: Can't set immediate mode: %s", + device, + pcap_statustostr(status)); + } +#endif + /* + * Is this an interface that supports monitor mode? + */ + if (pcap_can_set_rfmon(pd) == 1) + supports_monitor_mode = 1; + else + supports_monitor_mode = 0; + status = pcap_set_snaplen(pd, ndo->ndo_snaplen); + if (status != 0) + error("%s: Can't set snapshot length: %s", + device, pcap_statustostr(status)); + status = pcap_set_promisc(pd, !pflag); + if (status != 0) + error("%s: Can't set promiscuous mode: %s", + device, pcap_statustostr(status)); + if (Iflag) { + status = pcap_set_rfmon(pd, 1); + if (status != 0) + error("%s: Can't set monitor mode: %s", + device, pcap_statustostr(status)); + } + status = pcap_set_timeout(pd, 1000); + if (status != 0) + error("%s: pcap_set_timeout failed: %s", + device, pcap_statustostr(status)); + if (Bflag != 0) { + status = pcap_set_buffer_size(pd, Bflag); + if (status != 0) + error("%s: Can't set buffer size: %s", + device, pcap_statustostr(status)); + } +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + if (jflag != -1) { + status = pcap_set_tstamp_type(pd, jflag); + if (status < 0) + error("%s: Can't set time stamp type: %s", + device, pcap_statustostr(status)); + } +#endif + status = pcap_activate(pd); + if (status < 0) { + /* + * pcap_activate() failed. + */ + cp = pcap_geterr(pd); + if (status == PCAP_ERROR) + error("%s", cp); + else if ((status == PCAP_ERROR_NO_SUCH_DEVICE || + status == PCAP_ERROR_PERM_DENIED) && + *cp != '\0') + error("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + error("%s: %s", device, + pcap_statustostr(status)); + } else if (status > 0) { + /* + * pcap_activate() succeeded, but it's warning us + * of a problem it had. + */ + cp = pcap_geterr(pd); + if (status == PCAP_WARNING) + warning("%s", cp); + else if (status == PCAP_WARNING_PROMISC_NOTSUP && + *cp != '\0') + warning("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + warning("%s: %s", device, + pcap_statustostr(status)); + } +#ifdef HAVE_PCAP_SETDIRECTION + if (Qflag != -1) { + status = pcap_setdirection(pd, Qflag); + if (status != 0) + error("%s: pcap_setdirection() failed: %s", + device, pcap_geterr(pd)); } -#ifdef WIN32 - PrintCapBegins(program_name,device); -#endif /* WIN32 */ +#endif /* HAVE_PCAP_SETDIRECTION */ +#else *ebuf = '\0'; - pd = pcap_open_live(device, snaplen, !pflag, 1000, ebuf); + pd = pcap_open_live(device, ndo->ndo_snaplen, !pflag, 1000, + ebuf); if (pd == NULL) error("%s", ebuf); else if (*ebuf) warning("%s", ebuf); -#ifdef WIN32 - if(UserBufferSize != 1000000) - if(pcap_setbuff(pd, UserBufferSize)==-1){ +#endif /* HAVE_PCAP_CREATE */ + /* + * Let user own process after socket has been opened. + */ +#ifndef _WIN32 + if (setgid(getgid()) != 0 || setuid(getuid()) != 0) + fprintf(stderr, "Warning: setgid/setuid failed !\n"); +#endif /* _WIN32 */ +#if !defined(HAVE_PCAP_CREATE) && defined(_WIN32) + if(Bflag != 0) + if(pcap_setbuff(pd, Bflag)==-1){ error("%s", pcap_geterr(pd)); } -#endif /* WIN32 */ +#endif /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */ if (Lflag) - show_dlts_and_exit(pd); - if (dlt >= 0) { + show_dlts_and_exit(device); + if (yflag_dlt >= 0) { #ifdef HAVE_PCAP_SET_DATALINK - if (pcap_set_datalink(pd, dlt) < 0) + if (pcap_set_datalink(pd, yflag_dlt) < 0) error("%s", pcap_geterr(pd)); #else /* @@ -651,63 +1508,184 @@ main(int argc, char **argv) * data link type, so we only let them * set it to what it already is. */ - if (dlt != pcap_datalink(pd)) { + if (yflag_dlt != pcap_datalink(pd)) { error("%s is not one of the DLTs supported by this device\n", - dlt_name); + yflag_dlt_name); } #endif (void)fprintf(stderr, "%s: data link type %s\n", - program_name, dlt_name); + program_name, yflag_dlt_name); (void)fflush(stderr); } i = pcap_snapshot(pd); - if (snaplen < i) { - warning("snaplen raised from %d to %d", snaplen, i); - snaplen = i; + if (ndo->ndo_snaplen < i) { + warning("snaplen raised from %d to %d", ndo->ndo_snaplen, i); + ndo->ndo_snaplen = i; } - if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { - localnet = 0; - netmask = 0; - warning("%s", ebuf); - } - /* - * Let user own process after socket has been opened. - */ -#ifndef WIN32 - setuid(getuid()); -#endif /* WIN32 */ + if(ndo->ndo_fflag != 0) { + if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { + warning("foreign (-f) flag used but: %s", ebuf); + } + } + } if (infile) cmdbuf = read_infile(infile); else cmdbuf = copy_argv(&argv[optind]); +#ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG + pcap_set_optimizer_debug(dflag); +#endif if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); if (dflag) { bpf_dump(&fcode, dflag); pcap_close(pd); + free(cmdbuf); + pcap_freecode(&fcode); exit(0); } - init_addrtoname(localnet, netmask); + init_print(ndo, localnet, netmask, timezone_offset); +#ifndef _WIN32 + (void)setsignal(SIGPIPE, cleanup); (void)setsignal(SIGTERM, cleanup); (void)setsignal(SIGINT, cleanup); +#endif /* _WIN32 */ +#if defined(HAVE_FORK) || defined(HAVE_VFORK) + (void)setsignal(SIGCHLD, child_cleanup); +#endif /* Cooperate with nohup(1) */ -#ifndef WIN32 +#ifndef _WIN32 if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL) (void)setsignal(SIGHUP, oldhandler); -#endif /* WIN32 */ +#endif /* _WIN32 */ + +#ifndef _WIN32 + /* + * If a user name was specified with "-Z", attempt to switch to + * that user's UID. This would probably be used with sudo, + * to allow tcpdump to be run in a special restricted + * account (if you just want to allow users to open capture + * devices, and can't just give users that permission, + * you'd make tcpdump set-UID or set-GID). + * + * Tcpdump doesn't necessarily write only to one savefile; + * the general only way to allow a -Z instance to write to + * savefiles as the user under whose UID it's run, rather + * than as the user specified with -Z, would thus be to switch + * to the original user ID before opening a capture file and + * then switch back to the -Z user ID after opening the savefile. + * Switching to the -Z user ID only after opening the first + * savefile doesn't handle the general case. + */ + + if (getuid() == 0 || geteuid() == 0) { +#ifdef HAVE_LIBCAP_NG + /* Initialize capng */ + capng_clear(CAPNG_SELECT_BOTH); + if (username) { + capng_updatev( + CAPNG_ADD, + CAPNG_PERMITTED | CAPNG_EFFECTIVE, + CAP_SETUID, + CAP_SETGID, + -1); + } + + if (WFileName) { + capng_update( + CAPNG_ADD, + CAPNG_PERMITTED | CAPNG_EFFECTIVE, + CAP_DAC_OVERRIDE + ); + } + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ + if (username || chroot_dir) + droproot(username, chroot_dir); + + } +#endif /* _WIN32 */ if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); +#ifdef HAVE_CAPSICUM + if (RFileName == NULL && VFileName == NULL) { + static const unsigned long cmds[] = { BIOCGSTATS, BIOCROTZBUF }; + + cap_rights_init(&rights, CAP_IOCTL, CAP_READ); + if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 && + errno != ENOSYS) { + error("unable to limit pcap descriptor"); + } + if (cap_ioctls_limit(pcap_fileno(pd), cmds, + sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) { + error("unable to limit ioctls on pcap descriptor"); + } + } +#endif if (WFileName) { - pcap_dumper_t *p = pcap_dump_open(pd, WFileName); + pcap_dumper_t *p; + /* Do not exceed the default PATH_MAX for files. */ + dumpinfo.CurrentFileName = (char *)malloc(PATH_MAX + 1); + + if (dumpinfo.CurrentFileName == NULL) + error("malloc of dumpinfo.CurrentFileName"); + + /* We do not need numbering for dumpfiles if Cflag isn't set. */ + if (Cflag != 0) + MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars); + else + MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0); + + p = pcap_dump_open(pd, dumpinfo.CurrentFileName); +#ifdef HAVE_LIBCAP_NG + /* Give up CAP_DAC_OVERRIDE capability. + * Only allow it to be restored if the -C or -G flag have been + * set since we may need to create more files later on. + */ + capng_update( + CAPNG_DROP, + (Cflag || Gflag ? 0 : CAPNG_PERMITTED) + | CAPNG_EFFECTIVE, + CAP_DAC_OVERRIDE + ); + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ if (p == NULL) error("%s", pcap_geterr(pd)); - if (Cflag != 0) { - callback = dump_packet_and_trunc; +#ifdef HAVE_CAPSICUM + set_dumper_capsicum_rights(p); +#endif + if (Cflag != 0 || Gflag != 0) { +#ifdef HAVE_CAPSICUM + dumpinfo.WFileName = strdup(basename(WFileName)); + if (dumpinfo.WFileName == NULL) { + error("Unable to allocate memory for file %s", + WFileName); + } + dumpinfo.dirfd = open(dirname(WFileName), + O_DIRECTORY | O_RDONLY); + if (dumpinfo.dirfd < 0) { + error("unable to open directory %s", + dirname(WFileName)); + } + cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, + CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE); + if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 && + errno != ENOSYS) { + error("unable to limit directory rights"); + } + if (cap_fcntls_limit(dumpinfo.dirfd, CAP_FCNTL_GETFL) < 0 && + errno != ENOSYS) { + error("unable to limit dump descriptor fcntls"); + } +#else /* !HAVE_CAPSICUM */ dumpinfo.WFileName = WFileName; +#endif + callback = dump_packet_and_trunc; dumpinfo.pd = pd; dumpinfo.p = p; pcap_userdata = (u_char *)&dumpinfo; @@ -715,140 +1693,534 @@ main(int argc, char **argv) callback = dump_packet; pcap_userdata = (u_char *)p; } +#ifdef HAVE_PCAP_DUMP_FLUSH + if (Uflag) + pcap_dump_flush(p); +#endif } else { - type = pcap_datalink(pd); - printinfo.printer = lookup_printer(type); - if (printinfo.printer == NULL) { - dlt_name = pcap_datalink_val_to_name(type); - if (dlt_name != NULL) - error("unsupported data link type %s", dlt_name); - else - error("unsupported data link type %d", type); - } + dlt = pcap_datalink(pd); + ndo->ndo_if_printer = get_if_printer(ndo, dlt); callback = print_packet; - pcap_userdata = (u_char *)&printinfo; + pcap_userdata = (u_char *)ndo; } -#ifdef SIGINFO - (void)setsignal(SIGINFO, requestinfo); + +#ifdef SIGNAL_REQ_INFO + /* + * We can't get statistics when reading from a file rather + * than capturing from a device. + */ + if (RFileName == NULL) + (void)setsignal(SIGNAL_REQ_INFO, requestinfo); +#endif + + if (ndo->ndo_vflag > 0 && WFileName) { + /* + * When capturing to a file, "-v" means tcpdump should, + * every 10 seconds, "v"erbosely report the number of + * packets captured. + */ +#ifdef USE_WIN32_MM_TIMER + /* call verbose_stats_dump() each 1000 +/-100msec */ + timer_id = timeSetEvent(1000, 100, verbose_stats_dump, 0, TIME_PERIODIC); + setvbuf(stderr, NULL, _IONBF, 0); +#elif defined(HAVE_ALARM) + (void)setsignal(SIGALRM, verbose_stats_dump); + alarm(1); #endif -#ifndef WIN32 + } + +#ifndef _WIN32 if (RFileName == NULL) { - if (!vflag && !WFileName) { + /* + * Live capture (if -V was specified, we set RFileName + * to a file from the -V file). Print a message to + * the standard error on UN*X. + */ + if (!ndo->ndo_vflag && !WFileName) { (void)fprintf(stderr, "%s: verbose output suppressed, use -v or -vv for full protocol decode\n", program_name); } else (void)fprintf(stderr, "%s: ", program_name); - (void)fprintf(stderr, "listening on %s, capture size %u bytes\n", - device, snaplen); + dlt = pcap_datalink(pd); + dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name == NULL) { + (void)fprintf(stderr, "listening on %s, link-type %u, capture size %u bytes\n", + device, dlt, ndo->ndo_snaplen); + } else { + (void)fprintf(stderr, "listening on %s, link-type %s (%s), capture size %u bytes\n", + device, dlt_name, + pcap_datalink_val_to_description(dlt), ndo->ndo_snaplen); + } (void)fflush(stderr); } -#endif /* WIN32 */ - if (pcap_loop(pd, cnt, callback, pcap_userdata) < 0) { - (void)fprintf(stderr, "%s: pcap_loop: %s\n", - program_name, pcap_geterr(pd)); - cleanup(0); +#endif /* _WIN32 */ + +#ifdef HAVE_CAPSICUM + cansandbox = (ndo->ndo_nflag && VFileName == NULL && zflag == NULL); + if (cansandbox && cap_enter() < 0 && errno != ENOSYS) + error("unable to enter the capability mode"); +#endif /* HAVE_CAPSICUM */ + + do { + status = pcap_loop(pd, cnt, callback, pcap_userdata); + if (WFileName == NULL) { + /* + * We're printing packets. Flush the printed output, + * so it doesn't get intermingled with error output. + */ + if (status == -2) { + /* + * We got interrupted, so perhaps we didn't + * manage to finish a line we were printing. + * Print an extra newline, just in case. + */ + putchar('\n'); + } + (void)fflush(stdout); + } + if (status == -2) { + /* + * We got interrupted. If we are reading multiple + * files (via -V) set these so that we stop. + */ + VFileName = NULL; + ret = NULL; + } + if (status == -1) { + /* + * Error. Report it. + */ + (void)fprintf(stderr, "%s: pcap_loop: %s\n", + program_name, pcap_geterr(pd)); + } + if (RFileName == NULL) { + /* + * We're doing a live capture. Report the capture + * statistics. + */ + info(1); + } pcap_close(pd); - exit(1); + if (VFileName != NULL) { + ret = get_next_file(VFile, VFileLine); + if (ret) { + int new_dlt; + + RFileName = VFileLine; + pd = pcap_open_offline(RFileName, ebuf); + if (pd == NULL) + error("%s", ebuf); +#ifdef HAVE_CAPSICUM + cap_rights_init(&rights, CAP_READ); + if (cap_rights_limit(fileno(pcap_file(pd)), + &rights) < 0 && errno != ENOSYS) { + error("unable to limit pcap descriptor"); + } +#endif + new_dlt = pcap_datalink(pd); + if (new_dlt != dlt) { + /* + * The new file has a different + * link-layer header type from the + * previous one. + */ + if (WFileName != NULL) { + /* + * We're writing raw packets + * that match the filter to + * a pcap file. pcap files + * don't support multiple + * different link-layer + * header types, so we fail + * here. + */ + error("%s: new dlt does not match original", RFileName); + } + + /* + * We're printing the decoded packets; + * switch to the new DLT. + * + * To do that, we need to change + * the printer, change the DLT name, + * and recompile the filter with + * the new DLT. + */ + dlt = new_dlt; + ndo->ndo_if_printer = get_if_printer(ndo, dlt); + if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) + error("%s", pcap_geterr(pd)); + } + + /* + * Set the filter on the new file. + */ + if (pcap_setfilter(pd, &fcode) < 0) + error("%s", pcap_geterr(pd)); + + /* + * Report the new file. + */ + dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name == NULL) { + fprintf(stderr, "reading from file %s, link-type %u\n", + RFileName, dlt); + } else { + fprintf(stderr, + "reading from file %s, link-type %s (%s)\n", + RFileName, dlt_name, + pcap_datalink_val_to_description(dlt)); + } + } + } } - if (RFileName == NULL) - info(1); - pcap_close(pd); - exit(0); + while (ret != NULL); + + free(cmdbuf); + pcap_freecode(&fcode); + exit(status == -1 ? 1 : 0); } /* make a clean exit on interrupts */ static RETSIGTYPE -cleanup(int signo) +cleanup(int signo _U_) { +#ifdef USE_WIN32_MM_TIMER + if (timer_id) + timeKillEvent(timer_id); + timer_id = 0; +#elif defined(HAVE_ALARM) + alarm(0); +#endif - /* Can't print the summary if reading from a savefile */ +#ifdef HAVE_PCAP_BREAKLOOP + /* + * We have "pcap_breakloop()"; use it, so that we do as little + * as possible in the signal handler (it's probably not safe + * to do anything with standard I/O streams in a signal handler - + * the ANSI C standard doesn't say it is). + */ + pcap_breakloop(pd); +#else + /* + * We don't have "pcap_breakloop()"; this isn't safe, but + * it's the best we can do. Print the summary if we're + * not reading from a savefile - i.e., if we're doing a + * live capture - and exit. + */ if (pd != NULL && pcap_file(pd) == NULL) { + /* + * We got interrupted, so perhaps we didn't + * manage to finish a line we were printing. + * Print an extra newline, just in case. + */ + putchar('\n'); (void)fflush(stdout); - putc('\n', stderr); info(1); } - if (signo) - exit(0); + exit(0); +#endif +} + +/* + On windows, we do not use a fork, so we do not care less about + waiting a child processes to die + */ +#if defined(HAVE_FORK) || defined(HAVE_VFORK) +static RETSIGTYPE +child_cleanup(int signo _U_) +{ + wait(NULL); } +#endif /* HAVE_FORK && HAVE_VFORK */ static void info(register int verbose) { - struct pcap_stat stat; + struct pcap_stat stats; - if (pcap_stats(pd, &stat) < 0) { + /* + * Older versions of libpcap didn't set ps_ifdrop on some + * platforms; initialize it to 0 to handle that. + */ + stats.ps_ifdrop = 0; + if (pcap_stats(pd, &stats) < 0) { (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd)); + infoprint = 0; return; } if (!verbose) fprintf(stderr, "%s: ", program_name); - (void)fprintf(stderr, "%d packets received by filter", stat.ps_recv); + (void)fprintf(stderr, "%u packet%s captured", packets_captured, + PLURAL_SUFFIX(packets_captured)); + if (!verbose) + fputs(", ", stderr); + else + putc('\n', stderr); + (void)fprintf(stderr, "%u packet%s received by filter", stats.ps_recv, + PLURAL_SUFFIX(stats.ps_recv)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); - (void)fprintf(stderr, "%d packets dropped by kernel\n", stat.ps_drop); + (void)fprintf(stderr, "%u packet%s dropped by kernel", stats.ps_drop, + PLURAL_SUFFIX(stats.ps_drop)); + if (stats.ps_ifdrop != 0) { + if (!verbose) + fputs(", ", stderr); + else + putc('\n', stderr); + (void)fprintf(stderr, "%u packet%s dropped by interface\n", + stats.ps_ifdrop, PLURAL_SUFFIX(stats.ps_ifdrop)); + } else + putc('\n', stderr); infoprint = 0; } +#if defined(HAVE_FORK) || defined(HAVE_VFORK) +#ifdef HAVE_FORK +#define fork_subprocess() fork() +#else +#define fork_subprocess() vfork() +#endif static void -reverse(char *s) +compress_savefile(const char *filename) { - int i, j, c; + pid_t child; - for (i = 0, j = strlen(s) - 1; i < j; i++, j--) { - c = s[i]; - s[i] = s[j]; - s[j] = c; + child = fork_subprocess(); + if (child == -1) { + fprintf(stderr, + "compress_savefile: fork failed: %s\n", + pcap_strerror(errno)); + return; + } + if (child != 0) { + /* Parent process. */ + return; } -} - + /* + * Child process. + * Set to lowest priority so that this doesn't disturb the capture. + */ +#ifdef NZERO + setpriority(PRIO_PROCESS, 0, NZERO - 1); +#else + setpriority(PRIO_PROCESS, 0, 19); +#endif + if (execlp(zflag, zflag, filename, (char *)NULL) == -1) + fprintf(stderr, + "compress_savefile: execlp(%s, %s) failed: %s\n", + zflag, + filename, + pcap_strerror(errno)); +#ifdef HAVE_FORK + exit(1); +#else + _exit(1); +#endif +} +#else /* HAVE_FORK && HAVE_VFORK */ static void -swebitoa(unsigned int n, char *s) +compress_savefile(const char *filename) { - unsigned int i; - - i = 0; - do { - s[i++] = n % 10 + '0'; - } while ((n /= 10) > 0); - - s[i] = '\0'; - reverse(s); + fprintf(stderr, + "compress_savefile failed. Functionality not implemented under your system\n"); } +#endif /* HAVE_FORK && HAVE_VFORK */ static void dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { struct dump_info *dump_info; - static uint cnt = 2; - char *name; + + ++packets_captured; ++infodelay; dump_info = (struct dump_info *)user; + /* + * XXX - this won't force the file to rotate on the specified time + * boundary, but it will rotate on the first packet received after the + * specified Gflag number of seconds. Note: if a Gflag time boundary + * and a Cflag size boundary coincide, the time rotation will occur + * first thereby cancelling the Cflag boundary (since the file should + * be 0). + */ + if (Gflag != 0) { + /* Check if it is time to rotate */ + time_t t; + + /* Get the current time */ + if ((t = time(NULL)) == (time_t)-1) { + error("dump_and_trunc_packet: can't get current_time: %s", + pcap_strerror(errno)); + } + + + /* If the time is greater than the specified window, rotate */ + if (t - Gflag_time >= Gflag) { +#ifdef HAVE_CAPSICUM + FILE *fp; + int fd; +#endif + + /* Update the Gflag_time */ + Gflag_time = t; + /* Update Gflag_count */ + Gflag_count++; + /* + * Close the current file and open a new one. + */ + pcap_dump_close(dump_info->p); + + /* + * Compress the file we just closed, if the user asked for it + */ + if (zflag != NULL) + compress_savefile(dump_info->CurrentFileName); + + /* + * Check to see if we've exceeded the Wflag (when + * not using Cflag). + */ + if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) { + (void)fprintf(stderr, "Maximum file limit reached: %d\n", + Wflag); + exit(0); + /* NOTREACHED */ + } + if (dump_info->CurrentFileName != NULL) + free(dump_info->CurrentFileName); + /* Allocate space for max filename + \0. */ + dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1); + if (dump_info->CurrentFileName == NULL) + error("dump_packet_and_trunc: malloc"); + /* + * Gflag was set otherwise we wouldn't be here. Reset the count + * so multiple files would end with 1,2,3 in the filename. + * The counting is handled with the -C flow after this. + */ + Cflag_count = 0; + + /* + * This is always the first file in the Cflag + * rotation: e.g. 0 + * We also don't need numbering if Cflag is not set. + */ + if (Cflag != 0) + MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, + WflagChars); + else + MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0); + +#ifdef HAVE_LIBCAP_NG + capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ +#ifdef HAVE_CAPSICUM + fd = openat(dump_info->dirfd, + dump_info->CurrentFileName, + O_CREAT | O_WRONLY | O_TRUNC, 0644); + if (fd < 0) { + error("unable to open file %s", + dump_info->CurrentFileName); + } + fp = fdopen(fd, "w"); + if (fp == NULL) { + error("unable to fdopen file %s", + dump_info->CurrentFileName); + } + dump_info->p = pcap_dump_fopen(dump_info->pd, fp); +#else /* !HAVE_CAPSICUM */ + dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName); +#endif +#ifdef HAVE_LIBCAP_NG + capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ + if (dump_info->p == NULL) + error("%s", pcap_geterr(pd)); +#ifdef HAVE_CAPSICUM + set_dumper_capsicum_rights(dump_info->p); +#endif + } + } + /* * XXX - this won't prevent capture files from getting * larger than Cflag - the last packet written to the * file could put it over Cflag. */ - if (ftell((FILE *)dump_info->p) > Cflag) { - name = (char *) malloc(strlen(dump_info->WFileName) + 4); - if (name == NULL) - error("dump_packet_and_trunc: malloc"); - strcpy(name, dump_info->WFileName); - swebitoa(cnt, name + strlen(dump_info->WFileName)); - cnt++; - pcap_dump_close(dump_info->p); - dump_info->p = pcap_dump_open(dump_info->pd, name); - free(name); - if (dump_info->p == NULL) - error("%s", pcap_geterr(pd)); + if (Cflag != 0) { + long size = pcap_dump_ftell(dump_info->p); + + if (size == -1) + error("ftell fails on output file"); + if (size > Cflag) { +#ifdef HAVE_CAPSICUM + FILE *fp; + int fd; +#endif + + /* + * Close the current file and open a new one. + */ + pcap_dump_close(dump_info->p); + + /* + * Compress the file we just closed, if the user + * asked for it. + */ + if (zflag != NULL) + compress_savefile(dump_info->CurrentFileName); + + Cflag_count++; + if (Wflag > 0) { + if (Cflag_count >= Wflag) + Cflag_count = 0; + } + if (dump_info->CurrentFileName != NULL) + free(dump_info->CurrentFileName); + dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1); + if (dump_info->CurrentFileName == NULL) + error("dump_packet_and_trunc: malloc"); + MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars); +#ifdef HAVE_LIBCAP_NG + capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ +#ifdef HAVE_CAPSICUM + fd = openat(dump_info->dirfd, dump_info->CurrentFileName, + O_CREAT | O_WRONLY | O_TRUNC, 0644); + if (fd < 0) { + error("unable to open file %s", + dump_info->CurrentFileName); + } + fp = fdopen(fd, "w"); + if (fp == NULL) { + error("unable to fdopen file %s", + dump_info->CurrentFileName); + } + dump_info->p = pcap_dump_fopen(dump_info->pd, fp); +#else /* !HAVE_CAPSICUM */ + dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName); +#endif +#ifdef HAVE_LIBCAP_NG + capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); + capng_apply(CAPNG_SELECT_BOTH); +#endif /* HAVE_LIBCAP_NG */ + if (dump_info->p == NULL) + error("%s", pcap_geterr(pd)); +#ifdef HAVE_CAPSICUM + set_dumper_capsicum_rights(dump_info->p); +#endif + } } pcap_dump((u_char *)dump_info->p, h, sp); @@ -865,6 +2237,8 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s static void dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { + ++packets_captured; + ++infodelay; pcap_dump(user, h, sp); @@ -881,77 +2255,18 @@ dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) static void print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { - struct print_info *print_info; - u_int hdrlen; + ++packets_captured; ++infodelay; - ts_print(&h->ts); - - print_info = (struct print_info *)user; - - /* - * Some printers want to check that they're not walking off the - * end of the packet. - * Rather than pass it all the way down, we set this global. - */ - snapend = sp + h->caplen; - - hdrlen = (*print_info->printer)(h, sp); - if (xflag) { - /* - * Print the raw packet data. - */ - if (xflag > 1) { - /* - * Include the link-layer header. - */ - default_print_unaligned(sp, h->caplen); - } else { - /* - * Don't include the link-layer header - and if - * we have nothing past the link-layer header, - * print nothing. - */ - if (h->caplen > hdrlen) - default_print_unaligned(sp + hdrlen, - h->caplen - hdrlen); - } - } - putchar('\n'); + pretty_print_packet((netdissect_options *)user, h, sp, packets_captured); --infodelay; if (infoprint) info(0); } -/* Like default_print() but data need not be aligned */ -void -default_print_unaligned(register const u_char *cp, register u_int length) -{ - register u_int i, s; - register int nshorts; - - if (Xflag) { - ascii_print(cp, length); - return; - } - nshorts = (u_int) length / sizeof(u_short); - i = 0; - while (--nshorts >= 0) { - if ((i++ % 8) == 0) - (void)printf("\n\t\t\t"); - s = *cp++; - (void)printf(" %02x%02x", s, *cp++); - } - if (length & 1) { - if ((i % 8) == 0) - (void)printf("\n\t\t\t"); - (void)printf(" %02x", *cp); - } -} - -#ifdef WIN32 +#ifdef _WIN32 /* * XXX - there should really be libpcap calls to get the version * number as a string (the string would be generated from #defines @@ -970,22 +2285,15 @@ default_print_unaligned(register const u_char *cp, register u_int length) * version number of the Packet.dll code, to supply the * "Wpcap_version" information on Windows. */ - char WDversion[]="current-cvs.tcpdump.org"; - char version[]="current-cvs.tcpdump.org"; - char pcap_version[]="current-cvs.tcpdump.org"; - char Wpcap_version[]="3.0 alpha"; + char WDversion[]="current-git.tcpdump.org"; +#if !defined(HAVE_GENERATED_VERSION) + char version[]="current-git.tcpdump.org"; +#endif + char pcap_version[]="current-git.tcpdump.org"; + char Wpcap_version[]="3.1"; #endif -/* - * By default, print the specified data out in hex. - */ -void -default_print(register const u_char *bp, register u_int length) -{ - default_print_unaligned(bp, length); -} - -#ifdef SIGINFO +#ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int signo _U_) { if (infodelay) @@ -995,37 +2303,98 @@ RETSIGTYPE requestinfo(int signo _U_) } #endif +/* + * Called once each second in verbose mode while dumping to file + */ +#ifdef USE_WIN32_MM_TIMER +void CALLBACK verbose_stats_dump (UINT timer_id _U_, UINT msg _U_, DWORD_PTR arg _U_, + DWORD_PTR dw1 _U_, DWORD_PTR dw2 _U_) +{ + if (infodelay == 0) + fprintf(stderr, "Got %u\r", packets_captured); +} +#elif defined(HAVE_ALARM) +static void verbose_stats_dump(int sig _U_) +{ + if (infodelay == 0) + fprintf(stderr, "Got %u\r", packets_captured); + alarm(1); +} +#endif + +USES_APPLE_DEPRECATED_API static void -usage(void) +print_version(void) { extern char version[]; #ifndef HAVE_PCAP_LIB_VERSION -#if defined(WIN32) || defined(HAVE_PCAP_VERSION) +#if defined(_WIN32) || defined(HAVE_PCAP_VERSION) extern char pcap_version[]; -#else /* defined(WIN32) || defined(HAVE_PCAP_VERSION) */ +#else /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */ static char pcap_version[] = "unknown"; -#endif /* defined(WIN32) || defined(HAVE_PCAP_VERSION) */ +#endif /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */ #endif /* HAVE_PCAP_LIB_VERSION */ #ifdef HAVE_PCAP_LIB_VERSION +#ifdef _WIN32 + (void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version); +#else /* _WIN32 */ (void)fprintf(stderr, "%s version %s\n", program_name, version); - (void)fprintf(stderr, "%s\n", pcap_lib_version()); +#endif /* _WIN32 */ + (void)fprintf(stderr, "%s\n",pcap_lib_version()); #else /* HAVE_PCAP_LIB_VERSION */ -#ifdef WIN32 +#ifdef _WIN32 (void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version); (void)fprintf(stderr, "WinPcap version %s, based on libpcap version %s\n",Wpcap_version, pcap_version); -#else /* WIN32 */ +#else /* _WIN32 */ (void)fprintf(stderr, "%s version %s\n", program_name, version); (void)fprintf(stderr, "libpcap version %s\n", pcap_version); -#endif /* WIN32 */ +#endif /* _WIN32 */ #endif /* HAVE_PCAP_LIB_VERSION */ + +#if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION) + (void)fprintf (stderr, "%s\n", SSLeay_version(SSLEAY_VERSION)); +#endif + +#ifdef USE_LIBSMI + (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string); +#endif +} +USES_APPLE_RST + +static void +print_usage(void) +{ + print_version(); (void)fprintf(stderr, -"Usage: %s [-aAd" D_FLAG "eflLnNOpqRStu" U_FLAG "vxX]" B_FLAG_USAGE " [-c count] [ -C file_size ]\n", program_name); +"Usage: %s [-aAbd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ]\n", program_name); (void)fprintf(stderr, -"\t\t[ -E algo:secret ] [ -F file ] [ -i interface ] [ -r file ]\n"); +"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n"); (void)fprintf(stderr, -"\t\t[ -s snaplen ] [ -T type ] [ -w file ] [ -y datalinktype ]\n"); +"\t\t[ -i interface ]" j_FLAG_USAGE " [ -M secret ] [ --number ]\n"); +#ifdef HAVE_PCAP_SETDIRECTION (void)fprintf(stderr, -"\t\t[ expression ]\n"); - exit(1); +"\t\t[ -Q in|out|inout ]\n"); +#endif + (void)fprintf(stderr, +"\t\t[ -r file ] [ -s snaplen ] "); +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + (void)fprintf(stderr, "[ --time-stamp-precision precision ]\n"); + (void)fprintf(stderr, +"\t\t"); +#endif +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + (void)fprintf(stderr, "[ --immediate-mode ] "); +#endif + (void)fprintf(stderr, "[ -T type ] [ --version ] [ -V file ]\n"); + (void)fprintf(stderr, +"\t\t[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]\n"); + (void)fprintf(stderr, +"\t\t[ -Z user ] [ expression ]\n"); } +/* + * Local Variables: + * c-style: whitesmith + * c-basic-offset: 8 + * End: + */