]> The Tcpdump Group git mirrors - tcpdump/blobdiff - tcpdump.c
CMake: Fix the comment about versions. [skip ci]
[tcpdump] / tcpdump.c
index 40505b2c28c75a3cbda8c6144920738d4e4efb5f..9bab8d87850044e0bb1051346bd77642c556901b 100644 (file)
--- a/tcpdump.c
+++ b/tcpdump.c
  * combined efforts of Van, Steve McCanne and Craig Leres of LBL.
  */
 
-#ifdef HAVE_CONFIG_H
 #include <config.h>
-#endif
 
 /*
- * Some older versions of Mac OS X may ship pcap.h from libpcap 0.6 with a
+ * Some older versions of Mac OS X 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().
@@ -63,9 +61,7 @@ The Regents of the University of California.  All rights reserved.\n";
 
 #include <sys/stat.h>
 
-#ifdef HAVE_FCNTL_H
 #include <fcntl.h>
-#endif
 
 #ifdef HAVE_LIBCRYPTO
 #include <openssl/crypto.h>
@@ -78,7 +74,9 @@ The Regents of the University of California.  All rights reserved.\n";
 #endif
 /* Capsicum-specific code requires macros from <net/bpf.h>, which will fail
  * to compile if <pcap.h> 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 <pcap/pcap.h> declares bpf_dump() instead of <net/bpf.h>. Thus
+ * interface.h takes care of it later to avoid a compiler warning.
  */
 #ifdef HAVE_CAPSICUM
 #include <sys/capsicum.h>
@@ -124,6 +122,16 @@ The Regents of the University of California.  All rights reserved.\n";
 #include <grp.h>
 #endif /* _WIN32 */
 
+/*
+ * Pathname separator.
+ * Use this in pathnames, but do *not* use it in URLs.
+ */
+#ifdef _WIN32
+#define PATH_SEPARATOR '\\'
+#else
+#define PATH_SEPARATOR '/'
+#endif
+
 /* 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.
@@ -141,6 +149,7 @@ The Regents of the University of California.  All rights reserved.\n";
 #include <sys/sysctl.h>
 #endif /* __FreeBSD__ */
 
+#include "netdissect-stdinc.h"
 #include "netdissect.h"
 #include "interface.h"
 #include "addrtoname.h"
@@ -150,6 +159,8 @@ The Regents of the University of California.  All rights reserved.\n";
 
 #include "print.h"
 
+#include "diag-control.h"
+
 #include "fptype.h"
 
 #ifndef PATH_MAX
@@ -220,16 +231,13 @@ 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 int infodelay;
 static int infoprint;
 
 char *program_name;
 
-#ifdef HAVE_CASPER
-cap_channel_t *capdns;
-#endif
-
 /* Forwards */
 static NORETURN void error(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
 static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
@@ -237,8 +245,8 @@ static NORETURN void exit_tcpdump(int);
 static void (*setsignal (int sig, void (*func)(int)))(int);
 static void cleanup(int);
 static void child_cleanup(int);
-static void print_version(void);
-static void print_usage(void);
+static void print_version(FILE *);
+static void print_usage(FILE *);
 #ifdef HAVE_PCAP_SET_TSTAMP_TYPE
 static NORETURN void show_tstamp_types_and_exit(pcap_t *, const char *device);
 #endif
@@ -421,15 +429,15 @@ 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);
@@ -458,28 +466,30 @@ 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]);
                }
        }
@@ -690,6 +700,8 @@ show_remote_devices_and_exit(void)
 #define OPTION_TSTAMP_MICRO            133
 #define OPTION_TSTAMP_NANO             134
 #define OPTION_FP_TYPE                 135
+#define OPTION_COUNT                   136
+#define OPTION_TIME_T_SIZE             139
 
 static const struct option longopts[] = {
 #if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
@@ -733,9 +745,11 @@ static const struct option longopts[] = {
        { "debug-filter-parser", no_argument, NULL, 'Y' },
 #endif
        { "relinquish-privileges", required_argument, NULL, 'Z' },
+       { "count", no_argument, NULL, OPTION_COUNT },
        { "fp-type", no_argument, NULL, OPTION_FP_TYPE },
        { "number", no_argument, NULL, '#' },
        { "print", no_argument, NULL, OPTION_PRINT },
+       { "time-t-size", no_argument, NULL, OPTION_TIME_T_SIZE },
        { "version", no_argument, NULL, OPTION_VERSION },
        { NULL, 0, NULL, 0 }
 };
@@ -792,8 +806,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 +815,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 */
 
@@ -828,7 +842,9 @@ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars)
 {
         char *filename = malloc(PATH_MAX + 1);
         if (filename == NULL)
-            error("Makefilename: malloc");
+            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) {
@@ -836,13 +852,29 @@ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars)
 
           /* Convert Gflag_time to a usable format */
           if ((local_tm = localtime(&Gflag_time)) == NULL) {
-                  error("MakeTimedFilename: localtime");
+                  error("%s: localtime", __func__);
           }
 
           /* 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);
         }
@@ -938,14 +970,14 @@ tstamp_precision_to_string(int precision)
  * 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
+ * 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.
+ * regular files notwithstanding.
  *
  * The Capsicum people decided that fine-grained control of descriptor
  * operations was required, so that you need to grant permission for
@@ -1022,7 +1054,7 @@ copy_argv(char **argv)
 
        buf = (char *)malloc(len);
        if (buf == NULL)
-               error("copy_argv: malloc");
+               error("%s: malloc", __func__);
 
        p = argv;
        dst = buf;
@@ -1052,15 +1084,22 @@ read_infile(char *fname)
        int i, fd;
        ssize_t cc;
        char *cp;
-       struct stat buf;
+       our_statb buf;
 
        fd = open(fname, O_RDONLY|O_BINARY);
        if (fd < 0)
                error("can't open %s: %s", fname, pcap_strerror(errno));
 
-       if (fstat(fd, &buf) < 0)
+       if (our_fstat(fd, &buf) < 0)
                error("can't stat %s: %s", fname, pcap_strerror(errno));
 
+       /*
+        * Reject files whose size doesn't fit into an int; a filter
+        * *that* large will probably be too big.
+        */
+       if (buf.st_size > INT_MAX)
+               error("%s is too large", fname);
+
        cp = malloc((u_int)buf.st_size + 1);
        if (cp == NULL)
                error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
@@ -1069,7 +1108,8 @@ read_infile(char *fname)
        if (cc < 0)
                error("read %s: %s", fname, pcap_strerror(errno));
        if (cc != buf.st_size)
-               error("short read %s (%zd != %d)", fname, cc, (int)buf.st_size);
+               error("short read %s (%d != %d)", fname, (int) cc,
+                   (int)buf.st_size);
 
        close(fd);
        /* replace "# comment" with spaces */
@@ -1124,7 +1164,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 {
@@ -1199,8 +1239,11 @@ _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);
@@ -1478,6 +1521,157 @@ main(int argc, char **argv)
        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 againt
+        *     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
+
        /*
         * Initialize the netdissect code.
         */
@@ -1495,7 +1689,7 @@ main(int argc, char **argv)
        VFile = NULL;
        WFileName = NULL;
        dlt = -1;
-       if ((cp = strrchr(argv[0], '/')) != NULL)
+       if ((cp = strrchr(argv[0], PATH_SEPARATOR)) != NULL)
                ndo->program_name = program_name = cp + 1;
        else
                ndo->program_name = program_name = argv[0];
@@ -1518,6 +1712,13 @@ main(int argc, char **argv)
        if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
                error("%s", ebuf);
 
+       /*
+        * 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.
+        */
+       tzset();
+
        while (
            (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
                switch (op) {
@@ -1619,13 +1820,13 @@ main(int argc, char **argv)
 
                        /* 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));
+                               error("%s: can't get current time: %s",
+                                   __func__, pcap_strerror(errno));
                        }
                        break;
 
                case 'h':
-                       print_usage();
+                       print_usage(stdout);
                        exit_tcpdump(S_SUCCESS);
                        break;
 
@@ -1686,7 +1887,7 @@ 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");
                        }
@@ -1730,7 +1931,7 @@ 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 */
 
@@ -1791,8 +1992,12 @@ main(int argc, char **argv)
                                ndo->ndo_packettype = PT_RESP;
                        else if (ascii_strcasecmp(optarg, "ptp") == 0)
                                ndo->ndo_packettype = PT_PTP;
+                       else if (ascii_strcasecmp(optarg, "someip") == 0)
+                               ndo->ndo_packettype = PT_SOMEIP;
+                       else if (ascii_strcasecmp(optarg, "domain") == 0)
+                               ndo->ndo_packettype = PT_DOMAIN;
                        else
-                               error("unknown packet type `%s'", optarg);
+                               error("unknown packet type '%s'", optarg);
                        break;
 
                case 'u':
@@ -1862,8 +2067,12 @@ main(int argc, char **argv)
                        ndo->ndo_packet_number = 1;
                        break;
 
+               case OPTION_TIME_T_SIZE:
+                       printf("%zu\n", sizeof(time_t) * 8);
+                       return 0;
+
                case OPTION_VERSION:
-                       print_version();
+                       print_version(stdout);
                        exit_tcpdump(S_SUCCESS);
                        break;
 
@@ -1903,24 +2112,26 @@ main(int argc, char **argv)
                         * may differ (e.g., it might be 32-bit, 64-bit,
                         * or 80-bit).
                         */
-                       switch (float_type_check(0x4e93312d)) {
-
-                       case 0x461a5794:
-                               printf("FPTYPE1\n");
-                               break;
-
-                       default:
-                               printf("FPTYPE2\n");
-                               break;
-                       }
+                       float_type_check(0x4e93312d);
                        return 0;
 
+               case OPTION_COUNT:
+                       count_mode = 1;
+                       break;
+
                default:
-                       print_usage();
+                       print_usage(stderr);
                        exit_tcpdump(S_ERR_HOST_PROGRAM);
                        /* NOTREACHED */
                }
 
+       if (ndo->ndo_Aflag && ndo->ndo_xflag)
+               warning("-A and -x[x] are mutually exclusive. -A ignored.");
+       if (ndo->ndo_Aflag && ndo->ndo_Xflag)
+               warning("-A and -X[X] are mutually exclusive. -A ignored.");
+       if (ndo->ndo_xflag && ndo->ndo_Xflag)
+               warning("-x[x] and -X[X] are mutually exclusive. -x[x] ignored.");
+
 #ifdef HAVE_PCAP_FINDALLDEVS
        if (Dflag)
                show_devices_and_exit();
@@ -1979,6 +2190,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
 
@@ -2178,9 +2391,28 @@ main(int argc, char **argv)
                        }
 #endif
                        (void)fprintf(stderr, "%s: data link type %s\n",
-                                     program_name, yflag_dlt_name);
+                                     program_name,
+                                     pcap_datalink_val_to_name(yflag_dlt));
                        (void)fflush(stderr);
                }
+#if defined(DLT_LINUX_SLL2) && defined(HAVE_PCAP_SET_DATALINK)
+               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)
@@ -2232,7 +2464,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 */
 
@@ -2260,33 +2506,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 */
@@ -2352,17 +2598,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 &&
@@ -2389,7 +2662,7 @@ DIAG_ON_CLANG(assign-enum)
                }
                if (print) {
                        dlt = pcap_datalink(pd);
-                       ndo->ndo_if_printer = get_if_printer(ndo, dlt);
+                       ndo->ndo_if_printer = get_if_printer(dlt);
                        dumpinfo.ndo = ndo;
                } else
                        dumpinfo.ndo = NULL;
@@ -2400,7 +2673,7 @@ DIAG_ON_CLANG(assign-enum)
 #endif
        } else {
                dlt = pcap_datalink(pd);
-               ndo->ndo_if_printer = get_if_printer(ndo, dlt);
+               ndo->ndo_if_printer = get_if_printer(dlt);
                callback = print_packet;
                pcap_userdata = (u_char *)ndo;
        }
@@ -2417,11 +2690,14 @@ DIAG_ON_CLANG(assign-enum)
        (void)setsignal(SIGNAL_FLUSH_PCAP, flushpcap);
 #endif
 
-       if (ndo->ndo_vflag > 0 && WFileName && !print) {
+       if (ndo->ndo_vflag > 0 && WFileName && RFileName == NULL && !print) {
                /*
                 * When capturing to a file, if "--print" wasn't specified,
                 *"-v" means tcpdump should, once per second,
                 * "v"erbosely report the number of packets captured.
+                * Except when reading from a file, because -r, -w and -v
+                * together used to make a corner case, in which pcap_loop()
+                * errored due to EINTR (see GH #155 for details).
                 */
 #ifdef _WIN32
                /*
@@ -2459,7 +2735,7 @@ DIAG_ON_CLANG(assign-enum)
                 */
                if (!ndo->ndo_vflag && !WFileName) {
                        (void)fprintf(stderr,
-                           "%s: verbose output suppressed, use -v or -vv for full protocol decode\n",
+                           "%s: verbose output suppressed, use -v[v]... for full protocol decode\n",
                            program_name);
                } else
                        (void)fprintf(stderr, "%s: ", program_name);
@@ -2483,6 +2759,9 @@ 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 */
@@ -2573,7 +2852,9 @@ DIAG_ON_CLANG(assign-enum)
                                         * the new DLT.
                                         */
                                        dlt = new_dlt;
-                                       ndo->ndo_if_printer = get_if_printer(ndo, dlt);
+                                       ndo->ndo_if_printer = get_if_printer(dlt);
+                                       /* Free the old filter */
+                                       pcap_freecode(&fcode);
                                        if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
                                                error("%s", pcap_geterr(pd));
                                }
@@ -2602,9 +2883,13 @@ DIAG_ON_CLANG(assign-enum)
        }
        while (ret != NULL);
 
+       if (count_mode && RFileName != NULL)
+               fprintf(stdout, "%u packet%s\n", packets_captured,
+                       PLURAL_SUFFIX(packets_captured));
+
        free(cmdbuf);
        pcap_freecode(&fcode);
-       exit_tcpdump(status == -1 ? 1 : 0);
+       exit_tcpdump(status == -1 ? S_ERR_HOST_PROGRAM : S_SUCCESS);
 }
 
 /*
@@ -2620,10 +2905,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
 }
@@ -2685,7 +2984,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 */
 
@@ -2812,8 +3111,8 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
 
                /* 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));
+                       error("%s: can't get current_time: %s",
+                           __func__, pcap_strerror(errno));
                }
 
 
@@ -2954,7 +3253,7 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
                                free(dump_info->CurrentFileName);
                        dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
                        if (dump_info->CurrentFileName == NULL)
-                               error("dump_packet_and_trunc: malloc");
+                               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);
@@ -3034,7 +3333,8 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
 
        ++infodelay;
 
-       pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
+       if (!count_mode)
+               pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
 
        --infodelay;
        if (infoprint)
@@ -3089,9 +3389,9 @@ static void verbose_stats_dump(int sig _U_)
 }
 #endif /* _WIN32 */
 
-USES_APPLE_DEPRECATED_API
+DIAG_OFF_DEPRECATION
 static void
-print_version(void)
+print_version(FILE *f)
 {
 #ifndef HAVE_PCAP_LIB_VERSION
   #ifdef HAVE_PCAP_VERSION
@@ -3102,59 +3402,63 @@ print_version(void)
 #endif /* HAVE_PCAP_LIB_VERSION */
        const char *smi_version_string;
 
-       (void)fprintf(stderr, "%s version " PACKAGE_VERSION "\n", program_name);
+       (void)fprintf(f, "%s version " PACKAGE_VERSION "\n", program_name);
 #ifdef HAVE_PCAP_LIB_VERSION
-       (void)fprintf(stderr, "%s\n", pcap_lib_version());
+       (void)fprintf(f, "%s\n", pcap_lib_version());
 #else /* HAVE_PCAP_LIB_VERSION */
-       (void)fprintf(stderr, "libpcap version %s\n", pcap_version);
+       (void)fprintf(f, "libpcap version %s\n", pcap_version);
 #endif /* HAVE_PCAP_LIB_VERSION */
 
 #if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION)
-       (void)fprintf (stderr, "%s\n", SSLeay_version(SSLEAY_VERSION));
+       (void)fprintf (f, "%s\n", SSLeay_version(SSLEAY_VERSION));
 #endif
 
        smi_version_string = nd_smi_version_string();
        if (smi_version_string != NULL)
-               (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string);
+               (void)fprintf (f, "SMI-library: %s\n", smi_version_string);
 
 #if defined(__SANITIZE_ADDRESS__)
-       (void)fprintf (stderr, "Compiled with AddressSanitizer/GCC.\n");
+       (void)fprintf (f, "Compiled with AddressSanitizer/GCC.\n");
 #elif defined(__has_feature)
 #  if __has_feature(address_sanitizer)
-       (void)fprintf (stderr, "Compiled with AddressSanitizer/CLang.\n");
+       (void)fprintf (f, "Compiled with AddressSanitizer/Clang.\n");
+#  elif __has_feature(memory_sanitizer)
+       (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(void)
+print_usage(FILE *f)
 {
-       print_version();
-       (void)fprintf(stderr,
-"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,
+       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);
+       (void)fprintf(f,
 "\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n");
 #ifdef HAVE_PCAP_FINDALLDEVS_EX
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t" LIST_REMOTE_INTERFACES_USAGE "\n");
 #endif
 #ifdef USE_LIBSMI
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t" m_FLAG_USAGE "\n");
 #endif
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n");
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n");
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n");
 #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n");
 #endif
-       (void)fprintf(stderr,
+       (void)fprintf(f,
 "\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n");
 }