X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/244754b3f0d3e733dc5b7dba8dcba33a3e107afe..HEAD:/tcpdump.c diff --git a/tcpdump.c b/tcpdump.c index 0bde6468..e5975671 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -33,20 +33,9 @@ * combined efforts of Van, Steve McCanne and Craig Leres of LBL. */ -#ifdef HAVE_CONFIG_H #include -#endif - -/* - * Some older versions of 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 +#ifndef TCPDUMP_CONFIG_H_ +#error "The included config.h header is not from the tcpdump build." #endif #include "netdissect-stdinc.h" @@ -63,9 +52,7 @@ The Regents of the University of California. All rights reserved.\n"; #include -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_LIBCRYPTO #include @@ -78,7 +65,9 @@ The Regents of the University of California. All rights reserved.\n"; #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. + * in the opposite order works fine. For the most part anyway, because in + * FreeBSD declares bpf_dump() instead of . Thus + * interface.h takes care of it later to avoid a compiler warning. */ #ifdef HAVE_CAPSICUM #include @@ -151,16 +140,15 @@ The Regents of the University of California. All rights reserved.\n"; #include #endif /* __FreeBSD__ */ -#include "netdissect-stdinc.h" #include "netdissect.h" #include "interface.h" #include "addrtoname.h" -#include "machdep.h" -#include "pcap-missing.h" #include "ascii_strcasecmp.h" #include "print.h" +#include "diag-control.h" + #include "fptype.h" #ifndef PATH_MAX @@ -173,22 +161,14 @@ The Regents of the University of California. All rights reserved.\n"; #define SIGNAL_REQ_INFO SIGUSR1 #endif -#if defined(HAVE_PCAP_DUMP_FLUSH) && defined(SIGUSR2) +#if defined(SIGUSR2) #define SIGNAL_FLUSH_PCAP SIGUSR2 #endif -#if defined(HAVE_PCAP_CREATE) || defined(_WIN32) static int Bflag; /* buffer size */ -#endif -#ifdef HAVE_PCAP_DUMP_FTELL64 static int64_t Cflag; /* rotate dump files after this many bytes */ -#else -static long Cflag; /* rotate dump files after this many bytes */ -#endif static int Cflag_count; /* Keep track of which file number we're writing */ -#ifdef HAVE_PCAP_FINDALLDEVS static int Dflag; /* list available devices and exit */ -#endif #ifdef HAVE_PCAP_FINDALLDEVS_EX static char *remote_interfaces_source; /* list available devices from this source and exit */ #endif @@ -218,34 +198,37 @@ static int jflag = -1; /* packet time stamp source */ #endif static int lflag; /* line-buffered output */ static int pflag; /* don't go promiscuous */ -#ifdef HAVE_PCAP_SETDIRECTION static int Qflag = -1; /* restrict captured packet by send/receive direction */ -#endif -#ifdef HAVE_PCAP_DUMP_FLUSH static int Uflag; /* "unbuffered" output of dump files */ -#endif static int Wflag; /* recycle output files after this number of files */ static int WflagChars; +#if defined(HAVE_FORK) || defined(HAVE_VFORK) static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */ +#endif static int timeout = 1000; /* default timeout = 1000 ms = 1 s */ #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE static int immediate_mode; #endif static int count_mode; +static u_int packets_to_skip; static int infodelay; static int infoprint; char *program_name; -#ifdef HAVE_CASPER -cap_channel_t *capdns; -#endif - /* Forwards */ +static int parse_int(const char *argname, const char *string, char **endp, + int minval, int maxval, int base); +static u_int parse_u_int(const char *argname, const char *string, char **endp, + u_int minval, u_int maxval, int base); +static int64_t parse_int64(const char *argname, const char *string, + char **endp, int64_t minval, int64_t maxval, int base); static void (*setsignal (int sig, void (*func)(int)))(int); static void cleanup(int); +#if defined(HAVE_FORK) || defined(HAVE_VFORK) static void child_cleanup(int); +#endif static void print_version(FILE *); static void print_usage(FILE *); @@ -271,7 +254,6 @@ static void flushpcap(int); static void info(int); static u_int packets_captured; -#ifdef HAVE_PCAP_FINDALLDEVS static const struct tok status_flags[] = { #ifdef PCAP_IF_UP { PCAP_IF_UP, "Up" }, @@ -285,7 +267,6 @@ static const struct tok status_flags[] = { #endif { 0, NULL } }; -#endif static pcap_t *pd; static pcap_dumper_t *pdd = NULL; @@ -341,7 +322,7 @@ pcap_set_parser_debug(int value) } #define HAVE_PCAP_SET_PARSER_DEBUG -#endif +#endif // HAVE_PCAP_SET_PARSER_DEBUG, HAVE_PCAP_DEBUG, HAVE_YYDEBUG #if defined(HAVE_PCAP_SET_OPTIMIZER_DEBUG) /* @@ -356,7 +337,7 @@ __declspec(dllimport) extern #endif /* _WIN32 */ void pcap_set_optimizer_debug(int); -#endif +#endif // HAVE_PCAP_SET_OPTIMIZER_DEBUG static void NORETURN exit_tcpdump(const int status) @@ -419,21 +400,21 @@ show_tstamp_types_and_exit(pcap_t *pc, const char *device) device); exit_tcpdump(S_SUCCESS); } - fprintf(stderr, "Time stamp types for %s (use option -j to set):\n", + fprintf(stdout, "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, + (void) fprintf(stdout, " %s (%s)\n", tstamp_type_name, pcap_tstamp_type_val_to_description(tstamp_types[i])); } else { - (void) fprintf(stderr, " %d\n", tstamp_types[i]); + (void) fprintf(stdout, " %d\n", tstamp_types[i]); } } pcap_free_tstamp_types(tstamp_types); exit_tcpdump(S_SUCCESS); } -#endif +#endif // HAVE_PCAP_SET_TSTAMP_TYPE static void NORETURN show_dlts_and_exit(pcap_t *pc, const char *device) @@ -456,38 +437,37 @@ show_dlts_and_exit(pcap_t *pc, const char *device) * monitor mode might be different from the ones available when * not in monitor mode). */ + (void) fprintf(stdout, "Data link types for "); if (supports_monitor_mode) - (void) fprintf(stderr, "Data link types for %s %s (use option -y to set):\n", + (void) fprintf(stdout, "%s %s", 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", + (void) fprintf(stdout, "%s", device); + (void) fprintf(stdout, " (use option -y to set):\n"); for (i = 0; i < n_dlts; i++) { dlt_name = pcap_datalink_val_to_name(dlts[i]); if (dlt_name != NULL) { - (void) fprintf(stderr, " %s (%s)", dlt_name, + (void) fprintf(stdout, " %s (%s)", dlt_name, pcap_datalink_val_to_description(dlts[i])); /* * OK, does tcpdump handle that type? */ if (!has_printer(dlts[i])) - (void) fprintf(stderr, " (printing not supported)"); - fprintf(stderr, "\n"); + (void) fprintf(stdout, " (printing not supported)"); + fprintf(stdout, "\n"); } else { - (void) fprintf(stderr, " DLT %d (printing not supported)\n", + (void) fprintf(stdout, " DLT %d (printing not supported)\n", dlts[i]); } } -#ifdef HAVE_PCAP_FREE_DATALINKS pcap_free_datalinks(dlts); -#endif exit_tcpdump(S_SUCCESS); } -#ifdef HAVE_PCAP_FINDALLDEVS static void NORETURN show_devices_and_exit(void) { @@ -542,7 +522,7 @@ show_devices_and_exit(void) break; } } -#endif +#endif // PCAP_IF_WIRELESS printf("]"); } printf("\n"); @@ -550,7 +530,6 @@ show_devices_and_exit(void) pcap_freealldevs(devlist); exit_tcpdump(S_SUCCESS); } -#endif /* HAVE_PCAP_FINDALLDEVS */ #ifdef HAVE_PCAP_FINDALLDEVS_EX static void NORETURN @@ -561,8 +540,21 @@ show_remote_devices_and_exit(void) int i; if (pcap_findalldevs_ex(remote_interfaces_source, NULL, &devlist, - ebuf) < 0) + ebuf) < 0) { + if (strcmp(ebuf, "not supported") == 0) { + /* + * macOS 14's pcap_findalldevs_ex(), which is a + * stub that always returns -1 with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + error("Remote capture not supported"); + } 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) @@ -574,7 +566,7 @@ show_remote_devices_and_exit(void) pcap_freealldevs(devlist); exit_tcpdump(S_SUCCESS); } -#endif /* HAVE_PCAP_FINDALLDEVS */ +#endif /* HAVE_PCAP_FINDALLDEVS_EX */ /* * Short options. @@ -610,55 +602,41 @@ show_remote_devices_and_exit(void) * Set up flags that might or might not be supported depending on the * version of libpcap we're using. */ -#if defined(HAVE_PCAP_CREATE) || defined(_WIN32) -#define B_FLAG "B:" -#define B_FLAG_USAGE " [ -B size ]" -#else /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ -#define B_FLAG -#define B_FLAG_USAGE -#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ - -#ifdef HAVE_PCAP_FINDALLDEVS -#define D_FLAG "D" -#else -#define D_FLAG -#endif - -#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 */ +#else /* HAVE_PCAP_SET_TSTAMP_TYPE */ #define j_FLAG #define j_FLAG_USAGE #define J_FLAG -#endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ +#endif /* HAVE_PCAP_SET_TSTAMP_TYPE */ #ifdef USE_LIBSMI #define m_FLAG_USAGE "[ -m module ] ..." #endif -#ifdef HAVE_PCAP_SETDIRECTION -#define Q_FLAG "Q:" -#define Q_FLAG_USAGE " [ -Q in|out|inout ]" +#if defined(HAVE_FORK) || defined(HAVE_VFORK) +#define z_FLAG "z:" +#define z_FLAG_USAGE "[ -z postrotate-command ] " #else -#define Q_FLAG -#define Q_FLAG_USAGE +#define z_FLAG +#define z_FLAG_USAGE #endif -#ifdef HAVE_PCAP_DUMP_FLUSH -#define U_FLAG "U" +#ifdef HAVE_LIBCRYPTO +#define E_FLAG "E:" +#define E_FLAG_USAGE "[ -E algo:secret ] " +#define M_FLAG "M:" +#define M_FLAG_USAGE "[ -M secret ] " #else -#define U_FLAG +#define E_FLAG +#define E_FLAG_USAGE +#define M_FLAG +#define M_FLAG_USAGE #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:#" +#define SHORTOPTS "aAbB:c:C:dDe" E_FLAG "fF:G:hHi:I" j_FLAG J_FLAG "KlLm:" M_FLAG "nNOpqQ:r:s:StT:uUvV:w:W:xXy:Y" z_FLAG "Z:#" /* * Long options. @@ -689,20 +667,20 @@ show_remote_devices_and_exit(void) #define OPTION_TSTAMP_NANO 134 #define OPTION_FP_TYPE 135 #define OPTION_COUNT 136 +#define OPTION_PRINT_SAMPLING 137 +#define OPTION_LENGTHS 138 +#define OPTION_TIME_T_SIZE 139 +#define OPTION_SKIP 140 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' }, #ifdef HAVE_PCAP_FINDALLDEVS_EX { "list-remote-interfaces", required_argument, NULL, OPTION_LIST_REMOTE_INTERFACES }, #endif { "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' }, @@ -716,14 +694,10 @@ static const struct option longopts[] = { { "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 }, @@ -736,12 +710,16 @@ static const struct option longopts[] = { { "fp-type", no_argument, NULL, OPTION_FP_TYPE }, { "number", no_argument, NULL, '#' }, { "print", no_argument, NULL, OPTION_PRINT }, + { "print-sampling", required_argument, NULL, OPTION_PRINT_SAMPLING }, + { "lengths", no_argument, NULL, OPTION_LENGTHS }, + { "time-t-size", no_argument, NULL, OPTION_TIME_T_SIZE }, + { "skip", required_argument, NULL, OPTION_SKIP }, { "version", no_argument, NULL, OPTION_VERSION }, { NULL, 0, NULL, 0 } }; #ifdef HAVE_PCAP_FINDALLDEVS_EX -#define LIST_REMOTE_INTERFACES_USAGE "[ --list-remote-interfaces remote-source ]" +#define LIST_REMOTE_INTERFACES_USAGE " [ --list-remote-interfaces remote-source ]" #else #define LIST_REMOTE_INTERFACES_USAGE #endif @@ -773,7 +751,7 @@ droproot(const char *username, const char *chroot_dir) { int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG); if (ret < 0) - error("capng_change_id(): return %d\n", ret); + error("capng_change_id(): return %d", ret); else fprintf(stderr, "dropped privs to %s\n", username); } @@ -792,8 +770,8 @@ droproot(const char *username, const char *chroot_dir) } else error("Couldn't find user '%.32s'", username); #ifdef HAVE_LIBCAP_NG - /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT any more. */ -DIAG_OFF_CLANG(assign-enum) + /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT anymore. */ +DIAG_OFF_ASSIGN_ENUM capng_updatev( CAPNG_DROP, CAPNG_EFFECTIVE | CAPNG_PERMITTED, @@ -801,7 +779,7 @@ DIAG_OFF_CLANG(assign-enum) CAP_SETGID, CAP_SYS_CHROOT, -1); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ @@ -829,6 +807,8 @@ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) char *filename = malloc(PATH_MAX + 1); if (filename == NULL) error("%s: malloc", __func__); + if (strlen(orig_name) == 0) + error("an empty string is not a valid file name"); /* Process with strftime if Gflag is set. */ if (Gflag != 0) { @@ -840,9 +820,25 @@ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) } /* There's no good way to detect an error in strftime since a return - * value of 0 isn't necessarily failure. + * value of 0 isn't necessarily failure; if orig_name is an empty + * string, the formatted string will be empty. + * + * However, the C90 standard says that, if there *is* a + * buffer overflow, the content of the buffer is undefined, + * so we must check for a buffer overflow. + * + * So we check above for an empty orig_name, and only call + * strftime() if it's non-empty, in which case the return + * value will only be 0 if the formatted date doesn't fit + * in the buffer. + * + * (We check above because, even if we don't use -G, we + * want a better error message than "tcpdump: : No such + * file or directory" for this case.) */ - strftime(filename, PATH_MAX, orig_name, local_tm); + if (strftime(filename, PATH_MAX, orig_name, local_tm) == 0) { + error("%s: strftime", __func__); + } } else { strncpy(filename, orig_name, PATH_MAX); } @@ -894,6 +890,7 @@ capdns_setup(void) if (cap_dns_type_limit(capdnsloc, types, 1) < 0) error("unable to limit access to system.dns service"); families[0] = AF_INET; + /* Casper is a feature of FreeBSD, which defines AF_INET6. */ families[1] = AF_INET6; if (cap_dns_family_limit(capdnsloc, families, 2) < 0) error("unable to limit access to system.dns service"); @@ -930,7 +927,7 @@ tstamp_precision_to_string(int precision) return "unknown"; } } -#endif +#endif // HAVE_PCAP_SET_TSTAMP_PRECISION #ifdef HAVE_CAPSICUM /* @@ -945,7 +942,7 @@ tstamp_precision_to_string(int precision) * 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. + * regular files notwithstanding. * * The Capsicum people decided that fine-grained control of descriptor * operations was required, so that you need to grant permission for @@ -961,7 +958,7 @@ tstamp_precision_to_string(int precision) * 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 + * 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 @@ -1000,7 +997,7 @@ set_dumper_capsicum_rights(pcap_dumper_t *p) error("unable to limit dump descriptor fcntls"); } } -#endif +#endif // HAVE_CAPSICUM /* * Copy arg vector into a new buffer, concatenating arguments with spaces. @@ -1090,7 +1087,6 @@ read_infile(char *fname) return (cp); } -#ifdef HAVE_PCAP_FINDALLDEVS static long parse_interface_number(const char *device) { @@ -1132,7 +1128,7 @@ parse_interface_number(const char *device) /* * No, it's not an ordinal. */ - error("Invalid adapter index"); + error("Invalid adapter index %s", device); } return (devnum); } else { @@ -1197,7 +1193,7 @@ _U_ status = pcap_findalldevs_ex(host_url, NULL, &devlist, ebuf); free(host_url); } else -#endif +#endif // HAVE_PCAP_FINDALLDEVS_EX status = pcap_findalldevs(&devlist, ebuf); if (status < 0) error("%s", ebuf); @@ -1207,13 +1203,15 @@ _U_ for (i = 0, dev = devlist; i < devnum-1 && dev != NULL; i++, dev = dev->next) ; - if (dev == NULL) - error("Invalid adapter index"); + if (dev == NULL) { + pcap_freealldevs(devlist); + error("Invalid adapter index %ld: only %ld interfaces found", + devnum, i); + } device = strdup(dev->name); pcap_freealldevs(devlist); return (device); } -#endif #ifdef HAVE_PCAP_OPEN /* @@ -1227,10 +1225,8 @@ static pcap_t * open_interface(const char *device, netdissect_options *ndo, char *ebuf) { pcap_t *pc; -#ifdef HAVE_PCAP_CREATE int status; char *cp; -#endif #ifdef HAVE_PCAP_OPEN /* @@ -1246,6 +1242,18 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) pflag ? 0 : PCAP_OPENFLAG_PROMISCUOUS, timeout, NULL, ebuf); if (pc == NULL) { + /* + * macOS 14's pcap_pcap_open(), which is a + * stub that always returns NULL with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + if (strcmp(ebuf, "not supported") == 0) + error("Remote capture not supported"); + /* * If this failed with "No such device" or "The system * cannot find the device specified", that means @@ -1264,7 +1272,6 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) } #endif /* HAVE_PCAP_OPEN */ -#ifdef HAVE_PCAP_CREATE pc = pcap_create(device, ebuf); if (pc == NULL) { /* @@ -1364,6 +1371,11 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0') error("%s: %s\n(%s)", device, pcap_statustostr(status), cp); +#ifdef PCAP_ERROR_CAPTURE_NOTSUP + else if (status == PCAP_ERROR_CAPTURE_NOTSUP && *cp != '\0') + error("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); +#endif #ifdef __FreeBSD__ else if (status == PCAP_ERROR_RFMON_NOTSUP && strncmp(device, "wlan", 4) == 0) { @@ -1381,13 +1393,13 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) * specific case would be an error message that looks a bit odd. */ newdev[strlen(newdev)-1]++; - error("%s is not a monitor mode VAP\n" + error("%s is not a monitor mode VAP" "To create a new monitor mode VAP use:\n" " ifconfig %s create wlandev %s wlanmode monitor\n" "and use %s as the tcpdump interface", device, newdev, parent, newdev); } -#endif +#endif // __FreeBSD__ else error("%s: %s", device, pcap_statustostr(status)); @@ -1409,37 +1421,12 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) warning("%s: %s", device, pcap_statustostr(status)); } -#ifdef HAVE_PCAP_SETDIRECTION if (Qflag != -1) { status = pcap_setdirection(pc, Qflag); if (status != 0) error("%s: pcap_setdirection() failed: %s", device, pcap_geterr(pc)); - } -#endif /* HAVE_PCAP_SETDIRECTION */ -#else /* HAVE_PCAP_CREATE */ - *ebuf = '\0'; - /* - * If no snapshot length was specified, or a length of 0 was - * specified, default to 256KB. - */ - if (ndo->ndo_snaplen == 0) - ndo->ndo_snaplen = MAXIMUM_SNAPLEN; - pc = pcap_open_live(device, ndo->ndo_snaplen, !pflag, timeout, ebuf); - if (pc == NULL) { - /* - * If this failed with "No such device", that means - * the interface doesn't exist; return NULL, so that - * the caller can see whether the device name is - * actually an interface index. - */ - if (strstr(ebuf, "No such device") != NULL) - return (NULL); - error("%s", ebuf); } - if (*ebuf) - warning("%s", ebuf); -#endif /* HAVE_PCAP_CREATE */ return (pc); } @@ -1467,11 +1454,8 @@ main(int argc, char **argv) const char *chroot_dir = NULL; #endif char *ret = NULL; - char *end; -#ifdef HAVE_PCAP_FINDALLDEVS pcap_if_t *devlist; long devnum; -#endif int status; FILE *VFile; #ifdef HAVE_CAPSICUM @@ -1482,10 +1466,162 @@ main(int argc, char **argv) int yflag_dlt = -1; const char *yflag_dlt_name = NULL; int print = 0; + long Cflagmult; netdissect_options Ndo; netdissect_options *ndo = &Ndo; +#ifdef _WIN32 + /* + * We need to look for wpcap.dll in \Windows\System32\Npcap first, + * as either: + * + * 1) WinPcap isn't installed and Npcap isn't installed in "WinPcap + * API-compatible Mode", so there's no wpcap.dll in + * \Windows\System32, only in \Windows\System32\Npcap; + * + * 2) WinPcap is installed and Npcap isn't installed in "WinPcap + * API-compatible Mode", so the wpcap.dll in \Windows\System32 + * is a WinPcap DLL, but we'd prefer an Npcap DLL (we should + * work with either one if we're configured against WinPcap, + * and we'll probably require Npcap if we're configured against + * it), and that's in \Windows\System32\Npcap; + * + * 3) Npcap is installed in "WinPcap API-compatible Mode", so both + * \Windows\System32 and \Windows\System32\Npcap have an Npcap + * wpcap.dll. + * + * Unfortunately, Windows has no notion of an rpath, so we can't + * set the rpath to include \Windows\System32\Npcap at link time; + * what we need to do is to link wpcap as a delay-load DLL and + * add \Windows\System32\Npcap to the DLL search path early in + * main() with a call to SetDllDirectory(). + * + * The same applies to packet.dll. + * + * We add \Windows\System32\Npcap here. + * + * See https://npcap.com/guide/npcap-devguide.html#npcap-feature-native-dll-implicitly + */ + WCHAR *dll_directory = NULL; + size_t dll_directory_buf_len = 0; /* units of bytes */ + UINT system_directory_buf_len = 0; /* units of WCHARs */ + UINT system_directory_len; /* units of WCHARs */ + static const WCHAR npcap[] = L"\\Npcap"; + + /* + * Get the system directory path, in UTF-16, into a buffer that's + * large enough for that directory path plus "\Npcap". + * + * String manipulation in C, plus fetching a variable-length + * string into a buffer whose size is fixed at the time of + * the call, with an oddball return value (see below), is just + * a huge bag of fun. + * + * And it's even more fun when dealing with UTF-16, so that the + * buffer sizes used in GetSystemDirectoryW() are in different + * units from the buffer sizes used in realloc()! We maintain + * all sizes/length in units of bytes, not WCHARs, so that our + * heads don't explode. + */ + for (;;) { + /* + * Try to fetch the system directory. + * + * GetSystemDirectoryW() expects a buffer size in units + * of WCHARs, not bytes, and returns a directory path + * length in units of WCHARs, not bytes. + * + * For extra fun, if GetSystemDirectoryW() succeeds, + * the return value is the length of the directory + * path in units of WCHARs, *not* including the + * terminating '\0', but if it fails because the + * path string wouldn't fit, the return value is + * the length of the directory path in units of WCHARs, + * *including* the terminating '\0'. + */ + system_directory_len = GetSystemDirectoryW(dll_directory, + system_directory_buf_len); + if (system_directory_len == 0) + error("GetSystemDirectoryW() failed"); + + /* + * Did the directory path fit in the buffer? + * + * As per the above, this means that the return value + * *plus 1*, so that the terminating '\0' is counted, + * is <= the buffer size. + * + * (If the directory path, complete with the terminating + * '\0', fits *exactly*, the return value would be the + * size of the buffer minus 1, as it doesn't count the + * terminating '\0', so the test below would succeed. + * + * If everything *but* the terminating '\0' fits, + * the return value would be the size of the buffer + 1, + * i.e., the size that the string in question would + * have required. + * + * The astute reader will note that returning the + * size of the buffer is not one of the two cases + * above, and should never happen.) + */ + if ((system_directory_len + 1) <= system_directory_buf_len) { + /* + * No. We have a buffer that's large enough + * for our purposes. + */ + break; + } + + /* + * Yes. Grow the buffer. + * + * The space we'll need in the buffer for the system + * directory, in units of WCHARs, is system_directory_len, + * as that's the length of the system directory path + * including the terminating '\0'. + */ + system_directory_buf_len = system_directory_len; + + /* + * The size of the DLL directory buffer, in *bytes*, must + * be the number of WCHARs taken by the system directory, + * *minus* the terminating '\0' (as we'll overwrite that + * with the "\" of the "\Npcap" string), multiplied by + * sizeof(WCHAR) to convert it to the number of bytes, + * plus the size of the "\Npcap" string, in bytes (which + * will include the terminating '\0', as that will become + * the DLL path's terminating '\0'). + */ + dll_directory_buf_len = + ((system_directory_len - 1)*sizeof(WCHAR)) + sizeof npcap; + dll_directory = realloc(dll_directory, dll_directory_buf_len); + if (dll_directory == NULL) + error("Can't allocate string for Npcap directory"); + } + + /* + * OK, that worked. + * + * Now append \Npcap. We add the length of the system directory path, + * in WCHARs, *not* including the terminating '\0' (which, since + * GetSystemDirectoryW() succeeded, is the return value of + * GetSystemDirectoryW(), as per the above), to the pointer to the + * beginning of the path, to go past the end of the system directory + * to point to the terminating '\0'. + */ + memcpy(dll_directory + system_directory_len, npcap, sizeof npcap); + + /* + * Now add that as a system DLL directory. + */ + if (!SetDllDirectoryW(dll_directory)) + error("SetDllDirectory failed"); + + free(dll_directory); +#endif // _WIN32 + /* * Initialize the netdissect code. */ @@ -1517,14 +1653,11 @@ main(int argc, char **argv) #endif /* - * 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. + * An explicit tzset() call is usually not needed as it happens + * implicitly the first time we call localtime() or mktime(), + * but in some cases (sandboxing, chroot) this may be too late. */ - if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0) - error("%s", ebuf); + tzset(); while ( (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) @@ -1542,51 +1675,103 @@ main(int argc, char **argv) ++ndo->ndo_bflag; break; -#if defined(HAVE_PCAP_CREATE) || defined(_WIN32) case 'B': - Bflag = atoi(optarg)*1024; - if (Bflag <= 0) - error("invalid packet buffer size %s", optarg); + Bflag = parse_int("packet buffer size", optarg, NULL, 1, + INT_MAX, 10); + /* + * Will multiplying it by 1024 overflow? + */ + if (Bflag > INT_MAX / 1024) + error("packet buffer size %s is too large", optarg); + Bflag *= 1024; break; -#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ case 'c': - cnt = atoi(optarg); - if (cnt <= 0) - error("invalid packet count %s", optarg); + cnt = parse_int("packet count", optarg, NULL, 1, + INT_MAX, 10); break; case 'C': - errno = 0; -#ifdef HAVE_PCAP_DUMP_FTELL64 - Cflag = strtoint64_t(optarg, &endp, 10); -#else - Cflag = strtol(optarg, &endp, 10); -#endif - if (endp == optarg || *endp != '\0' || errno != 0 - || Cflag <= 0) - error("invalid file size %s", optarg); + Cflag = parse_int64("file size", optarg, &endp, 1, + INT64_MAX, 10); + + if (*endp == '\0') { + /* + * There's nothing after the file size, + * so the size is in units of 1 MB + * (1,000,000 bytes). + */ + Cflagmult = 1000000; + } else { + /* + * There's something after the file + * size. + * + * If it's a single letter, then: + * + * if the letter is k or K, the size + * is in units of 1 KiB (1024 bytes); + * + * if the letter is m or M, the size + * is in units of 1 MiB (1,048,576 bytes); + * + * if the letter is g or G, the size + * is in units of 1 GiB (1,073,741,824 bytes). + * + * Otherwise, it's an error. + */ + switch (*endp) { + + case 'k': + case 'K': + Cflagmult = 1024; + break; + + case 'm': + case 'M': + Cflagmult = 1024*1024; + break; + + case 'g': + case 'G': + Cflagmult = 1024*1024*1024; + break; + + default: + error("invalid file size %s (invalid units)", optarg); + } + + /* + * OK, there was a letter that we treat + * as a units indication; was there + * anything after it? + */ + endp++; + if (*endp != '\0') { + /* Yes - error */ + error("invalid file size %s (invalid units)", optarg); + } + } + /* - * Will multiplying it by 1000000 overflow? + * Will multiplying it by multiplier overflow? */ #ifdef HAVE_PCAP_DUMP_FTELL64 - if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / 1000000) + if (Cflag > INT64_MAX / Cflagmult) #else - if (Cflag > LONG_MAX / 1000000) + if (Cflag > LONG_MAX / Cflagmult) #endif error("file size %s is too large", optarg); - Cflag *= 1000000; + Cflag *= Cflagmult; break; case 'd': ++dflag; break; -#ifdef HAVE_PCAP_FINDALLDEVS case 'D': Dflag++; break; -#endif #ifdef HAVE_PCAP_FINDALLDEVS_EX case OPTION_LIST_REMOTE_INTERFACES: @@ -1602,12 +1787,11 @@ main(int argc, char **argv) ++ndo->ndo_eflag; break; +#ifdef HAVE_LIBCRYPTO case 'E': -#ifndef HAVE_LIBCRYPTO - warning("crypto code not compiled in"); -#endif ndo->ndo_espsecret = optarg; break; +#endif case 'f': ++ndo->ndo_fflag; @@ -1618,9 +1802,8 @@ main(int argc, char **argv) break; case 'G': - Gflag = atoi(optarg); - if (Gflag < 0) - error("invalid number of seconds %s", optarg); + Gflag = parse_int("number of seconds", optarg, NULL, 0, + INT_MAX, 10); /* We will create one file initially. */ Gflag_count = 0; @@ -1635,7 +1818,7 @@ main(int argc, char **argv) case 'h': print_usage(stdout); exit_tcpdump(S_SUCCESS); - break; + /* NOTREACHED */ case 'H': ++ndo->ndo_Hflag; @@ -1645,11 +1828,9 @@ main(int argc, char **argv) device = optarg; break; -#ifdef HAVE_PCAP_CREATE case 'I': ++Iflag; break; -#endif /* HAVE_PCAP_CREATE */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE case 'j': @@ -1676,11 +1857,7 @@ main(int argc, char **argv) */ setvbuf(stdout, NULL, _IONBF, 0); #else /* _WIN32 */ -#ifdef HAVE_SETLINEBUF - setlinebuf(stdout); -#else setvbuf(stdout, NULL, _IOLBF, 0); -#endif #endif /* _WIN32 */ lflag = 1; break; @@ -1694,19 +1871,18 @@ main(int argc, char **argv) if (nd_load_smi_module(optarg, ebuf, sizeof(ebuf)) == -1) error("%s", ebuf); } else { - (void)fprintf(stderr, "%s: ignoring option `-m %s' ", + (void)fprintf(stderr, "%s: ignoring option '-m %s' ", program_name, optarg); (void)fprintf(stderr, "(no libsmi support)\n"); } break; +#ifdef HAVE_LIBCRYPTO case 'M': /* TCP-MD5 shared secret */ -#ifndef HAVE_LIBCRYPTO - warning("crypto code not compiled in"); -#endif ndo->ndo_sigsecret = optarg; break; +#endif case 'n': ++ndo->ndo_nflag; @@ -1729,7 +1905,6 @@ main(int argc, char **argv) ++ndo->ndo_suppress_default_print; break; -#ifdef HAVE_PCAP_SETDIRECTION case 'Q': if (ascii_strcasecmp(optarg, "in") == 0) Qflag = PCAP_D_IN; @@ -1738,20 +1913,16 @@ main(int argc, char **argv) else if (ascii_strcasecmp(optarg, "inout") == 0) Qflag = PCAP_D_INOUT; else - error("unknown capture direction `%s'", optarg); + error("unknown capture direction '%s'", optarg); break; -#endif /* HAVE_PCAP_SETDIRECTION */ case 'r': RFileName = optarg; break; case 's': - ndo->ndo_snaplen = (int)strtol(optarg, &end, 0); - if (optarg == end || *end != '\0' - || ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN) - error("invalid snaplen %s (must be >= 0 and <= %d)", - optarg, MAXIMUM_SNAPLEN); + ndo->ndo_snaplen = parse_int("snaplen", optarg, NULL, + 0, MAXIMUM_SNAPLEN, 0); break; case 'S': @@ -1803,19 +1974,19 @@ main(int argc, char **argv) ndo->ndo_packettype = PT_SOMEIP; else if (ascii_strcasecmp(optarg, "domain") == 0) ndo->ndo_packettype = PT_DOMAIN; + else if (ascii_strcasecmp(optarg, "quic") == 0) + ndo->ndo_packettype = PT_QUIC; else - error("unknown packet type `%s'", optarg); + error("unknown packet type '%s'", optarg); break; case 'u': ++ndo->ndo_uflag; break; -#ifdef HAVE_PCAP_DUMP_FLUSH case 'U': ++Uflag; break; -#endif case 'v': ++ndo->ndo_vflag; @@ -1830,9 +2001,8 @@ main(int argc, char **argv) break; case 'W': - Wflag = atoi(optarg); - if (Wflag <= 0) - error("invalid number of output files %s", optarg); + Wflag = parse_int("number of output files", optarg, + NULL, 1, INT_MAX, 10); WflagChars = getWflagChars(Wflag); break; @@ -1862,9 +2032,12 @@ main(int argc, char **argv) } break; #endif + +#if defined(HAVE_FORK) || defined(HAVE_VFORK) case 'z': zflag = optarg; break; +#endif case 'Z': username = optarg; @@ -1874,10 +2047,18 @@ main(int argc, char **argv) ndo->ndo_packet_number = 1; break; + case OPTION_LENGTHS: + ndo->ndo_lengths = 1; + break; + + case OPTION_TIME_T_SIZE: + printf("%zu\n", sizeof(time_t) * 8); + return 0; + case OPTION_VERSION: print_version(stdout); exit_tcpdump(S_SUCCESS); - break; + /* NOTREACHED */ #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION case OPTION_TSTAMP_PRECISION: @@ -1897,6 +2078,18 @@ main(int argc, char **argv) print = 1; break; + case OPTION_PRINT_SAMPLING: + print = 1; + ++ndo->ndo_Sflag; + ndo->ndo_print_sampling = parse_int("print sampling", + optarg, NULL, 1, INT_MAX, 10); + break; + + case OPTION_SKIP: + packets_to_skip = parse_u_int("packet skip count", + optarg, NULL, 0, INT_MAX, 0); + break; + #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION case OPTION_TSTAMP_MICRO: ndo->ndo_tstamp_precision = PCAP_TSTAMP_PRECISION_MICRO; @@ -1928,23 +2121,34 @@ main(int argc, char **argv) /* NOTREACHED */ } -#ifdef HAVE_PCAP_FINDALLDEVS + if (ndo->ndo_Aflag && ndo->ndo_xflag) + error("-A and -x[x] are mutually exclusive."); + if (ndo->ndo_Aflag && ndo->ndo_Xflag) + error("-A and -X[X] are mutually exclusive."); + if (ndo->ndo_xflag && ndo->ndo_Xflag) + error("-x[x] and -X[X] are mutually exclusive."); + if (Cflag != 0 && WFileName == NULL) + error("-C cannot be used without -w."); + if (Gflag != 0 && WFileName == NULL) + error("-G cannot be used without -w."); +#if defined(HAVE_FORK) || defined(HAVE_VFORK) + if (zflag != NULL && (WFileName == NULL || (Cflag == 0 && Gflag == 0))) + error("-z cannot be used without -w and (-C or -G)."); +#endif + + if (cnt != -1) + if ((int)packets_to_skip > (INT_MAX - cnt)) + // cnt + (int)packets_to_skip used in pcap_loop() call + error("Overflow (-c count) %d + (--skip count) %d", cnt, + (int)packets_to_skip); + if (Dflag) show_devices_and_exit(); -#endif #ifdef HAVE_PCAP_FINDALLDEVS_EX if (remote_interfaces_source != NULL) show_remote_devices_and_exit(); #endif -#if defined(DLT_LINUX_SLL2) && defined(HAVE_PCAP_SET_DATALINK) -/* Set default linktype DLT_LINUX_SLL2 when capturing on the "any" device */ - if (device != NULL && - strncmp (device, "any", strlen("any")) == 0 - && yflag_dlt == -1) - yflag_dlt = DLT_LINUX_SLL2; -#endif - switch (ndo->ndo_tflag) { case 0: /* Default */ @@ -1957,11 +2161,11 @@ main(int argc, char **argv) default: /* Not supported */ error("only -t, -tt, -ttt, -tttt and -ttttt are supported"); - break; + /* NOTREACHED */ } if (ndo->ndo_fflag != 0 && (VFileName != NULL || RFileName != NULL)) - error("-f can not be used with -V or -r"); + error("-f cannot be used with -V or -r."); if (VFileName != NULL && RFileName != NULL) error("-V and -r are mutually exclusive."); @@ -1994,6 +2198,8 @@ main(int argc, char **argv) /* Run with '-Z root' to restore old behaviour */ if (!username) username = WITH_USER; + else if (strcmp(username, "root") == 0) + username = NULL; } #endif @@ -2027,11 +2233,11 @@ main(int argc, char **argv) VFile = fopen(VFileName, "r"); if (VFile == NULL) - error("Unable to open file: %s\n", pcap_strerror(errno)); + error("Unable to open file: %s", pcap_strerror(errno)); ret = get_next_file(VFile, VFileLine); if (!ret) - error("Nothing in %s\n", VFileName); + error("Nothing in %s", VFileName); RFileName = VFileLine; } @@ -2061,7 +2267,7 @@ main(int argc, char **argv) pcap_datalink_val_to_description(dlt)); } fprintf(stderr, ", snapshot length %d\n", pcap_snapshot(pd)); -#ifdef DLT_LINUX_SLL2 +#if defined(DLT_LINUX_SLL2) && defined(__linux__) if (dlt == DLT_LINUX_SLL2) fprintf(stderr, "Warning: interface names might be incorrect\n"); #endif @@ -2096,7 +2302,6 @@ main(int argc, char **argv) /* * No interface was specified. Pick one. */ -#ifdef HAVE_PCAP_FINDALLDEVS /* * Find the list of interfaces, and pick * the first interface. @@ -2107,15 +2312,6 @@ main(int argc, char **argv) error("no interfaces available for capture"); device = strdup(devlist->name); pcap_freealldevs(devlist); -#else /* HAVE_PCAP_FINDALLDEVS */ - /* - * Use whatever interface pcap_lookupdev() - * chooses. - */ - device = pcap_lookupdev(ebuf); - if (device == NULL) - error("%s", ebuf); -#endif } /* @@ -2130,7 +2326,6 @@ main(int argc, char **argv) * a 1-based index in the list of * interfaces. */ -#ifdef HAVE_PCAP_FINDALLDEVS devnum = parse_interface_number(device); if (devnum == -1) { /* @@ -2152,13 +2347,6 @@ main(int argc, char **argv) pd = open_interface(device, ndo, ebuf); if (pd == NULL) error("%s", ebuf); -#else /* HAVE_PCAP_FINDALLDEVS */ - /* - * We can't get a list of interfaces; just - * fail. - */ - error("%s", ebuf); -#endif /* HAVE_PCAP_FINDALLDEVS */ } /* @@ -2169,34 +2357,34 @@ main(int argc, char **argv) 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 /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */ if (Lflag) show_dlts_and_exit(pd, device); if (yflag_dlt >= 0) { -#ifdef HAVE_PCAP_SET_DATALINK if (pcap_set_datalink(pd, yflag_dlt) < 0) error("%s", pcap_geterr(pd)); -#else - /* - * We don't actually support changing the - * data link type, so we only let them - * set it to what it already is. - */ - if (yflag_dlt != pcap_datalink(pd)) { - error("%s is not one of the DLTs supported by this device\n", - yflag_dlt_name); - } -#endif (void)fprintf(stderr, "%s: data link type %s\n", program_name, pcap_datalink_val_to_name(yflag_dlt)); (void)fflush(stderr); } +#if defined(DLT_LINUX_SLL2) + else { + /* + * Attempt to set default linktype to + * DLT_LINUX_SLL2 when capturing on the + * "any" device. + * + * If the attempt fails, just quietly drive + * on; this may be a non-Linux "any" device + * that doesn't support DLT_LINUX_SLL2. + */ + if (strcmp(device, "any") == 0) { +DIAG_OFF_WARN_UNUSED_RESULT + (void) pcap_set_datalink(pd, DLT_LINUX_SLL2); +DIAG_ON_WARN_UNUSED_RESULT + } + } +#endif i = pcap_snapshot(pd); if (ndo->ndo_snaplen < i) { if (ndo->ndo_snaplen != 0) @@ -2221,7 +2409,11 @@ main(int argc, char **argv) #ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG pcap_set_optimizer_debug(dflag); #endif - if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) + /* + * netmask is in network byte order, pcap_compile() takes it + * in host byte order. + */ + if (pcap_compile(pd, &fcode, cmdbuf, Oflag, ntohl(netmask)) < 0) error("%s", pcap_geterr(pd)); if (dflag) { bpf_dump(&fcode, dflag); @@ -2236,6 +2428,7 @@ main(int argc, char **argv) capdns = capdns_setup(); #endif /* HAVE_CASPER */ + // Both localnet and netmask are in network byte order. init_print(ndo, localnet, netmask); #ifndef _WIN32 @@ -2248,7 +2441,21 @@ main(int argc, char **argv) #endif /* Cooperate with nohup(1) */ #ifndef _WIN32 + /* + * In illumos /usr/include/sys/iso/signal_iso.h causes Clang to + * generate a -Wstrict-prototypes warning here, see [1]. The + * __illumos__ macro is available since at least GCC 11 and Clang 13, + * see [2]. + * 1: https://www.illumos.org/issues/16344 + * 2: https://www.illumos.org/issues/13726 + */ +#ifdef __illumos__ + DIAG_OFF_STRICT_PROTOTYPES +#endif /* __illumos__ */ if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL) +#ifdef __illumos__ + DIAG_ON_STRICT_PROTOTYPES +#endif /* __illumos__ */ (void)setsignal(SIGHUP, oldhandler); #endif /* _WIN32 */ @@ -2261,7 +2468,7 @@ main(int argc, char **argv) * 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; + * 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 @@ -2276,33 +2483,33 @@ main(int argc, char **argv) /* Initialize capng */ capng_clear(CAPNG_SELECT_BOTH); if (username) { -DIAG_OFF_CLANG(assign-enum) +DIAG_OFF_ASSIGN_ENUM capng_updatev( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_SETUID, CAP_SETGID, -1); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM } if (chroot_dir) { -DIAG_OFF_CLANG(assign-enum) +DIAG_OFF_ASSIGN_ENUM capng_update( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_SYS_CHROOT ); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM } if (WFileName) { -DIAG_OFF_CLANG(assign-enum) +DIAG_OFF_ASSIGN_ENUM capng_update( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE ); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM } capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ @@ -2368,17 +2575,44 @@ DIAG_ON_CLANG(assign-enum) #endif if (Cflag != 0 || Gflag != 0) { #ifdef HAVE_CAPSICUM - dumpinfo.WFileName = strdup(basename(WFileName)); + /* + * basename() and dirname() may modify their input buffer + * and they do since FreeBSD 12.0, but they didn't before. + * Hence use the return value only, but always assume the + * input buffer has been modified and would need to be + * reset before the next use. + */ + char *WFileName_copy; + + if ((WFileName_copy = strdup(WFileName)) == NULL) { + error("Unable to allocate memory for file %s", + WFileName); + } + DIAG_OFF_C11_EXTENSIONS + dumpinfo.WFileName = strdup(basename(WFileName_copy)); + DIAG_ON_C11_EXTENSIONS if (dumpinfo.WFileName == NULL) { error("Unable to allocate memory for file %s", WFileName); } - dumpinfo.dirfd = open(dirname(WFileName), + free(WFileName_copy); + + if ((WFileName_copy = strdup(WFileName)) == NULL) { + error("Unable to allocate memory for file %s", + WFileName); + } + DIAG_OFF_C11_EXTENSIONS + char *WFileName_dirname = dirname(WFileName_copy); + DIAG_ON_C11_EXTENSIONS + dumpinfo.dirfd = open(WFileName_dirname, O_DIRECTORY | O_RDONLY); if (dumpinfo.dirfd < 0) { error("unable to open directory %s", - dirname(WFileName)); + WFileName_dirname); } + free(WFileName_dirname); + free(WFileName_copy); + cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 && @@ -2410,10 +2644,8 @@ DIAG_ON_CLANG(assign-enum) } else dumpinfo.ndo = NULL; -#ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush(pdd); -#endif } else { dlt = pcap_datalink(pd); ndo->ndo_if_printer = get_if_printer(dlt); @@ -2502,12 +2734,17 @@ DIAG_ON_CLANG(assign-enum) #else cansandbox = (cansandbox && ndo->ndo_nflag); #endif /* HAVE_CASPER */ + cansandbox = (cansandbox && (pcap_fileno(pd) != -1 || + RFileName != 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); + status = pcap_loop(pd, + (cnt == -1 ? -1 : cnt + (int)packets_to_skip), + callback, pcap_userdata); if (WFileName == NULL) { /* * We're printing packets. Flush the printed output, @@ -2593,7 +2830,13 @@ DIAG_ON_CLANG(assign-enum) */ dlt = new_dlt; ndo->ndo_if_printer = get_if_printer(dlt); - if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) + /* Free the old filter */ + pcap_freecode(&fcode); + /* + * netmask is in network byte order, pcap_compile() takes it + * in host byte order. + */ + if (pcap_compile(pd, &fcode, cmdbuf, Oflag, ntohl(netmask)) < 0) error("%s", pcap_geterr(pd)); } @@ -2627,7 +2870,135 @@ DIAG_ON_CLANG(assign-enum) free(cmdbuf); pcap_freecode(&fcode); - exit_tcpdump(status == -1 ? 1 : 0); + exit_tcpdump(status == -1 ? S_ERR_HOST_PROGRAM : S_SUCCESS); +} + +/* + * Routines to parse numerical command-line arguments and check for + * errors, including "too large for that type". + */ +static int +parse_int(const char *argname, const char *string, char **endpp, + int minval, int maxval, int base) +{ + long val; + char *endp; + + errno = 0; + val = strtol(string, &endp, base); + + /* + * Did it either not parse any of the string, find extra stuff + * at the end that the caller isn't interested in, or get + * another parsing error? + */ + if (string == endp || (endpp == NULL && *endp != '\0') || + (val == 0 && errno == EINVAL)) { + error("invalid %s \"%s\" (not a valid number)", argname, + string); + } + + /* + * Did it get a value that's out of range? + */ + if (((val == LONG_MAX || val == LONG_MIN) && errno == ERANGE) || + val < minval || val > maxval) { + error("invalid %s %s (must be >= %d and <= %d)", + argname, string, minval, maxval); + } + + /* + * OK, it passes all the tests. + */ + if (endpp != NULL) + *endpp = endp; + return ((int)val); +} + +static u_int +parse_u_int(const char *argname, const char *string, char **endpp, + u_int minval, u_int maxval, int base) +{ + unsigned long val; + char *endp; + + errno = 0; + + /* + * strtoul() does *NOT* report an error if the string + * begins with a minus sign. We do. + */ + if (*string == '-') { + error("invalid %s \"%s\" (not a valid unsigned number)", + argname, string); + } + + val = strtoul(string, &endp, base); + + /* + * Did it either not parse any of the string, find extra stuff + * at the end that the caller isn't interested in, or get + * another parsing error? + */ + if (string == endp || (endpp == NULL && *endp != '\0') || + (val == 0 && errno == EINVAL)) { + error("invalid %s \"%s\" (not a valid unsigned number)", + argname, string); + } + + /* + * Did it get a value that's out of range? + */ + if ((val == ULONG_MAX && errno == ERANGE) || + val < minval || val > maxval) { + error("invalid %s %s (must be >= %u and <= %u)", + argname, string, minval, maxval); + } + + /* + * OK, it passes all the tests. + */ + if (endpp != NULL) + *endpp = endp; + return ((u_int)val); +} + +static int64_t +parse_int64(const char *argname, const char *string, char **endpp, + int64_t minval, int64_t maxval, int base) +{ + intmax_t val; + char *endp; + + errno = 0; + val = strtoimax(string, &endp, base); + + /* + * Did it either not parse any of the string, find extra stuff + * at the end that the caller isn't interested in, or get + * another parsing error? + */ + if (string == endp || (endpp == NULL && *endp != '\0') || + (val == 0 && errno == EINVAL)) { + error("invalid %s \"%s\" (not a valid number)", argname, + string); + } + + /* + * Did it get a value that's out of range? + */ + if (((val == INTMAX_MAX || val == INTMAX_MIN) && errno == ERANGE) || + val < minval || val > maxval) { + error("invalid %s %s (must be >= %" PRId64 " and <= %" PRId64 ")", + argname, string, minval, maxval); + } + + /* + * OK, it passes all the tests. + */ + if (endpp != NULL) + *endpp = endp; + return ((int64_t)val); } /* @@ -2643,10 +3014,24 @@ static void memset(&new, 0, sizeof(new)); new.sa_handler = func; - if (sig == SIGCHLD) + if ((sig == SIGCHLD) +# ifdef SIGNAL_REQ_INFO + || (sig == SIGNAL_REQ_INFO) +# endif +# ifdef SIGNAL_FLUSH_PCAP + || (sig == SIGNAL_FLUSH_PCAP) +# endif + ) new.sa_flags = SA_RESTART; if (sigaction(sig, &new, &old) < 0) + /* The same workaround as for SIG_DFL above. */ +#ifdef __illumos__ + DIAG_OFF_STRICT_PROTOTYPES +#endif /* __illumos__ */ return (SIG_ERR); +#ifdef __illumos__ + DIAG_ON_STRICT_PROTOTYPES +#endif /* __illumos__ */ return (old.sa_handler); #endif } @@ -2671,7 +3056,6 @@ cleanup(int signo _U_) setitimer(ITIMER_REAL, &timer, NULL); #endif /* _WIN32 */ -#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 @@ -2679,25 +3063,6 @@ cleanup(int signo _U_) * 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); - info(1); - } - exit_tcpdump(S_SUCCESS); -#endif } /* @@ -2708,7 +3073,7 @@ cleanup(int signo _U_) static void child_cleanup(int signo _U_) { - wait(NULL); + while (waitpid(-1, NULL, WNOHANG) >= 0); } #endif /* HAVE_FORK && HAVE_VFORK */ @@ -2771,8 +3136,8 @@ compress_savefile(const char *filename) child = fork_subprocess(); if (child == -1) { fprintf(stderr, - "compress_savefile: fork failed: %s\n", - pcap_strerror(errno)); + "%s: fork failed: %s\n", + __func__, pcap_strerror(errno)); return; } if (child != 0) { @@ -2791,24 +3156,69 @@ compress_savefile(const char *filename) #endif if (execlp(zflag, zflag, filename, (char *)NULL) == -1) fprintf(stderr, - "compress_savefile: execlp(%s, %s) failed: %s\n", - zflag, - filename, - pcap_strerror(errno)); + "%s: execlp(%s, %s) failed: %s\n", + __func__, zflag, filename, pcap_strerror(errno)); #ifdef HAVE_FORK exit(S_ERR_HOST_PROGRAM); #else _exit(S_ERR_HOST_PROGRAM); #endif } -#else /* HAVE_FORK && HAVE_VFORK */ +#endif /* HAVE_FORK || HAVE_VFORK */ + static void -compress_savefile(const char *filename) +close_old_dump_file(struct dump_info *dump_info) { - fprintf(stderr, - "compress_savefile failed. Functionality not implemented under your system\n"); + /* + * Close the current file and open a new one. + */ + pcap_dump_close(dump_info->pdd); + +#if defined(HAVE_FORK) || defined(HAVE_VFORK) + /* + * Compress the file we just closed, if the user asked for it. + */ + if (zflag != NULL) + compress_savefile(dump_info->CurrentFileName); +#endif +} + +static void +open_new_dump_file(struct dump_info *dump_info) +{ +#ifdef HAVE_CAPSICUM + FILE *fp; + int fd; +#endif + +#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->pdd = pcap_dump_fopen(dump_info->pd, fp); +#else /* !HAVE_CAPSICUM */ + dump_info->pdd = 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->pdd == NULL) + error("%s", pcap_geterr(pd)); +#ifdef HAVE_CAPSICUM + set_dumper_capsicum_rights(dump_info->pdd); +#endif } -#endif /* HAVE_FORK && HAVE_VFORK */ static void dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) @@ -2821,6 +3231,9 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s dump_info = (struct dump_info *)user; + if (packets_captured <= packets_to_skip) + return; + /* * 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 @@ -2842,25 +3255,12 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s /* 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->pdd); - /* - * Compress the file we just closed, if the user asked for it - */ - if (zflag != NULL) - compress_savefile(dump_info->CurrentFileName); + close_old_dump_file(dump_info); /* * Check to see if we've exceeded the Wflag (when @@ -2897,36 +3297,7 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s 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->pdd = pcap_dump_fopen(dump_info->pd, fp); -#else /* !HAVE_CAPSICUM */ - dump_info->pdd = 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->pdd == NULL) - error("%s", pcap_geterr(pd)); -#ifdef HAVE_CAPSICUM - set_dumper_capsicum_rights(dump_info->pdd); -#endif + open_new_dump_file(dump_info); } } @@ -2951,22 +3322,7 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s 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->pdd); - - /* - * Compress the file we just closed, if the user - * asked for it. - */ - if (zflag != NULL) - compress_savefile(dump_info->CurrentFileName); + close_old_dump_file(dump_info); Cflag_count++; if (Wflag > 0) { @@ -2979,43 +3335,14 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s if (dump_info->CurrentFileName == NULL) error("%s: malloc", __func__); 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->pdd = pcap_dump_fopen(dump_info->pd, fp); -#else /* !HAVE_CAPSICUM */ - dump_info->pdd = 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->pdd == NULL) - error("%s", pcap_geterr(pd)); -#ifdef HAVE_CAPSICUM - set_dumper_capsicum_rights(dump_info->pdd); -#endif + + open_new_dump_file(dump_info); } } pcap_dump((u_char *)dump_info->pdd, h, sp); -#ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush(dump_info->pdd); -#endif if (dump_info->ndo != NULL) pretty_print_packet(dump_info->ndo, h, sp, packets_captured); @@ -3036,11 +3363,12 @@ dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) dump_info = (struct dump_info *)user; + if (packets_captured <= packets_to_skip) + return; + pcap_dump((u_char *)dump_info->pdd, h, sp); -#ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush(dump_info->pdd); -#endif if (dump_info->ndo != NULL) pretty_print_packet(dump_info->ndo, h, sp, packets_captured); @@ -3057,7 +3385,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) ++infodelay; - if (!count_mode) + if (!count_mode && packets_captured > packets_to_skip) pretty_print_packet((netdissect_options *)user, h, sp, packets_captured); --infodelay; @@ -3113,25 +3441,14 @@ static void verbose_stats_dump(int sig _U_) } #endif /* _WIN32 */ -USES_APPLE_DEPRECATED_API +DIAG_OFF_DEPRECATION static void print_version(FILE *f) { -#ifndef HAVE_PCAP_LIB_VERSION - #ifdef HAVE_PCAP_VERSION - extern char pcap_version[]; - #else /* HAVE_PCAP_VERSION */ - static char pcap_version[] = "unknown"; - #endif /* HAVE_PCAP_VERSION */ -#endif /* HAVE_PCAP_LIB_VERSION */ const char *smi_version_string; (void)fprintf(f, "%s version " PACKAGE_VERSION "\n", program_name); -#ifdef HAVE_PCAP_LIB_VERSION (void)fprintf(f, "%s\n", pcap_lib_version()); -#else /* HAVE_PCAP_LIB_VERSION */ - (void)fprintf(f, "libpcap version %s\n", pcap_version); -#endif /* HAVE_PCAP_LIB_VERSION */ #if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION) (void)fprintf (f, "%s\n", SSLeay_version(SSLEAY_VERSION)); @@ -3150,31 +3467,33 @@ print_version(FILE *f) (void)fprintf (f, "Compiled with MemorySanitizer/Clang.\n"); # endif #endif /* __SANITIZE_ADDRESS__ or __has_feature */ + (void)fprintf (f, "%zu-bit build, %zu-bit time_t\n", + sizeof(void *) * 8, sizeof(time_t) * 8); } -USES_APPLE_RST +DIAG_ON_DEPRECATION static void print_usage(FILE *f) { print_version(f); (void)fprintf(f, -"Usage: %s [-Abd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ] [--count]\n", program_name); +"Usage: %s [-AbdDefhHI" J_FLAG "KlLnNOpqStuUvxX#] [ -B size ] [ -c count ] [--count]\n", program_name); (void)fprintf(f, -"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n"); +"\t\t[ -C file_size ] " E_FLAG_USAGE "[ -F file ] [ -G seconds ]\n"); (void)fprintf(f, "\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n"); -#ifdef HAVE_PCAP_FINDALLDEVS_EX (void)fprintf(f, -"\t\t" LIST_REMOTE_INTERFACES_USAGE "\n"); -#endif +"\t\t[ --lengths ]" LIST_REMOTE_INTERFACES_USAGE "\n"); #ifdef USE_LIBSMI (void)fprintf(f, "\t\t" m_FLAG_USAGE "\n"); #endif (void)fprintf(f, -"\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n"); +"\t\t" M_FLAG_USAGE "[ --number ] [ --print ]\n"); + (void)fprintf(f, +"\t\t[ --print-sampling nth ] [ -Q in|out|inout ] [ -r file ]\n"); (void)fprintf(f, -"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n"); +"\t\t[ -s snaplen ] [ --skip count ] [ -T type ] [ --version ]\n"); (void)fprintf(f, "\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n"); #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION @@ -3182,5 +3501,5 @@ print_usage(FILE *f) "\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n"); #endif (void)fprintf(f, -"\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n"); +"\t\t" z_FLAG_USAGE "[ -Z user ] [ expression ]\n"); }