+#endif /* _WIN32 */
+
+ if (pcap_setfilter(pd, &fcode) < 0)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ if (RFileName == NULL && VFileName == NULL && pcap_fileno(pd) != -1) {
+ static const unsigned long cmds[] = { BIOCGSTATS, BIOCROTZBUF };
+
+ /*
+ * The various libpcap devices use a combination of
+ * read (bpf), ioctl (bpf, netmap), poll (netmap)
+ * so we add the relevant access rights.
+ */
+ cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_EVENT);
+ if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+ if (cap_ioctls_limit(pcap_fileno(pd), cmds,
+ sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) {
+ error("unable to limit ioctls on pcap descriptor");
+ }
+ }
+#endif
+ if (WFileName) {
+ /* Do not exceed the default PATH_MAX for files. */
+ dumpinfo.CurrentFileName = (char *)malloc(PATH_MAX + 1);
+
+ if (dumpinfo.CurrentFileName == NULL)
+ error("malloc of dumpinfo.CurrentFileName");
+
+ /* We do not need numbering for dumpfiles if Cflag isn't set. */
+ if (Cflag != 0)
+ MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars);
+ else
+ MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
+
+ pdd = pcap_dump_open(pd, dumpinfo.CurrentFileName);
+#ifdef HAVE_LIBCAP_NG
+ /* Give up CAP_DAC_OVERRIDE capability.
+ * Only allow it to be restored if the -C or -G flag have been
+ * set since we may need to create more files later on.
+ */
+ capng_update(
+ CAPNG_DROP,
+ (Cflag || Gflag ? 0 : CAPNG_PERMITTED)
+ | CAPNG_EFFECTIVE,
+ CAP_DAC_OVERRIDE
+ );
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+ if (pdd == NULL)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(pdd);
+#endif
+ if (Cflag != 0 || Gflag != 0) {
+#ifdef HAVE_CAPSICUM
+ /*
+ * 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);
+ }
+ 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",
+ 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 &&
+ errno != ENOSYS) {
+ error("unable to limit directory rights");
+ }
+ if (cap_fcntls_limit(dumpinfo.dirfd, CAP_FCNTL_GETFL) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit dump descriptor fcntls");
+ }
+#else /* !HAVE_CAPSICUM */
+ dumpinfo.WFileName = WFileName;
+#endif
+ callback = dump_packet_and_trunc;
+ dumpinfo.pd = pd;
+ dumpinfo.pdd = pdd;
+ pcap_userdata = (u_char *)&dumpinfo;
+ } else {
+ callback = dump_packet;
+ dumpinfo.WFileName = WFileName;
+ dumpinfo.pd = pd;
+ dumpinfo.pdd = pdd;
+ pcap_userdata = (u_char *)&dumpinfo;
+ }
+ if (print) {
+ dlt = pcap_datalink(pd);
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ dumpinfo.ndo = ndo;
+ } else
+ dumpinfo.ndo = NULL;
+
+ if (Uflag)
+ pcap_dump_flush(pdd);
+ } else {
+ dlt = pcap_datalink(pd);
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ callback = print_packet;
+ pcap_userdata = (u_char *)ndo;
+ }
+
+#ifdef SIGNAL_REQ_INFO
+ /*
+ * We can't get statistics when reading from a file rather
+ * than capturing from a device.
+ */
+ if (RFileName == NULL)
+ (void)setsignal(SIGNAL_REQ_INFO, requestinfo);
+#endif
+#ifdef SIGNAL_FLUSH_PCAP
+ (void)setsignal(SIGNAL_FLUSH_PCAP, flushpcap);
+#endif
+
+ 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
+ /*
+ * https://blogs.msdn.microsoft.com/oldnewthing/20151230-00/?p=92741
+ *
+ * suggests that this dates back to W2K.
+ *
+ * I don't know what a "long wait" is, but we'll assume
+ * that printing the stats could be a "long wait".
+ */
+ CreateTimerQueueTimer(&timer_handle, NULL,
+ verbose_stats_dump, NULL, 1000, 1000,
+ WT_EXECUTEDEFAULT|WT_EXECUTELONGFUNCTION);
+ setvbuf(stderr, NULL, _IONBF, 0);
+#else /* _WIN32 */
+ /*
+ * Assume this is UN*X, and that it has setitimer(); that
+ * dates back to UNIX 95.
+ */
+ struct itimerval timer;
+ (void)setsignal(SIGALRM, verbose_stats_dump);
+ timer.it_interval.tv_sec = 1;
+ timer.it_interval.tv_usec = 0;
+ timer.it_value.tv_sec = 1;
+ timer.it_value.tv_usec = 1;
+ setitimer(ITIMER_REAL, &timer, NULL);
+#endif /* _WIN32 */
+ }
+
+ if (RFileName == NULL) {
+ /*
+ * Live capture (if -V was specified, we set RFileName
+ * to a file from the -V file). Print a message to
+ * the standard error on UN*X.
+ */
+ if (!ndo->ndo_vflag && !WFileName) {
+ (void)fprintf(stderr,
+ "%s: verbose output suppressed, use -v[v]... for full protocol decode\n",
+ program_name);
+ } else
+ (void)fprintf(stderr, "%s: ", program_name);
+ dlt = pcap_datalink(pd);
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ (void)fprintf(stderr, "listening on %s", device);
+ if (dlt_name == NULL) {
+ (void)fprintf(stderr, ", link-type %u", dlt);
+ } else {
+ (void)fprintf(stderr, ", link-type %s (%s)", dlt_name,
+ pcap_datalink_val_to_description(dlt));
+ }
+ (void)fprintf(stderr, ", snapshot length %d bytes\n", ndo->ndo_snaplen);
+ (void)fflush(stderr);
+ }
+
+#ifdef HAVE_CAPSICUM
+ cansandbox = (VFileName == NULL && zflag == NULL);
+#ifdef HAVE_CASPER
+ cansandbox = (cansandbox && (ndo->ndo_nflag || capdns != NULL));
+#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 == -1 ? -1 : cnt + (int)packets_to_skip),
+ callback, pcap_userdata);
+ if (WFileName == NULL) {
+ /*
+ * We're printing packets. Flush the printed output,
+ * so it doesn't get intermingled with error output.
+ */
+ if (status == -2) {
+ /*
+ * We got interrupted, so perhaps we didn't
+ * manage to finish a line we were printing.
+ * Print an extra newline, just in case.
+ */
+ putchar('\n');
+ }
+ (void)fflush(stdout);
+ }
+ if (status == -2) {
+ /*
+ * We got interrupted. If we are reading multiple
+ * files (via -V) set these so that we stop.
+ */
+ VFileName = NULL;
+ ret = NULL;
+ }
+ if (status == -1) {
+ /*
+ * Error. Report it.
+ */
+ (void)fprintf(stderr, "%s: pcap_loop: %s\n",
+ program_name, pcap_geterr(pd));
+ }
+ if (RFileName == NULL) {
+ /*
+ * We're doing a live capture. Report the capture
+ * statistics.
+ */
+ info(1);
+ }
+ pcap_close(pd);
+ if (VFileName != NULL) {
+ ret = get_next_file(VFile, VFileLine);
+ if (ret) {
+ int new_dlt;
+
+ RFileName = VFileLine;
+ pd = pcap_open_offline(RFileName, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+#ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)),
+ &rights) < 0 && errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+#endif
+ new_dlt = pcap_datalink(pd);
+ if (new_dlt != dlt) {
+ /*
+ * The new file has a different
+ * link-layer header type from the
+ * previous one.
+ */
+ if (WFileName != NULL) {
+ /*
+ * We're writing raw packets
+ * that match the filter to
+ * a pcap file. pcap files
+ * don't support multiple
+ * different link-layer
+ * header types, so we fail
+ * here.
+ */
+ error("%s: new dlt does not match original", RFileName);
+ }
+
+ /*
+ * We're printing the decoded packets;
+ * switch to the new DLT.
+ *
+ * To do that, we need to change
+ * the printer, change the DLT name,
+ * and recompile the filter with
+ * the new DLT.
+ */
+ dlt = new_dlt;
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ /* 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));
+ }
+
+ /*
+ * Set the filter on the new file.
+ */
+ if (pcap_setfilter(pd, &fcode) < 0)
+ error("%s", pcap_geterr(pd));
+
+ /*
+ * Report the new file.
+ */
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ fprintf(stderr, "reading from file %s", RFileName);
+ if (dlt_name == NULL) {
+ fprintf(stderr, ", link-type %u", dlt);
+ } else {
+ fprintf(stderr, ", link-type %s (%s)",
+ dlt_name,
+ pcap_datalink_val_to_description(dlt));
+ }
+ fprintf(stderr, ", snapshot length %d\n", pcap_snapshot(pd));
+ }
+ }
+ }
+ 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 ? 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);
+ }