/* * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Support for splitting captures into multiple files with a maximum * file size: * * Copyright (c) 2001 * Seth Webster */ /* * tcpdump - dump traffic on a network * * First written in 1987 by Van Jacobson, Lawrence Berkeley Laboratory. * Mercilessly hacked and occasionally improved since then via the * combined efforts of Van, Steve McCanne and Craig Leres of LBL. */ #ifdef HAVE_CONFIG_H #include #endif /* * 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(). */ #ifdef HAVE_PCAP_FINDALLDEVS #ifndef HAVE_PCAP_IF_T #undef HAVE_PCAP_FINDALLDEVS #endif #endif #include "netdissect-stdinc.h" /* * This must appear after including netdissect-stdinc.h, so that _U_ is * defined. */ #ifndef lint static const char copyright[] _U_ = "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\ The Regents of the University of California. All rights reserved.\n"; #endif #include #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIBCRYPTO #include #endif #ifdef HAVE_GETOPT_LONG #include #else #include "missing/getopt_long.h" #endif /* Capsicum-specific code requires macros from , which will fail * to compile if has already been included; including the headers * in the opposite order works fine. 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 #include #include #include #ifdef HAVE_CASPER #include #include #include #endif /* HAVE_CASPER */ #endif /* HAVE_CAPSICUM */ #ifdef HAVE_PCAP_OPEN /* * We found pcap_open() in the capture library, so we'll be using * the remote capture APIs; define PCAP_REMOTE before we include pcap.h, * so we get those APIs declared, and the types and #defines that they * use defined. * * WinPcap's headers require that PCAP_REMOTE be defined in order to get * remote-capture APIs declared and types and #defines that they use * defined. * * (Versions of libpcap with those APIs, and thus Npcap, which is based on * those versions of libpcap, don't require it.) */ #define HAVE_REMOTE #endif #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #include #include #include #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. * Thus, the later tests are done only on HAVE_LIBCAP_NG. */ #ifdef HAVE_LIBCAP_NG #ifdef HAVE_CAP_NG_H #include #else #undef HAVE_LIBCAP_NG #endif /* HAVE_CAP_NG_H */ #endif /* HAVE_LIBCAP_NG */ #ifdef __FreeBSD__ #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 #define PATH_MAX 1024 #endif #if defined(SIGINFO) #define SIGNAL_REQ_INFO SIGINFO #elif defined(SIGUSR1) #define SIGNAL_REQ_INFO SIGUSR1 #endif #if defined(HAVE_PCAP_DUMP_FLUSH) && 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 /* * This is exported because, in some versions of libpcap, if libpcap * is built with optimizer debugging code (which is *NOT* the default * configuration!), the library *imports*(!) a variable named dflag, * under the expectation that tcpdump is exporting it, to govern * how much debugging information to print when optimizing * the generated BPF code. * * This is a horrible hack; newer versions of libpcap don't import * dflag but, instead, *if* built with optimizer debugging code, * *export* a routine to set that flag. */ extern int dflag; int dflag; /* print filter code */ static int Gflag; /* rotate dump files after this many seconds */ static int Gflag_count; /* number of files created with Gflag rotation */ static time_t Gflag_time; /* The last time_t the dump file was rotated. */ static int Lflag; /* list available data link types and exit */ static int Iflag; /* rfmon (monitor) mode */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE static int Jflag; /* list available time stamp types */ 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; static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */ 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; /* Forwards */ static void (*setsignal (int sig, void (*func)(int)))(int); static void cleanup(int); static void child_cleanup(int); static void print_version(FILE *); static void print_usage(FILE *); static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); #ifdef SIGNAL_REQ_INFO static void requestinfo(int); #endif #ifdef SIGNAL_FLUSH_PCAP static void flushpcap(int); #endif #ifdef _WIN32 static HANDLE timer_handle = INVALID_HANDLE_VALUE; static void CALLBACK verbose_stats_dump(PVOID param, BOOLEAN timer_fired); #else /* _WIN32 */ static void verbose_stats_dump(int sig); #endif /* _WIN32 */ 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" }, #endif #ifdef PCAP_IF_RUNNING { PCAP_IF_RUNNING, "Running" }, #endif { PCAP_IF_LOOPBACK, "Loopback" }, #ifdef PCAP_IF_WIRELESS { PCAP_IF_WIRELESS, "Wireless" }, #endif { 0, NULL } }; #endif static pcap_t *pd; static pcap_dumper_t *pdd = NULL; static int supports_monitor_mode; extern int optind; extern int opterr; extern char *optarg; struct dump_info { char *WFileName; char *CurrentFileName; pcap_t *pd; pcap_dumper_t *pdd; netdissect_options *ndo; #ifdef HAVE_CAPSICUM int dirfd; #endif }; #if defined(HAVE_PCAP_SET_PARSER_DEBUG) /* * We have pcap_set_parser_debug() in libpcap; declare it (it's not declared * by any libpcap header, because it's a special hack, only available if * libpcap was configured to include it, and only intended for use by * libpcap developers trying to debug the parser for filter expressions). */ #ifdef _WIN32 __declspec(dllimport) #else /* _WIN32 */ extern #endif /* _WIN32 */ void pcap_set_parser_debug(int); #elif defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG) /* * We don't have pcap_set_parser_debug() in libpcap, but we do have * pcap_debug or yydebug. Make a local version of pcap_set_parser_debug() * to set the flag, and define HAVE_PCAP_SET_PARSER_DEBUG. */ static void pcap_set_parser_debug(int value) { #ifdef HAVE_PCAP_DEBUG extern int pcap_debug; pcap_debug = value; #else /* HAVE_PCAP_DEBUG */ extern int yydebug; yydebug = value; #endif /* HAVE_PCAP_DEBUG */ } #define HAVE_PCAP_SET_PARSER_DEBUG #endif #if defined(HAVE_PCAP_SET_OPTIMIZER_DEBUG) /* * We have pcap_set_optimizer_debug() in libpcap; declare it (it's not declared * by any libpcap header, because it's a special hack, only available if * libpcap was configured to include it, and only intended for use by * libpcap developers trying to debug the optimizer for filter expressions). */ #ifdef _WIN32 __declspec(dllimport) #else /* _WIN32 */ extern #endif /* _WIN32 */ void pcap_set_optimizer_debug(int); #endif static void NORETURN exit_tcpdump(const int status) { nd_cleanup(); exit(status); } /* VARARGS */ static void NORETURN PRINTFLIKE(1, 2) error(FORMAT_STRING(const char *fmt), ...) { va_list ap; (void)fprintf(stderr, "%s: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } exit_tcpdump(S_ERR_HOST_PROGRAM); /* NOTREACHED */ } /* VARARGS */ static void PRINTFLIKE(1, 2) warning(FORMAT_STRING(const char *fmt), ...) { va_list ap; (void)fprintf(stderr, "%s: WARNING: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } } #ifdef HAVE_PCAP_SET_TSTAMP_TYPE static void NORETURN show_tstamp_types_and_exit(pcap_t *pc, const char *device) { int n_tstamp_types; int *tstamp_types = 0; const char *tstamp_type_name; int i; n_tstamp_types = pcap_list_tstamp_types(pc, &tstamp_types); if (n_tstamp_types < 0) error("%s", pcap_geterr(pc)); if (n_tstamp_types == 0) { fprintf(stderr, "Time stamp type cannot be set for %s\n", device); exit_tcpdump(S_SUCCESS); } 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(stdout, " %s (%s)\n", tstamp_type_name, pcap_tstamp_type_val_to_description(tstamp_types[i])); } else { (void) fprintf(stdout, " %d\n", tstamp_types[i]); } } pcap_free_tstamp_types(tstamp_types); exit_tcpdump(S_SUCCESS); } #endif static void NORETURN show_dlts_and_exit(pcap_t *pc, const char *device) { int n_dlts, i; int *dlts = 0; const char *dlt_name; n_dlts = pcap_list_datalinks(pc, &dlts); if (n_dlts < 0) error("%s", pcap_geterr(pc)); else if (n_dlts == 0 || !dlts) error("No data link types."); /* * If the interface is known to support monitor mode, indicate * whether these are the data link types available when not in * monitor mode, if -I wasn't specified, or when in monitor mode, * when -I was specified (the link-layer types available in * monitor mode might be different from the ones available when * not in monitor mode). */ (void) fprintf(stdout, "Data link types for "); if (supports_monitor_mode) (void) fprintf(stdout, "%s %s", device, Iflag ? "when in monitor mode" : "when not in monitor mode"); else (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(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(stdout, " (printing not supported)"); fprintf(stdout, "\n"); } else { (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) { pcap_if_t *dev, *devlist; char ebuf[PCAP_ERRBUF_SIZE]; int i; if (pcap_findalldevs(&devlist, ebuf) < 0) error("%s", ebuf); for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) { printf("%d.%s", i+1, dev->name); if (dev->description != NULL) printf(" (%s)", dev->description); if (dev->flags != 0) { printf(" ["); printf("%s", bittok2str(status_flags, "none", dev->flags)); #ifdef PCAP_IF_WIRELESS if (dev->flags & PCAP_IF_WIRELESS) { switch (dev->flags & PCAP_IF_CONNECTION_STATUS) { case PCAP_IF_CONNECTION_STATUS_UNKNOWN: printf(", Association status unknown"); break; case PCAP_IF_CONNECTION_STATUS_CONNECTED: printf(", Associated"); break; case PCAP_IF_CONNECTION_STATUS_DISCONNECTED: printf(", Not associated"); break; case PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE: break; } } else { switch (dev->flags & PCAP_IF_CONNECTION_STATUS) { case PCAP_IF_CONNECTION_STATUS_UNKNOWN: printf(", Connection status unknown"); break; case PCAP_IF_CONNECTION_STATUS_CONNECTED: printf(", Connected"); break; case PCAP_IF_CONNECTION_STATUS_DISCONNECTED: printf(", Disconnected"); break; case PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE: break; } } #endif printf("]"); } printf("\n"); } pcap_freealldevs(devlist); exit_tcpdump(S_SUCCESS); } #endif /* HAVE_PCAP_FINDALLDEVS */ #ifdef HAVE_PCAP_FINDALLDEVS_EX static void NORETURN show_remote_devices_and_exit(void) { pcap_if_t *dev, *devlist; char ebuf[PCAP_ERRBUF_SIZE]; int i; if (pcap_findalldevs_ex(remote_interfaces_source, NULL, &devlist, ebuf) < 0) error("%s", ebuf); for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) { printf("%d.%s", i+1, dev->name); if (dev->description != NULL) printf(" (%s)", dev->description); if (dev->flags != 0) printf(" [%s]", bittok2str(status_flags, "none", dev->flags)); printf("\n"); } pcap_freealldevs(devlist); exit_tcpdump(S_SUCCESS); } #endif /* HAVE_PCAP_FINDALLDEVS */ /* * Short options. * * Note that there we use all letters for short options except for g, k, * o, and P, and those are used by other versions of tcpdump, and we should * only use them for the same purposes that the other versions of tcpdump * use them: * * macOS tcpdump uses -g to force non--v output for IP to be on one * line, making it more "g"repable; * * macOS tcpdump uses -k to specify that packet comments in pcapng files * should be printed; * * OpenBSD tcpdump uses -o to indicate that OS fingerprinting should be done * for hosts sending TCP SYN packets; * * macOS tcpdump uses -P to indicate that -w should write pcapng rather * than pcap files. * * macOS tcpdump also uses -Q to specify expressions that match packet * metadata, including but not limited to the packet direction. * The expression syntax is different from a simple "in|out|inout", * and those expressions aren't accepted by macOS tcpdump, but the * equivalents would be "in" = "dir=in", "out" = "dir=out", and * "inout" = "dir=in or dir=out", and the parser could conceivably * special-case "in", "out", and "inout" as expressions for backwards * compatibility, so all is not (yet) lost. */ /* * Set up flags that might or might not be supported depending on the * version of libpcap we're using. */ #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 */ #define j_FLAG #define j_FLAG_USAGE #define J_FLAG #endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ #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 ]" #else #define Q_FLAG #define Q_FLAG_USAGE #endif #ifdef HAVE_PCAP_DUMP_FLUSH #define U_FLAG "U" #else #define U_FLAG #endif #define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#" /* * Long options. * * We do not currently have long options corresponding to all short * options; we should probably pick appropriate option names for them. * * However, the short options where the number of times the option is * specified matters, such as -v and -d and -t, should probably not * just map to a long option, as saying * * tcpdump --verbose --verbose * * doesn't make sense; it should be --verbosity={N} or something such * as that. * * For long options with no corresponding short options, we define values * outside the range of ASCII graphic characters, make that the last * component of the entry for the long option, and have a case for that * option in the switch statement. */ #define OPTION_VERSION 128 #define OPTION_TSTAMP_PRECISION 129 #define OPTION_IMMEDIATE_MODE 130 #define OPTION_PRINT 131 #define OPTION_LIST_REMOTE_INTERFACES 132 #define OPTION_TSTAMP_MICRO 133 #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) { "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' }, #endif #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION { "micro", no_argument, NULL, OPTION_TSTAMP_MICRO}, { "nano", no_argument, NULL, OPTION_TSTAMP_NANO}, { "time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION}, #endif { "dont-verify-checksums", no_argument, NULL, 'K' }, { "list-data-link-types", no_argument, NULL, 'L' }, { "no-optimize", no_argument, NULL, 'O' }, { "no-promiscuous-mode", no_argument, NULL, 'p' }, #ifdef HAVE_PCAP_SETDIRECTION { "direction", required_argument, NULL, 'Q' }, #endif { "snapshot-length", required_argument, NULL, 's' }, { "absolute-tcp-sequence-numbers", no_argument, NULL, 'S' }, #ifdef HAVE_PCAP_DUMP_FLUSH { "packet-buffered", no_argument, NULL, 'U' }, #endif { "linktype", required_argument, NULL, 'y' }, #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE }, #endif #ifdef HAVE_PCAP_SET_PARSER_DEBUG { "debug-filter-parser", no_argument, NULL, 'Y' }, #endif { "relinquish-privileges", required_argument, NULL, 'Z' }, { "count", no_argument, NULL, OPTION_COUNT }, { "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 } }; #ifdef HAVE_PCAP_FINDALLDEVS_EX #define LIST_REMOTE_INTERFACES_USAGE "[ --list-remote-interfaces remote-source ]" #else #define LIST_REMOTE_INTERFACES_USAGE #endif #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE #define IMMEDIATE_MODE_USAGE " [ --immediate-mode ]" #else #define IMMEDIATE_MODE_USAGE "" #endif #ifndef _WIN32 /* Drop root privileges and chroot if necessary */ static void droproot(const char *username, const char *chroot_dir) { struct passwd *pw = NULL; if (chroot_dir && !username) error("Chroot without dropping root is insecure"); pw = getpwnam(username); if (pw) { if (chroot_dir) { if (chroot(chroot_dir) != 0 || chdir ("/") != 0) error("Couldn't chroot/chdir to '%.64s': %s", chroot_dir, pcap_strerror(errno)); } #ifdef HAVE_LIBCAP_NG { 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); else fprintf(stderr, "dropped privs to %s\n", username); } #else if (initgroups(pw->pw_name, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) error("Couldn't change to '%.32s' uid=%lu gid=%lu: %s", username, (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid, pcap_strerror(errno)); else { fprintf(stderr, "dropped privs to %s\n", username); } #endif /* HAVE_LIBCAP_NG */ } 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_ASSIGN_ENUM capng_updatev( CAPNG_DROP, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SETUID, CAP_SETGID, CAP_SYS_CHROOT, -1); DIAG_ON_ASSIGN_ENUM capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ } #endif /* _WIN32 */ static int getWflagChars(int x) { int c = 0; x -= 1; while (x > 0) { c += 1; x /= 10; } return c; } static void MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) { char *filename = malloc(PATH_MAX + 1); if (filename == NULL) error("%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) { struct tm *local_tm; /* Convert Gflag_time to a usable format */ if ((local_tm = localtime(&Gflag_time)) == NULL) { 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; 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.) */ if (strftime(filename, PATH_MAX, orig_name, local_tm) == 0) { error("%s: strftime", __func__); } } else { strncpy(filename, orig_name, PATH_MAX); } if (cnt == 0 && max_chars == 0) strncpy(buffer, filename, PATH_MAX + 1); else if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX) /* Report an error if the filename is too large */ error("too many output files or filename is too long (> %d)", PATH_MAX); free(filename); } static char * get_next_file(FILE *VFile, char *ptr) { char *ret; size_t len; ret = fgets(ptr, PATH_MAX, VFile); if (!ret) return NULL; len = strlen (ptr); if (len > 0 && ptr[len - 1] == '\n') ptr[len - 1] = '\0'; return ret; } #ifdef HAVE_CASPER static cap_channel_t * capdns_setup(void) { cap_channel_t *capcas, *capdnsloc; const char *types[1]; int families[2]; capcas = cap_init(); if (capcas == NULL) error("unable to create casper process"); capdnsloc = cap_service_open(capcas, "system.dns"); /* Casper capability no longer needed. */ cap_close(capcas); if (capdnsloc == NULL) error("unable to open system.dns service"); /* Limit system.dns to reverse DNS lookups. */ types[0] = "ADDR"; 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"); return (capdnsloc); } #endif /* HAVE_CASPER */ #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION static int tstamp_precision_from_string(const char *precision) { if (strncmp(precision, "nano", strlen("nano")) == 0) return PCAP_TSTAMP_PRECISION_NANO; if (strncmp(precision, "micro", strlen("micro")) == 0) return PCAP_TSTAMP_PRECISION_MICRO; return -EINVAL; } static const char * tstamp_precision_to_string(int precision) { switch (precision) { case PCAP_TSTAMP_PRECISION_MICRO: return "micro"; case PCAP_TSTAMP_PRECISION_NANO: return "nano"; default: return "unknown"; } } #endif #ifdef HAVE_CAPSICUM /* * Ensure that, on a dump file's descriptor, we have all the rights * necessary to make the standard I/O library work with an fdopen()ed * FILE * from that descriptor. * * A long time ago in a galaxy far, far away, AT&T decided that, instead * of providing separate APIs for getting and setting the FD_ flags on a * descriptor, getting and setting the O_ flags on a descriptor, and * locking files, they'd throw them all into a kitchen-sink fcntl() call * along the lines of ioctl(), the fact that ioctl() operations are * largely specific to particular character devices but fcntl() operations * are either generic to all descriptors or generic to all descriptors for * regular files nonwithstanding. * * The Capsicum people decided that fine-grained control of descriptor * operations was required, so that you need to grant permission for * reading, writing, seeking, and fcntl-ing. The latter, courtesy of * AT&T's decision, means that "fcntl-ing" isn't a thing, but a motley * collection of things, so there are *individual* fcntls for which * permission needs to be granted. * * The FreeBSD standard I/O people implemented some optimizations that * requires that the standard I/O routines be able to determine whether * the descriptor for the FILE * is open append-only or not; as that * descriptor could have come from an open() rather than an fopen(), * that requires that it be able to do an F_GETFL fcntl() to read * the O_ flags. * * Tcpdump uses ftell() to determine how much data has been written * to a file in order to, when used with -C, determine when it's time * to rotate capture files. ftell() therefore needs to do an lseek() * to find out the file offset and must, thanks to the aforementioned * optimization, also know whether the descriptor is open append-only * or not. * * The net result of all the above is that we need to grant CAP_SEEK, * CAP_WRITE, and CAP_FCNTL with the CAP_FCNTL_GETFL subcapability. * * Perhaps this is the universe's way of saying that either * * 1) there needs to be an fopenat() call and a pcap_dump_openat() call * using it, so that Capsicum-capable tcpdump wouldn't need to do * an fdopen() * * or * * 2) there needs to be a cap_fdopen() call in the FreeBSD standard * I/O library that knows what rights are needed by the standard * I/O library, based on the open mode, and assigns them, perhaps * with an additional argument indicating, for example, whether * seeking should be allowed, so that tcpdump doesn't need to know * what the standard I/O library happens to require this week. */ static void set_dumper_capsicum_rights(pcap_dumper_t *p) { int fd = fileno(pcap_dump_file(p)); cap_rights_t rights; cap_rights_init(&rights, CAP_SEEK, CAP_WRITE, CAP_FCNTL); if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) { error("unable to limit dump descriptor"); } if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) { error("unable to limit dump descriptor fcntls"); } } #endif /* * Copy arg vector into a new buffer, concatenating arguments with spaces. */ static char * copy_argv(char **argv) { char **p; size_t len = 0; char *buf; char *src, *dst; p = argv; if (*p == NULL) return 0; while (*p) len += strlen(*p++) + 1; buf = (char *)malloc(len); if (buf == NULL) error("%s: malloc", __func__); p = argv; dst = buf; while ((src = *p++) != NULL) { while ((*dst++ = *src++) != '\0') ; dst[-1] = ' '; } dst[-1] = '\0'; return buf; } /* * On Windows, we need to open the file in binary mode, so that * we get all the bytes specified by the size we get from "fstat()". * On UNIX, that's not necessary. O_BINARY is defined on Windows; * we define it as 0 if it's not defined, so it does nothing. */ #ifndef O_BINARY #define O_BINARY 0 #endif static char * read_infile(char *fname) { int i, fd; ssize_t cc; char *cp; our_statb buf; fd = open(fname, O_RDONLY|O_BINARY); if (fd < 0) error("can't open %s: %s", fname, pcap_strerror(errno)); 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, fname, pcap_strerror(errno)); cc = read(fd, cp, (u_int)buf.st_size); if (cc < 0) error("read %s: %s", fname, pcap_strerror(errno)); if (cc != buf.st_size) error("short read %s (%d != %d)", fname, (int) cc, (int)buf.st_size); close(fd); /* replace "# comment" with spaces */ for (i = 0; i < cc; i++) { if (cp[i] == '#') while (i < cc && cp[i] != '\n') cp[i++] = ' '; } cp[cc] = '\0'; return (cp); } #ifdef HAVE_PCAP_FINDALLDEVS static long parse_interface_number(const char *device) { const char *p; long devnum; char *end; /* * Search for a colon, terminating any scheme at the beginning * of the device. */ p = strchr(device, ':'); if (p != NULL) { /* * We found it. Is it followed by "//"? */ p++; /* skip the : */ if (strncmp(p, "//", 2) == 0) { /* * Yes. Search for the next /, at the end of the * authority part of the URL. */ p += 2; /* skip the // */ p = strchr(p, '/'); if (p != NULL) { /* * OK, past the / is the path. */ device = p + 1; } } } devnum = strtol(device, &end, 10); if (device != end && *end == '\0') { /* * It's all-numeric, but is it a valid number? */ if (devnum <= 0) { /* * No, it's not an ordinal. */ error("Invalid adapter index"); } return (devnum); } else { /* * It's not all-numeric; return -1, so our caller * knows that. */ return (-1); } } static char * find_interface_by_number(const char *url #ifndef HAVE_PCAP_FINDALLDEVS_EX _U_ #endif , long devnum) { pcap_if_t *dev, *devlist; long i; char ebuf[PCAP_ERRBUF_SIZE]; char *device; #ifdef HAVE_PCAP_FINDALLDEVS_EX const char *endp; char *host_url; #endif int status; #ifdef HAVE_PCAP_FINDALLDEVS_EX /* * Search for a colon, terminating any scheme at the beginning * of the URL. */ endp = strchr(url, ':'); if (endp != NULL) { /* * We found it. Is it followed by "//"? */ endp++; /* skip the : */ if (strncmp(endp, "//", 2) == 0) { /* * Yes. Search for the next /, at the end of the * authority part of the URL. */ endp += 2; /* skip the // */ endp = strchr(endp, '/'); } else endp = NULL; } if (endp != NULL) { /* * OK, everything from device to endp is a URL to hand * to pcap_findalldevs_ex(). */ endp++; /* Include the trailing / in the URL; pcap_findalldevs_ex() requires it */ host_url = malloc(endp - url + 1); if (host_url == NULL && (endp - url + 1) > 0) error("Invalid allocation for host"); memcpy(host_url, url, endp - url); host_url[endp - url] = '\0'; status = pcap_findalldevs_ex(host_url, NULL, &devlist, ebuf); free(host_url); } else #endif status = pcap_findalldevs(&devlist, ebuf); if (status < 0) error("%s", ebuf); /* * Look for the devnum-th entry in the list of devices (1-based). */ for (i = 0, dev = devlist; i < devnum-1 && dev != NULL; i++, dev = dev->next) ; if (dev == NULL) error("Invalid adapter index"); device = strdup(dev->name); pcap_freealldevs(devlist); return (device); } #endif #ifdef HAVE_PCAP_OPEN /* * Prefixes for rpcap URLs. */ static char rpcap_prefix[] = "rpcap://"; static char rpcap_ssl_prefix[] = "rpcaps://"; #endif 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 /* * Is this an rpcap URL? */ if (strncmp(device, rpcap_prefix, sizeof(rpcap_prefix) - 1) == 0 || strncmp(device, rpcap_ssl_prefix, sizeof(rpcap_ssl_prefix) - 1) == 0) { /* * Yes. Open it with pcap_open(). */ *ebuf = '\0'; pc = pcap_open(device, ndo->ndo_snaplen, pflag ? 0 : PCAP_OPENFLAG_PROMISCUOUS, timeout, NULL, ebuf); if (pc == NULL) { /* * If this failed with "No such device" or "The system * cannot find the device specified", 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 || strstr(ebuf, "The system cannot find the device specified") != NULL) return (NULL); error("%s", ebuf); } if (*ebuf) warning("%s", ebuf); return (pc); } #endif /* HAVE_PCAP_OPEN */ #ifdef HAVE_PCAP_CREATE pc = pcap_create(device, 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); } #ifdef HAVE_PCAP_SET_TSTAMP_TYPE if (Jflag) show_tstamp_types_and_exit(pc, device); #endif #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION status = pcap_set_tstamp_precision(pc, ndo->ndo_tstamp_precision); if (status != 0) error("%s: Can't set %ssecond time stamp precision: %s", device, tstamp_precision_to_string(ndo->ndo_tstamp_precision), pcap_statustostr(status)); #endif #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE if (immediate_mode) { status = pcap_set_immediate_mode(pc, 1); if (status != 0) error("%s: Can't set immediate mode: %s", device, pcap_statustostr(status)); } #endif /* * Is this an interface that supports monitor mode? */ if (pcap_can_set_rfmon(pc) == 1) supports_monitor_mode = 1; else supports_monitor_mode = 0; if (ndo->ndo_snaplen != 0) { /* * A snapshot length was explicitly specified; * use it. */ status = pcap_set_snaplen(pc, ndo->ndo_snaplen); if (status != 0) error("%s: Can't set snapshot length: %s", device, pcap_statustostr(status)); } status = pcap_set_promisc(pc, !pflag); if (status != 0) error("%s: Can't set promiscuous mode: %s", device, pcap_statustostr(status)); if (Iflag) { status = pcap_set_rfmon(pc, 1); if (status != 0) error("%s: Can't set monitor mode: %s", device, pcap_statustostr(status)); } status = pcap_set_timeout(pc, timeout); if (status != 0) error("%s: pcap_set_timeout failed: %s", device, pcap_statustostr(status)); if (Bflag != 0) { status = pcap_set_buffer_size(pc, Bflag); if (status != 0) error("%s: Can't set buffer size: %s", device, pcap_statustostr(status)); } #ifdef HAVE_PCAP_SET_TSTAMP_TYPE if (jflag != -1) { status = pcap_set_tstamp_type(pc, jflag); if (status < 0) error("%s: Can't set time stamp type: %s", device, pcap_statustostr(status)); else if (status > 0) warning("When trying to set timestamp type '%s' on %s: %s", pcap_tstamp_type_val_to_name(jflag), device, pcap_statustostr(status)); } #endif status = pcap_activate(pc); if (status < 0) { /* * pcap_activate() failed. */ cp = pcap_geterr(pc); if (status == PCAP_ERROR) error("%s", cp); else if (status == PCAP_ERROR_NO_SUCH_DEVICE) { /* * Return an error for our caller to handle. */ snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)", device, pcap_statustostr(status), cp); } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0') error("%s: %s\n(%s)", device, pcap_statustostr(status), cp); #ifdef __FreeBSD__ else if (status == PCAP_ERROR_RFMON_NOTSUP && strncmp(device, "wlan", 4) == 0) { char parent[8], newdev[8]; char sysctl[32]; size_t s = sizeof(parent); snprintf(sysctl, sizeof(sysctl), "net.wlan.%d.%%parent", atoi(device + 4)); sysctlbyname(sysctl, parent, &s, NULL, 0); strlcpy(newdev, device, sizeof(newdev)); /* Suggest a new wlan device. */ /* FIXME: incrementing the index this way is not going to work well * when the index is 9 or greater but the only consequence in this * 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" "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 else error("%s: %s", device, pcap_statustostr(status)); pcap_close(pc); return (NULL); } else if (status > 0) { /* * pcap_activate() succeeded, but it's warning us * of a problem it had. */ cp = pcap_geterr(pc); if (status == PCAP_WARNING) warning("%s", cp); else if (status == PCAP_WARNING_PROMISC_NOTSUP && *cp != '\0') warning("%s: %s\n(%s)", device, pcap_statustostr(status), cp); else warning("%s: %s", device, pcap_statustostr(status)); } #ifdef HAVE_PCAP_SETDIRECTION if (Qflag != -1) { status = pcap_setdirection(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); } int main(int argc, char **argv) { int cnt, op, i; bpf_u_int32 localnet = 0, netmask = 0; char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName; char *endp; pcap_handler callback; int dlt; const char *dlt_name; struct bpf_program fcode; #ifndef _WIN32 void (*oldhandler)(int); #endif struct dump_info dumpinfo; u_char *pcap_userdata; char ebuf[PCAP_ERRBUF_SIZE]; char VFileLine[PATH_MAX + 1]; const char *username = NULL; #ifndef _WIN32 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 cap_rights_t rights; int cansandbox; #endif /* HAVE_CAPSICUM */ int Oflag = 1; /* run filter code optimizer */ int yflag_dlt = -1; const char *yflag_dlt_name = NULL; int print = 0; long Cflagmult; netdissect_options Ndo; netdissect_options *ndo = &Ndo; /* * Initialize the netdissect code. */ if (nd_init(ebuf, sizeof(ebuf)) == -1) error("%s", ebuf); memset(ndo, 0, sizeof(*ndo)); ndo_set_function_pointers(ndo); cnt = -1; device = NULL; infile = NULL; RFileName = NULL; VFileName = NULL; VFile = NULL; WFileName = NULL; dlt = -1; if ((cp = strrchr(argv[0], PATH_SEPARATOR)) != NULL) ndo->program_name = program_name = cp + 1; else ndo->program_name = program_name = argv[0]; #if defined(HAVE_PCAP_WSOCKINIT) if (pcap_wsockinit() != 0) error("Attempting to initialize Winsock failed"); #elif defined(HAVE_WSOCKINIT) if (wsockinit() != 0) error("Attempting to initialize Winsock failed"); #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. */ if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0) error("%s", ebuf); while ( (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) switch (op) { case 'a': /* compatibility for old -a */ break; case 'A': ++ndo->ndo_Aflag; break; case 'b': ++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); break; #endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */ case 'c': cnt = atoi(optarg); if (cnt <= 0) error("invalid packet count %s", optarg); 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 || errno != 0 || Cflag <= 0) error("invalid file size %s", optarg); 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", 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", optarg); } } /* * Will multiplying it by multiplier overflow? */ #ifdef HAVE_PCAP_DUMP_FTELL64 if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / Cflagmult) #else if (Cflag > LONG_MAX / Cflagmult) #endif error("file size %s is too large", optarg); 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: remote_interfaces_source = optarg; break; #endif case 'L': Lflag++; break; case 'e': ++ndo->ndo_eflag; break; case 'E': #ifndef HAVE_LIBCRYPTO warning("crypto code not compiled in"); #endif ndo->ndo_espsecret = optarg; break; case 'f': ++ndo->ndo_fflag; break; case 'F': infile = optarg; break; case 'G': Gflag = atoi(optarg); if (Gflag < 0) error("invalid number of seconds %s", optarg); /* We will create one file initially. */ Gflag_count = 0; /* Grab the current time for rotation use. */ if ((Gflag_time = time(NULL)) == (time_t)-1) { error("%s: can't get current time: %s", __func__, pcap_strerror(errno)); } break; case 'h': print_usage(stdout); exit_tcpdump(S_SUCCESS); break; case 'H': ++ndo->ndo_Hflag; break; case 'i': device = optarg; break; #ifdef HAVE_PCAP_CREATE case 'I': ++Iflag; break; #endif /* HAVE_PCAP_CREATE */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE case 'j': jflag = pcap_tstamp_type_name_to_val(optarg); if (jflag < 0) error("invalid time stamp type %s", optarg); break; case 'J': Jflag++; break; #endif case 'l': #ifdef _WIN32 /* * _IOLBF is the same as _IOFBF in Microsoft's C * libraries; the only alternative they offer * is _IONBF. * * XXX - this should really be checking for MSVC++, * not _WIN32, if, for example, MinGW has its own * C library that is more UNIX-compatible. */ setvbuf(stdout, NULL, _IONBF, 0); #else /* _WIN32 */ #ifdef HAVE_SETLINEBUF setlinebuf(stdout); #else setvbuf(stdout, NULL, _IOLBF, 0); #endif #endif /* _WIN32 */ lflag = 1; break; case 'K': ++ndo->ndo_Kflag; break; case 'm': if (nd_have_smi_support()) { if (nd_load_smi_module(optarg, ebuf, sizeof(ebuf)) == -1) error("%s", ebuf); } else { (void)fprintf(stderr, "%s: ignoring option `-m %s' ", program_name, optarg); (void)fprintf(stderr, "(no libsmi support)\n"); } break; case 'M': /* TCP-MD5 shared secret */ #ifndef HAVE_LIBCRYPTO warning("crypto code not compiled in"); #endif ndo->ndo_sigsecret = optarg; break; case 'n': ++ndo->ndo_nflag; break; case 'N': ++ndo->ndo_Nflag; break; case 'O': Oflag = 0; break; case 'p': ++pflag; break; case 'q': ++ndo->ndo_qflag; ++ndo->ndo_suppress_default_print; break; #ifdef HAVE_PCAP_SETDIRECTION case 'Q': if (ascii_strcasecmp(optarg, "in") == 0) Qflag = PCAP_D_IN; else if (ascii_strcasecmp(optarg, "out") == 0) Qflag = PCAP_D_OUT; else if (ascii_strcasecmp(optarg, "inout") == 0) Qflag = PCAP_D_INOUT; else error("unknown capture direction `%s'", optarg); break; #endif /* HAVE_PCAP_SETDIRECTION */ case 'r': 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); break; case 'S': ++ndo->ndo_Sflag; break; case 't': ++ndo->ndo_tflag; break; case 'T': if (ascii_strcasecmp(optarg, "vat") == 0) ndo->ndo_packettype = PT_VAT; else if (ascii_strcasecmp(optarg, "wb") == 0) ndo->ndo_packettype = PT_WB; else if (ascii_strcasecmp(optarg, "rpc") == 0) ndo->ndo_packettype = PT_RPC; else if (ascii_strcasecmp(optarg, "rtp") == 0) ndo->ndo_packettype = PT_RTP; else if (ascii_strcasecmp(optarg, "rtcp") == 0) ndo->ndo_packettype = PT_RTCP; else if (ascii_strcasecmp(optarg, "snmp") == 0) ndo->ndo_packettype = PT_SNMP; else if (ascii_strcasecmp(optarg, "cnfp") == 0) ndo->ndo_packettype = PT_CNFP; else if (ascii_strcasecmp(optarg, "tftp") == 0) ndo->ndo_packettype = PT_TFTP; else if (ascii_strcasecmp(optarg, "aodv") == 0) ndo->ndo_packettype = PT_AODV; else if (ascii_strcasecmp(optarg, "carp") == 0) ndo->ndo_packettype = PT_CARP; else if (ascii_strcasecmp(optarg, "radius") == 0) ndo->ndo_packettype = PT_RADIUS; else if (ascii_strcasecmp(optarg, "zmtp1") == 0) ndo->ndo_packettype = PT_ZMTP1; else if (ascii_strcasecmp(optarg, "vxlan") == 0) ndo->ndo_packettype = PT_VXLAN; else if (ascii_strcasecmp(optarg, "pgm") == 0) ndo->ndo_packettype = PT_PGM; else if (ascii_strcasecmp(optarg, "pgm_zmtp1") == 0) ndo->ndo_packettype = PT_PGM_ZMTP1; else if (ascii_strcasecmp(optarg, "lmp") == 0) ndo->ndo_packettype = PT_LMP; else if (ascii_strcasecmp(optarg, "resp") == 0) ndo->ndo_packettype = PT_RESP; else 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 if (ascii_strcasecmp(optarg, "quic") == 0) ndo->ndo_packettype = PT_QUIC; else 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; break; case 'V': VFileName = optarg; break; case 'w': WFileName = optarg; break; case 'W': Wflag = atoi(optarg); if (Wflag <= 0) error("invalid number of output files %s", optarg); WflagChars = getWflagChars(Wflag); break; case 'x': ++ndo->ndo_xflag; ++ndo->ndo_suppress_default_print; break; case 'X': ++ndo->ndo_Xflag; ++ndo->ndo_suppress_default_print; break; case 'y': yflag_dlt_name = optarg; yflag_dlt = pcap_datalink_name_to_val(yflag_dlt_name); if (yflag_dlt < 0) error("invalid data link type %s", yflag_dlt_name); break; #ifdef HAVE_PCAP_SET_PARSER_DEBUG case 'Y': { /* Undocumented flag */ pcap_set_parser_debug(1); } break; #endif case 'z': zflag = optarg; break; case 'Z': username = optarg; break; case '#': ndo->ndo_packet_number = 1; break; case OPTION_VERSION: print_version(stdout); exit_tcpdump(S_SUCCESS); break; #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION case OPTION_TSTAMP_PRECISION: ndo->ndo_tstamp_precision = tstamp_precision_from_string(optarg); if (ndo->ndo_tstamp_precision < 0) error("unsupported time stamp precision"); break; #endif #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE case OPTION_IMMEDIATE_MODE: immediate_mode = 1; break; #endif case OPTION_PRINT: 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; break; case OPTION_TSTAMP_NANO: ndo->ndo_tstamp_precision = PCAP_TSTAMP_PRECISION_NANO; break; #endif case OPTION_FP_TYPE: /* * Print out the type of floating-point arithmetic * we're doing; it's probably IEEE, unless somebody * tries to run this on a VAX, but the precision * may differ (e.g., it might be 32-bit, 64-bit, * or 80-bit). */ float_type_check(0x4e93312d); return 0; case OPTION_COUNT: count_mode = 1; break; default: print_usage(stderr); exit_tcpdump(S_ERR_HOST_PROGRAM); /* NOTREACHED */ } #ifdef HAVE_PCAP_FINDALLDEVS 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 */ case 1: /* No time stamp */ case 2: /* Unix timeval style */ case 3: /* Microseconds/nanoseconds since previous packet */ case 4: /* Date + Default */ case 5: /* Microseconds/nanoseconds since first packet */ break; default: /* Not supported */ error("only -t, -tt, -ttt, -tttt and -ttttt are supported"); break; } if (ndo->ndo_fflag != 0 && (VFileName != NULL || RFileName != NULL)) error("-f can not be used with -V or -r"); if (VFileName != NULL && RFileName != NULL) error("-V and -r are mutually exclusive."); /* * If we're printing dissected packets to the standard output, * and either the standard output is a terminal or we're doing * "line" buffering, set the capture timeout to .1 second rather * than 1 second, as the user's probably expecting to see packets * pop up immediately shortly after they arrive. * * XXX - would there be some value appropriate for all cases, * based on, say, the buffer size and packet input rate? */ if ((WFileName == NULL || print) && (isatty(1) || lflag)) timeout = 100; #ifdef WITH_CHROOT /* if run as root, prepare for chrooting */ if (getuid() == 0 || geteuid() == 0) { /* future extensibility for cmd-line arguments */ if (!chroot_dir) chroot_dir = WITH_CHROOT; } #endif #ifdef WITH_USER /* if run as root, prepare for dropping root privileges */ if (getuid() == 0 || geteuid() == 0) { /* Run with '-Z root' to restore old behaviour */ if (!username) username = WITH_USER; } #endif if (RFileName != NULL || VFileName != NULL) { /* * If RFileName is non-null, it's the pathname of a * savefile to read. If VFileName is non-null, it's * the pathname of a file containing a list of pathnames * (one per line) of savefiles to read. * * In either case, we're reading a savefile, not doing * a live capture. */ #ifndef _WIN32 /* * We don't need network access, so relinquish any set-UID * or set-GID privileges we have (if any). * * We do *not* want set-UID privileges when opening a * trace file, as that might let the user read other * people's trace files (especially if we're set-UID * root). */ if (setgid(getgid()) != 0 || setuid(getuid()) != 0 ) fprintf(stderr, "Warning: setgid/setuid failed !\n"); #endif /* _WIN32 */ if (VFileName != NULL) { if (VFileName[0] == '-' && VFileName[1] == '\0') VFile = stdin; else VFile = fopen(VFileName, "r"); if (VFile == NULL) error("Unable to open file: %s\n", pcap_strerror(errno)); ret = get_next_file(VFile, VFileLine); if (!ret) error("Nothing in %s\n", VFileName); RFileName = VFileLine; } #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION pd = pcap_open_offline_with_tstamp_precision(RFileName, ndo->ndo_tstamp_precision, ebuf); #else pd = pcap_open_offline(RFileName, ebuf); #endif if (pd == NULL) error("%s", ebuf); #ifdef HAVE_CAPSICUM cap_rights_init(&rights, CAP_READ); if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 && errno != ENOSYS) { error("unable to limit pcap descriptor"); } #endif dlt = pcap_datalink(pd); dlt_name = pcap_datalink_val_to_name(dlt); 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)); #ifdef DLT_LINUX_SLL2 if (dlt == DLT_LINUX_SLL2) fprintf(stderr, "Warning: interface names might be incorrect\n"); #endif } else if (dflag && !device) { int dump_dlt = DLT_EN10MB; /* * We're dumping the compiled code without an explicit * device specification. (If a device is specified, we * definitely want to open it to use the DLT of that device.) * Either default to DLT_EN10MB with a warning, or use * the user-specified value if supplied. */ /* * 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; /* * If a DLT was specified with the -y flag, use that instead. */ if (yflag_dlt != -1) dump_dlt = yflag_dlt; else fprintf(stderr, "Warning: assuming Ethernet\n"); pd = pcap_open_dead(dump_dlt, ndo->ndo_snaplen); } else { /* * We're doing a live capture. */ if (device == NULL) { /* * No interface was specified. Pick one. */ #ifdef HAVE_PCAP_FINDALLDEVS /* * Find the list of interfaces, and pick * the first interface. */ if (pcap_findalldevs(&devlist, ebuf) == -1) error("%s", ebuf); if (devlist == NULL) 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 } /* * Try to open the interface with the specified name. */ pd = open_interface(device, ndo, ebuf); if (pd == NULL) { /* * That failed. If we can get a list of * interfaces, and the interface name * is purely numeric, try to use it as * a 1-based index in the list of * interfaces. */ #ifdef HAVE_PCAP_FINDALLDEVS devnum = parse_interface_number(device); if (devnum == -1) { /* * It's not a number; just report * the open error and fail. */ error("%s", ebuf); } /* * OK, it's a number; try to find the * interface with that index, and try * to open it. * * find_interface_by_number() exits if it * couldn't be found. */ device = find_interface_by_number(device, devnum); 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 */ } /* * Let user own process after capture device has * been opened. */ #ifndef _WIN32 if (setgid(getgid()) != 0 || setuid(getuid()) != 0) fprintf(stderr, "Warning: setgid/setuid failed !\n"); #endif /* _WIN32 */ #if !defined(HAVE_PCAP_CREATE) && defined(_WIN32) if(Bflag != 0) if(pcap_setbuff(pd, Bflag)==-1){ error("%s", pcap_geterr(pd)); } #endif /* !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); } i = pcap_snapshot(pd); if (ndo->ndo_snaplen < i) { if (ndo->ndo_snaplen != 0) warning("snaplen raised from %d to %d", ndo->ndo_snaplen, i); ndo->ndo_snaplen = i; } else if (ndo->ndo_snaplen > i) { warning("snaplen lowered from %d to %d", ndo->ndo_snaplen, i); ndo->ndo_snaplen = i; } if(ndo->ndo_fflag != 0) { if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { warning("foreign (-f) flag used but: %s", ebuf); } } } if (infile) cmdbuf = read_infile(infile); else cmdbuf = copy_argv(&argv[optind]); #ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG pcap_set_optimizer_debug(dflag); #endif if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); if (dflag) { bpf_dump(&fcode, dflag); pcap_close(pd); free(cmdbuf); pcap_freecode(&fcode); exit_tcpdump(S_SUCCESS); } #ifdef HAVE_CASPER if (!ndo->ndo_nflag) capdns = capdns_setup(); #endif /* HAVE_CASPER */ init_print(ndo, localnet, netmask); #ifndef _WIN32 (void)setsignal(SIGPIPE, cleanup); (void)setsignal(SIGTERM, cleanup); #endif /* _WIN32 */ (void)setsignal(SIGINT, cleanup); #if defined(HAVE_FORK) || defined(HAVE_VFORK) (void)setsignal(SIGCHLD, child_cleanup); #endif /* Cooperate with nohup(1) */ #ifndef _WIN32 if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL) (void)setsignal(SIGHUP, oldhandler); #endif /* _WIN32 */ #ifndef _WIN32 /* * If a user name was specified with "-Z", attempt to switch to * that user's UID. This would probably be used with sudo, * to allow tcpdump to be run in a special restricted * account (if you just want to allow users to open capture * devices, and can't just give users that permission, * you'd make tcpdump set-UID or set-GID). * * Tcpdump doesn't necessarily write only to one savefile; * the general only way to allow a -Z instance to write to * savefiles as the user under whose UID it's run, rather * than as the user specified with -Z, would thus be to switch * to the original user ID before opening a capture file and * then switch back to the -Z user ID after opening the savefile. * Switching to the -Z user ID only after opening the first * savefile doesn't handle the general case. */ if (getuid() == 0 || geteuid() == 0) { #ifdef HAVE_LIBCAP_NG /* Initialize capng */ capng_clear(CAPNG_SELECT_BOTH); if (username) { DIAG_OFF_ASSIGN_ENUM capng_updatev( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_SETUID, CAP_SETGID, -1); DIAG_ON_ASSIGN_ENUM } if (chroot_dir) { DIAG_OFF_ASSIGN_ENUM capng_update( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_SYS_CHROOT ); DIAG_ON_ASSIGN_ENUM } if (WFileName) { DIAG_OFF_ASSIGN_ENUM capng_update( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE ); DIAG_ON_ASSIGN_ENUM } capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ if (username || chroot_dir) droproot(username, chroot_dir); } #endif /* _WIN32 */ if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); #ifdef HAVE_CAPSICUM if (RFileName == NULL && VFileName == NULL && 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; #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); 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 */ if (cansandbox && cap_enter() < 0 && errno != ENOSYS) error("unable to enter the capability mode"); #endif /* HAVE_CAPSICUM */ do { status = pcap_loop(pd, cnt, callback, pcap_userdata); if (WFileName == NULL) { /* * We're printing packets. Flush the printed output, * so it doesn't get intermingled with error output. */ if (status == -2) { /* * We got interrupted, so perhaps we didn't * manage to finish a line we were printing. * Print an extra newline, just in case. */ putchar('\n'); } (void)fflush(stdout); } if (status == -2) { /* * We got interrupted. If we are reading multiple * files (via -V) set these so that we stop. */ VFileName = NULL; ret = NULL; } if (status == -1) { /* * Error. Report it. */ (void)fprintf(stderr, "%s: pcap_loop: %s\n", program_name, pcap_geterr(pd)); } if (RFileName == NULL) { /* * We're doing a live capture. Report the capture * statistics. */ info(1); } pcap_close(pd); 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); if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); } /* * Set the filter on the new file. */ if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); /* * Report the new file. */ dlt_name = pcap_datalink_val_to_name(dlt); 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); } /* * Catch a signal. */ static void (*setsignal (int sig, void (*func)(int)))(int) { #ifdef _WIN32 return (signal(sig, func)); #else struct sigaction old, new; memset(&new, 0, sizeof(new)); new.sa_handler = func; 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); return (old.sa_handler); #endif } /* make a clean exit on interrupts */ static void cleanup(int signo _U_) { #ifdef _WIN32 if (timer_handle != INVALID_HANDLE_VALUE) { DeleteTimerQueueTimer(NULL, timer_handle, NULL); CloseHandle(timer_handle); timer_handle = INVALID_HANDLE_VALUE; } #else /* _WIN32 */ struct itimerval timer; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 0; 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 * to do anything with standard I/O streams in a signal handler - * the ANSI C standard doesn't say it is). */ pcap_breakloop(pd); #else /* * We don't have "pcap_breakloop()"; this isn't safe, but * it's the best we can do. Print the summary if we're * not reading from a savefile - i.e., if we're doing a * live capture - and exit. */ if (pd != NULL && pcap_file(pd) == NULL) { /* * We got interrupted, so perhaps we didn't * manage to finish a line we were printing. * Print an extra newline, just in case. */ putchar('\n'); (void)fflush(stdout); info(1); } exit_tcpdump(S_SUCCESS); #endif } /* On windows, we do not use a fork, so we do not care less about waiting a child processes to die */ #if defined(HAVE_FORK) || defined(HAVE_VFORK) static void child_cleanup(int signo _U_) { while (waitpid(-1, NULL, WNOHANG) >= 0); } #endif /* HAVE_FORK && HAVE_VFORK */ static void info(int verbose) { struct pcap_stat stats; /* * Older versions of libpcap didn't set ps_ifdrop on some * platforms; initialize it to 0 to handle that. */ stats.ps_ifdrop = 0; if (pcap_stats(pd, &stats) < 0) { (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd)); infoprint = 0; return; } if (!verbose) fprintf(stderr, "%s: ", program_name); (void)fprintf(stderr, "%u packet%s captured", packets_captured, PLURAL_SUFFIX(packets_captured)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); (void)fprintf(stderr, "%u packet%s received by filter", stats.ps_recv, PLURAL_SUFFIX(stats.ps_recv)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); (void)fprintf(stderr, "%u packet%s dropped by kernel", stats.ps_drop, PLURAL_SUFFIX(stats.ps_drop)); if (stats.ps_ifdrop != 0) { if (!verbose) fputs(", ", stderr); else putc('\n', stderr); (void)fprintf(stderr, "%u packet%s dropped by interface\n", stats.ps_ifdrop, PLURAL_SUFFIX(stats.ps_ifdrop)); } else putc('\n', stderr); infoprint = 0; } #if defined(HAVE_FORK) || defined(HAVE_VFORK) #ifdef HAVE_FORK #define fork_subprocess() fork() #else #define fork_subprocess() vfork() #endif static void compress_savefile(const char *filename) { pid_t child; child = fork_subprocess(); if (child == -1) { fprintf(stderr, "compress_savefile: fork failed: %s\n", pcap_strerror(errno)); return; } if (child != 0) { /* Parent process. */ return; } /* * Child process. * Set to lowest priority so that this doesn't disturb the capture. */ #ifdef NZERO setpriority(PRIO_PROCESS, 0, NZERO - 1); #else setpriority(PRIO_PROCESS, 0, 19); #endif if (execlp(zflag, zflag, filename, (char *)NULL) == -1) fprintf(stderr, "compress_savefile: execlp(%s, %s) failed: %s\n", zflag, filename, pcap_strerror(errno)); #ifdef HAVE_FORK exit(S_ERR_HOST_PROGRAM); #else _exit(S_ERR_HOST_PROGRAM); #endif } #else /* HAVE_FORK && HAVE_VFORK */ static void compress_savefile(const char *filename) { fprintf(stderr, "compress_savefile failed. Functionality not implemented under your system\n"); } #endif /* HAVE_FORK && HAVE_VFORK */ static void dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { struct dump_info *dump_info; ++packets_captured; ++infodelay; dump_info = (struct dump_info *)user; /* * XXX - this won't force the file to rotate on the specified time * boundary, but it will rotate on the first packet received after the * specified Gflag number of seconds. Note: if a Gflag time boundary * and a Cflag size boundary coincide, the time rotation will occur * first thereby cancelling the Cflag boundary (since the file should * be 0). */ if (Gflag != 0) { /* Check if it is time to rotate */ time_t t; /* Get the current time */ if ((t = time(NULL)) == (time_t)-1) { error("%s: can't get current_time: %s", __func__, pcap_strerror(errno)); } /* If the time is greater than the specified window, rotate */ if (t - Gflag_time >= Gflag) { #ifdef HAVE_CAPSICUM FILE *fp; int fd; #endif /* Update the Gflag_time */ Gflag_time = t; /* Update Gflag_count */ Gflag_count++; /* * Close the current file and open a new one. */ pcap_dump_close(dump_info->pdd); /* * Compress the file we just closed, if the user asked for it */ if (zflag != NULL) compress_savefile(dump_info->CurrentFileName); /* * Check to see if we've exceeded the Wflag (when * not using Cflag). */ if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) { (void)fprintf(stderr, "Maximum file limit reached: %d\n", Wflag); info(1); exit_tcpdump(S_SUCCESS); /* NOTREACHED */ } if (dump_info->CurrentFileName != NULL) free(dump_info->CurrentFileName); /* Allocate space for max filename + \0. */ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1); if (dump_info->CurrentFileName == NULL) error("dump_packet_and_trunc: malloc"); /* * Gflag was set otherwise we wouldn't be here. Reset the count * so multiple files would end with 1,2,3 in the filename. * The counting is handled with the -C flow after this. */ Cflag_count = 0; /* * This is always the first file in the Cflag * rotation: e.g. 0 * We also don't need numbering if Cflag is not set. */ if (Cflag != 0) MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, WflagChars); else MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0); #ifdef HAVE_LIBCAP_NG capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ #ifdef HAVE_CAPSICUM fd = openat(dump_info->dirfd, dump_info->CurrentFileName, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { error("unable to open file %s", dump_info->CurrentFileName); } fp = fdopen(fd, "w"); if (fp == NULL) { error("unable to fdopen file %s", dump_info->CurrentFileName); } dump_info->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 } } /* * XXX - this won't prevent capture files from getting * larger than Cflag - the last packet written to the * file could put it over Cflag. */ if (Cflag != 0) { #ifdef HAVE_PCAP_DUMP_FTELL64 int64_t size = pcap_dump_ftell64(dump_info->pdd); #else /* * XXX - this only handles a Cflag value > 2^31-1 on * LP64 platforms; to handle ILP32 (32-bit UN*X and * Windows) or LLP64 (64-bit Windows) would require * a version of libpcap with pcap_dump_ftell64(). */ long size = pcap_dump_ftell(dump_info->pdd); #endif 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); Cflag_count++; if (Wflag > 0) { if (Cflag_count >= Wflag) Cflag_count = 0; } if (dump_info->CurrentFileName != NULL) free(dump_info->CurrentFileName); dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1); if (dump_info->CurrentFileName == NULL) error("%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 } } 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); --infodelay; if (infoprint) info(0); } static void dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { struct dump_info *dump_info; ++packets_captured; ++infodelay; dump_info = (struct dump_info *)user; 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); --infodelay; if (infoprint) info(0); } static void print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { ++packets_captured; ++infodelay; if (!count_mode) pretty_print_packet((netdissect_options *)user, h, sp, packets_captured); --infodelay; if (infoprint) info(0); } #ifdef SIGNAL_REQ_INFO static void requestinfo(int signo _U_) { if (infodelay) ++infoprint; else info(0); } #endif #ifdef SIGNAL_FLUSH_PCAP static void flushpcap(int signo _U_) { if (pdd != NULL) pcap_dump_flush(pdd); } #endif static void print_packets_captured (void) { static u_int prev_packets_captured, first = 1; if (infodelay == 0 && (first || packets_captured != prev_packets_captured)) { fprintf(stderr, "Got %u\r", packets_captured); first = 0; prev_packets_captured = packets_captured; } } /* * Called once each second in verbose mode while dumping to file */ #ifdef _WIN32 static void CALLBACK verbose_stats_dump(PVOID param _U_, BOOLEAN timer_fired _U_) { print_packets_captured(); } #else /* _WIN32 */ static void verbose_stats_dump(int sig _U_) { print_packets_captured(); } #endif /* _WIN32 */ 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)); #endif smi_version_string = nd_smi_version_string(); if (smi_version_string != NULL) (void)fprintf (f, "SMI-library: %s\n", smi_version_string); #if defined(__SANITIZE_ADDRESS__) (void)fprintf (f, "Compiled with AddressSanitizer/GCC.\n"); #elif defined(__has_feature) # if __has_feature(address_sanitizer) (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 */ } 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); (void)fprintf(f, "\t\t[ -C file_size ] [ -E algo:secret ] [ -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 #ifdef USE_LIBSMI (void)fprintf(f, "\t\t" m_FLAG_USAGE "\n"); #endif (void)fprintf(f, "\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[ -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 (void)fprintf(f, "\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n"); #endif (void)fprintf(f, "\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n"); }