]> The Tcpdump Group git mirrors - tcpdump/blobdiff - tcpdump.c
Replace some command name 'Tcpdump' with 'tcpdump'
[tcpdump] / tcpdump.c
index 95162ecc18165f9a4518354684698e783b94ebfd..7159d1c76fd8677ea395f6e4321c58768f27721e 100644 (file)
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -38,7 +38,7 @@
 #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().
@@ -78,7 +78,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>
@@ -161,6 +163,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
@@ -238,10 +242,6 @@ static int infoprint;
 
 char *program_name;
 
-#ifdef HAVE_CASPER
-cap_channel_t *capdns;
-#endif
-
 /* Forwards */
 static void (*setsignal (int sig, void (*func)(int)))(int);
 static void cleanup(int);
@@ -563,8 +563,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)
@@ -691,6 +704,7 @@ 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
 
 static const struct option longopts[] = {
 #if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
@@ -738,6 +752,7 @@ 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 },
        { "version", no_argument, NULL, OPTION_VERSION },
        { NULL, 0, NULL, 0 }
 };
@@ -794,8 +809,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,
@@ -803,7 +818,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 */
 
@@ -831,6 +846,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) {
@@ -842,9 +859,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);
         }
@@ -896,6 +929,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");
@@ -947,7 +981,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
@@ -963,7 +997,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
@@ -1248,6 +1282,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
@@ -1417,7 +1463,7 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf)
                if (status != 0)
                        error("%s: pcap_setdirection() failed: %s",
                              device,  pcap_geterr(pc));
-               }
+       }
 #endif /* HAVE_PCAP_SETDIRECTION */
 #else /* HAVE_PCAP_CREATE */
        *ebuf = '\0';
@@ -1529,6 +1575,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) {
@@ -1755,7 +1808,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");
                        }
@@ -1799,7 +1852,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 */
 
@@ -1864,8 +1917,10 @@ 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':
@@ -1958,6 +2013,14 @@ main(int argc, char **argv)
                        print = 1;
                        break;
 
+               case OPTION_PRINT_SAMPLING:
+                       print = 1;
+                       ++ndo->ndo_Sflag;
+                       ndo->ndo_print_sampling = atoi(optarg);
+                       if (ndo->ndo_print_sampling <= 0)
+                               error("invalid print sampling %s", optarg);
+                       break;
+
 #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
                case OPTION_TSTAMP_MICRO:
                        ndo->ndo_tstamp_precision = PCAP_TSTAMP_PRECISION_MICRO;
@@ -1998,14 +2061,6 @@ main(int argc, char **argv)
                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 */
@@ -2055,6 +2110,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
 
@@ -2258,6 +2315,24 @@ main(int argc, char **argv)
                                      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)
@@ -2322,7 +2397,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
@@ -2337,33 +2412,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 */
@@ -2429,17 +2504,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 &&
@@ -2563,6 +2665,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 */
@@ -2654,6 +2759,8 @@ DIAG_ON_CLANG(assign-enum)
                                         */
                                        dlt = new_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));
                                }
@@ -2688,7 +2795,7 @@ 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);
 }
 
 /*
@@ -2704,7 +2811,14 @@ 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)
                return (SIG_ERR);
@@ -2769,7 +2883,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 */
 
@@ -3174,7 +3288,7 @@ static void verbose_stats_dump(int sig _U_)
 }
 #endif /* _WIN32 */
 
-USES_APPLE_DEPRECATED_API
+DIAG_OFF_DEPRECATION
 static void
 print_version(FILE *f)
 {
@@ -3212,7 +3326,7 @@ print_version(FILE *f)
 #  endif
 #endif /* __SANITIZE_ADDRESS__ or __has_feature */
 }
-USES_APPLE_RST
+DIAG_ON_DEPRECATION
 
 static void
 print_usage(FILE *f)
@@ -3233,9 +3347,11 @@ print_usage(FILE *f)
 "\t\t" m_FLAG_USAGE "\n");
 #endif
        (void)fprintf(f,
-"\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n");
+"\t\t[ -M secret ] [ --number ] [ --print ]\n");
+       (void)fprintf(f,
+"\t\t[ --print-sampling nth ]" Q_FLAG_USAGE " [ -r file ]\n");
        (void)fprintf(f,
-"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n");
+"\t\t[ -s snaplen ] [ -T type ] [ --version ]\n");
        (void)fprintf(f,
 "\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n");
 #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION