X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/79fab92e6747a1419f91b01d779376d857a78d46..refs/heads/tcpdump-4.9:/tcpdump.c diff --git a/tcpdump.c b/tcpdump.c index 50b9c3ae..8f27ba2a 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -56,8 +56,10 @@ The Regents of the University of California. All rights reserved.\n"; #include -#ifdef USE_LIBSMI -#include +#include + +#ifdef HAVE_FCNTL_H +#include #endif #ifdef HAVE_LIBCRYPTO @@ -77,12 +79,12 @@ The Regents of the University of California. All rights reserved.\n"; #include #include #include -#include #include #endif /* HAVE_CAPSICUM */ #include #include #include +#include #include #include #include @@ -106,6 +108,7 @@ The Regents of the University of California. All rights reserved.\n"; #endif /* HAVE_CAP_NG_H */ #endif /* HAVE_LIBCAP_NG */ +#include "netdissect-stdinc.h" #include "netdissect.h" #include "interface.h" #include "addrtoname.h" @@ -127,10 +130,23 @@ The Regents of the University of California. All rights reserved.\n"; #define SIGNAL_REQ_INFO SIGUSR1 #endif -static int Cflag; /* rotate dump files after this many bytes */ +static int Bflag; /* buffer size */ +static long Cflag; /* rotate dump files after this many bytes */ static int Cflag_count; /* Keep track of which file number we're writing */ static int Dflag; /* list available devices and exit */ -static int dflag; /* print filter code */ +/* + * 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. + */ +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. */ @@ -139,13 +155,16 @@ static int Iflag; /* rfmon (monitor) mode */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE static int Jflag; /* list available time stamp types */ #endif +static int jflag = -1; /* packet time stamp source */ +static int pflag; /* don't go promiscuous */ #ifdef HAVE_PCAP_SETDIRECTION -int Qflag = -1; /* restrict captured packet by send/receive direction */ +static int Qflag = -1; /* restrict captured packet by send/receive direction */ #endif static int Uflag; /* "unbuffered" output of dump files */ 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 immediate_mode; static int infodelay; static int infoprint; @@ -153,12 +172,18 @@ static int infoprint; char *program_name; /* Forwards */ +static void error(FORMAT_STRING(const char *), ...) NORETURN PRINTFLIKE(1, 2); +static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2); +static void exit_tcpdump(int) NORETURN; static RETSIGTYPE cleanup(int); static RETSIGTYPE child_cleanup(int); static void print_version(void); static void print_usage(void); -static void show_tstamp_types_and_exit(const char *device) __attribute__((noreturn)); -static void show_dlts_and_exit(const char *device) __attribute__((noreturn)); +static void show_tstamp_types_and_exit(pcap_t *, const char *device) NORETURN; +static void show_dlts_and_exit(pcap_t *, const char *device) NORETURN; +#ifdef HAVE_PCAP_FINDALLDEVS +static void show_devices_and_exit (void) NORETURN; +#endif 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 *); @@ -180,6 +205,7 @@ RETSIGTYPE requestinfo(int); static void info(int); static u_int packets_captured; +#ifdef HAVE_PCAP_FINDALLDEVS static const struct tok status_flags[] = { #ifdef PCAP_IF_UP { PCAP_IF_UP, "Up" }, @@ -190,6 +216,7 @@ static const struct tok status_flags[] = { { PCAP_IF_LOOPBACK, "Loopback" }, { 0, NULL } }; +#endif static pcap_t *pd; @@ -209,23 +236,117 @@ struct dump_info { #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 + +/* VARARGS */ +static void +error(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(1); + /* NOTREACHED */ +} + +/* VARARGS */ +static void +warning(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); + } +} + +static void +exit_tcpdump(int status) +{ + nd_cleanup(); + exit(status); +} + #ifdef HAVE_PCAP_SET_TSTAMP_TYPE static void -show_tstamp_types_and_exit(const char *device) +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(pd, &tstamp_types); + n_tstamp_types = pcap_list_tstamp_types(pc, &tstamp_types); if (n_tstamp_types < 0) - error("%s", pcap_geterr(pd)); + error("%s", pcap_geterr(pc)); if (n_tstamp_types == 0) { fprintf(stderr, "Time stamp type cannot be set for %s\n", device); - exit(0); + exit_tcpdump(0); } fprintf(stderr, "Time stamp types for %s (use option -j to set):\n", device); @@ -239,20 +360,20 @@ show_tstamp_types_and_exit(const char *device) } } pcap_free_tstamp_types(tstamp_types); - exit(0); + exit_tcpdump(0); } #endif static void -show_dlts_and_exit(const char *device) +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(pd, &dlts); + n_dlts = pcap_list_datalinks(pc, &dlts); if (n_dlts < 0) - error("%s", pcap_geterr(pd)); + error("%s", pcap_geterr(pc)); else if (n_dlts == 0 || !dlts) error("No data link types."); @@ -292,31 +413,29 @@ show_dlts_and_exit(const char *device) #ifdef HAVE_PCAP_FREE_DATALINKS pcap_free_datalinks(dlts); #endif - exit(0); + exit_tcpdump(0); } #ifdef HAVE_PCAP_FINDALLDEVS static void show_devices_and_exit (void) { - pcap_if_t *devpointer; + pcap_if_t *dev, *devlist; char ebuf[PCAP_ERRBUF_SIZE]; int i; - if (pcap_findalldevs(&devpointer, ebuf) < 0) + if (pcap_findalldevs(&devlist, ebuf) < 0) error("%s", ebuf); - else { - for (i = 0; devpointer != NULL; i++) { - printf("%d.%s", i+1, devpointer->name); - if (devpointer->description != NULL) - printf(" (%s)", devpointer->description); - if (devpointer->flags != 0) - printf(" [%s]", bittok2str(status_flags, "none", devpointer->flags)); - printf("\n"); - devpointer = devpointer->next; - } + 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"); } - exit(0); + pcap_freealldevs(devlist); + exit_tcpdump(0); } #endif /* HAVE_PCAP_FINDALLDEVS */ @@ -331,7 +450,7 @@ show_devices_and_exit (void) * OS X tcpdump uses -g to force non--v output for IP to be on one * line, making it more "g"repable; * - * OS X tcpdump uses -k tospecify that packet comments in pcap-ng files + * OS X tcpdump uses -k to specify that packet comments in pcap-ng files * should be printed; * * OpenBSD tcpdump uses -o to indicate that OS fingerprinting should be done @@ -455,7 +574,7 @@ static const struct option longopts[] = { #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE }, #endif -#if defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG) +#ifdef HAVE_PCAP_SET_PARSER_DEBUG { "debug-filter-parser", no_argument, NULL, 'Y' }, #endif { "relinquish-privileges", required_argument, NULL, 'Z' }, @@ -474,7 +593,7 @@ droproot(const char *username, const char *chroot_dir) if (chroot_dir && !username) { fprintf(stderr, "%s: Chroot without dropping root is insecure\n", program_name); - exit(1); + exit_tcpdump(1); } pw = getpwnam(username); @@ -483,17 +602,16 @@ droproot(const char *username, const char *chroot_dir) if (chroot(chroot_dir) != 0 || chdir ("/") != 0) { fprintf(stderr, "%s: Couldn't chroot/chdir to '%.64s': %s\n", program_name, chroot_dir, pcap_strerror(errno)); - exit(1); + exit_tcpdump(1); } } #ifdef HAVE_LIBCAP_NG { int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG); - if (ret < 0) { - fprintf(stderr, "error : ret %d\n", ret); - } else { + 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 || @@ -503,7 +621,7 @@ droproot(const char *username, const char *chroot_dir) (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid, pcap_strerror(errno)); - exit(1); + exit_tcpdump(1); } else { fprintf(stderr, "dropped privs to %s\n", username); @@ -513,15 +631,16 @@ droproot(const char *username, const char *chroot_dir) else { fprintf(stderr, "%s: Couldn't find user '%.32s'\n", program_name, username); - exit(1); + exit_tcpdump(1); } #ifdef HAVE_LIBCAP_NG - /* We don't need CAP_SETUID and CAP_SETGID any more. */ + /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT any more. */ capng_updatev( CAPNG_DROP, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SETUID, CAP_SETGID, + CAP_SYS_CHROOT, -1); capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ @@ -581,13 +700,15 @@ static char * get_next_file(FILE *VFile, char *ptr) { char *ret; + size_t len; ret = fgets(ptr, PATH_MAX, VFile); if (!ret) return NULL; - if (ptr[strlen(ptr) - 1] == '\n') - ptr[strlen(ptr) - 1] = '\0'; + len = strlen (ptr); + if (len > 0 && ptr[len - 1] == '\n') + ptr[len - 1] = '\0'; return ret; } @@ -692,6 +813,299 @@ set_dumper_capsicum_rights(pcap_dumper_t *p) } #endif +/* + * Copy arg vector into a new buffer, concatenating arguments with spaces. + */ +static char * +copy_argv(register char **argv) +{ + register char **p; + register u_int 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("copy_argv: malloc"); + + 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) +{ + register int i, fd, cc; + register 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) +{ + long devnum; + char *end; + + 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(long devnum) +{ + pcap_if_t *dev, *devlist; + long i; + char ebuf[PCAP_ERRBUF_SIZE]; + char *device; + + if (pcap_findalldevs(&devlist, ebuf) < 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 + +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_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; + 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, 1000); + 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); + pcap_close(pc); + return (NULL); + } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0') + error("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + error("%s: %s", device, + pcap_statustostr(status)); + } 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'; + pc = pcap_open_live(device, ndo->ndo_snaplen, !pflag, 1000, 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) { @@ -701,7 +1115,6 @@ main(int argc, char **argv) register char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName; pcap_handler callback; int dlt; - int new_dlt; const char *dlt_name; struct bpf_program fcode; #ifndef _WIN32 @@ -716,8 +1129,8 @@ main(int argc, char **argv) char *ret = NULL; char *end; #ifdef HAVE_PCAP_FINDALLDEVS - pcap_if_t *p, *devpointer; - int devnum; + pcap_if_t *devlist; + long devnum; #endif int status; FILE *VFile; @@ -725,16 +1138,18 @@ main(int argc, char **argv) cap_rights_t rights; int cansandbox; #endif /* HAVE_CAPSICUM */ - int Bflag = 0; /* buffer size */ - int jflag = -1; /* packet time stamp source */ int Oflag = 1; /* run filter code optimizer */ - int pflag = 0; /* don't go promiscuous */ int yflag_dlt = -1; const char *yflag_dlt_name = NULL; netdissect_options Ndo; netdissect_options *ndo = &Ndo; - int immediate_mode = 0; + + /* + * Initialize the netdissect code. + */ + if (nd_init(ebuf, sizeof ebuf) == -1) + error("%s", ebuf); memset(ndo, 0, sizeof(*ndo)); ndo_set_function_pointers(ndo); @@ -768,10 +1183,6 @@ main(int argc, char **argv) if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0) error("%s", ebuf); -#ifdef USE_LIBSMI - smiInit("tcpdump"); -#endif - while ( (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) switch (op) { @@ -804,7 +1215,7 @@ main(int argc, char **argv) case 'C': Cflag = atoi(optarg) * 1000000; - if (Cflag < 0) + if (Cflag <= 0) error("invalid file size %s", optarg); break; @@ -856,7 +1267,7 @@ main(int argc, char **argv) case 'h': print_usage(); - exit(0); + exit_tcpdump(0); break; case 'H': @@ -864,42 +1275,6 @@ main(int argc, char **argv) break; case 'i': - if (optarg[0] == '0' && optarg[1] == 0) - error("Invalid adapter index"); - -#ifdef HAVE_PCAP_FINDALLDEVS - /* - * If the argument is a number, treat it as - * an index into the list of adapters, as - * printed by "tcpdump -D". - * - * This should be OK on UNIX systems, as interfaces - * shouldn't have names that begin with digits. - * It can be useful on Windows, where more than - * one interface can have the same name. - */ - devnum = strtol(optarg, &end, 10); - if (optarg != end && *end == '\0') { - if (devnum < 0) - error("Invalid adapter index"); - - if (pcap_findalldevs(&devpointer, ebuf) < 0) - error("%s", ebuf); - /* - * Look for the devnum-th entry in the - * list of devices (1-based). - */ - for (i = 0, p = devpointer; - i < devnum-1 && p != NULL; - i++, p = p->next) - ; - if (p == NULL) - error("Invalid adapter index"); - device = strdup(p->name); - pcap_freealldevs(devpointer); - break; - } -#endif /* HAVE_PCAP_FINDALLDEVS */ device = optarg; break; @@ -947,16 +1322,14 @@ main(int argc, char **argv) break; case 'm': -#ifdef USE_LIBSMI - if (smiLoadModule(optarg) == 0) { - error("could not load MIB module %s", optarg); + 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"); } - ndo->ndo_mflag = 1; -#else - (void)fprintf(stderr, "%s: ignoring option `-m %s' ", - program_name, optarg); - (void)fprintf(stderr, "(no libsmi support)\n"); -#endif break; case 'M': @@ -1055,6 +1428,8 @@ main(int argc, char **argv) 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 error("unknown packet type `%s'", optarg); break; @@ -1083,7 +1458,7 @@ main(int argc, char **argv) case 'W': Wflag = atoi(optarg); - if (Wflag < 0) + if (Wflag <= 0) error("invalid number of output files %s", optarg); WflagChars = getWflagChars(Wflag); break; @@ -1106,30 +1481,20 @@ main(int argc, char **argv) error("invalid data link type %s", yflag_dlt_name); break; -#if defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG) +#ifdef HAVE_PCAP_SET_PARSER_DEBUG case 'Y': { /* Undocumented flag */ -#ifdef HAVE_PCAP_DEBUG - extern int pcap_debug; - pcap_debug = 1; -#else - extern int yydebug; - yydebug = 1; -#endif + pcap_set_parser_debug(1); } break; #endif case 'z': - zflag = strdup(optarg); - if (zflag == NULL) - error("Unable to allocate memory for -z argument"); + zflag = optarg; break; case 'Z': - username = strdup(optarg); - if (username == NULL) - error("Unable to allocate memory for -Z argument"); + username = optarg; break; case '#': @@ -1138,7 +1503,7 @@ main(int argc, char **argv) case OPTION_VERSION: print_version(); - exit(0); + exit_tcpdump(0); break; #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION @@ -1157,7 +1522,7 @@ main(int argc, char **argv) default: print_usage(); - exit(1); + exit_tcpdump(1); /* NOTREACHED */ } @@ -1249,7 +1614,7 @@ main(int argc, char **argv) VFile = fopen(VFileName, "r"); if (VFile == NULL) - error("Unable to open file: %s\n", strerror(errno)); + error("Unable to open file: %s\n", pcap_strerror(errno)); ret = get_next_file(VFile, VFileLine); if (!ret) @@ -1289,148 +1654,73 @@ main(int argc, char **argv) * We're doing a live capture. */ if (device == NULL) { + /* + * No interface was specified. Pick one. + */ #ifdef HAVE_PCAP_FINDALLDEVS - if (pcap_findalldevs(&devpointer, ebuf) >= 0 && - devpointer != NULL) { - device = strdup(devpointer->name); - pcap_freealldevs(devpointer); + /* + * Find the list of interfaces, and pick + * the first interface. + */ + if (pcap_findalldevs(&devlist, ebuf) >= 0 && + devlist != NULL) { + device = strdup(devlist->name); + pcap_freealldevs(devlist); } #else /* HAVE_PCAP_FINDALLDEVS */ + /* + * Use whatever interface pcap_lookupdev() + * chooses. + */ device = pcap_lookupdev(ebuf); #endif if (device == NULL) error("%s", ebuf); } -#ifdef _WIN32 - /* - * Print a message to the standard error on Windows. - * XXX - why do it here, with a different message? - */ - if(strlen(device) == 1) /* we assume that an ASCII string is always longer than 1 char */ - { /* a Unicode string has a \0 as second byte (so strlen() is 1) */ - fprintf(stderr, "%s: listening on %ws\n", program_name, device); - } - else - { - fprintf(stderr, "%s: listening on %s\n", program_name, device); - } - fflush(stderr); -#endif /* _WIN32 */ -#ifdef HAVE_PCAP_CREATE - pd = pcap_create(device, ebuf); - if (pd == NULL) - error("%s", ebuf); -#ifdef HAVE_PCAP_SET_TSTAMP_TYPE - if (Jflag) - show_tstamp_types_and_exit(device); -#endif -#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION - status = pcap_set_tstamp_precision(pd, 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(pd, 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? + * Try to open the interface with the specified name. */ - if (pcap_can_set_rfmon(pd) == 1) - supports_monitor_mode = 1; - else - supports_monitor_mode = 0; - status = pcap_set_snaplen(pd, ndo->ndo_snaplen); - if (status != 0) - error("%s: Can't set snapshot length: %s", - device, pcap_statustostr(status)); - status = pcap_set_promisc(pd, !pflag); - if (status != 0) - error("%s: Can't set promiscuous mode: %s", - device, pcap_statustostr(status)); - if (Iflag) { - status = pcap_set_rfmon(pd, 1); - if (status != 0) - error("%s: Can't set monitor mode: %s", - device, pcap_statustostr(status)); - } - status = pcap_set_timeout(pd, 1000); - if (status != 0) - error("%s: pcap_set_timeout failed: %s", - device, pcap_statustostr(status)); - if (Bflag != 0) { - status = pcap_set_buffer_size(pd, 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(pd, jflag); - if (status < 0) - error("%s: Can't set time stamp type: %s", - device, pcap_statustostr(status)); - } -#endif - status = pcap_activate(pd); - if (status < 0) { + pd = open_interface(device, ndo, ebuf); + if (pd == NULL) { /* - * pcap_activate() failed. + * 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. */ - cp = pcap_geterr(pd); - if (status == PCAP_ERROR) - error("%s", cp); - else if ((status == PCAP_ERROR_NO_SUCH_DEVICE || - status == PCAP_ERROR_PERM_DENIED) && - *cp != '\0') - error("%s: %s\n(%s)", device, - pcap_statustostr(status), cp); - else - error("%s: %s", device, - pcap_statustostr(status)); - } else if (status > 0) { +#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); + } + /* - * pcap_activate() succeeded, but it's warning us - * of a problem it had. + * 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(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. */ - cp = pcap_geterr(pd); - 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(pd, Qflag); - if (status != 0) - error("%s: pcap_setdirection() failed: %s", - device, pcap_geterr(pd)); - } -#endif /* HAVE_PCAP_SETDIRECTION */ -#else - *ebuf = '\0'; - pd = pcap_open_live(device, ndo->ndo_snaplen, !pflag, 1000, - ebuf); - if (pd == NULL) error("%s", ebuf); - else if (*ebuf) - warning("%s", ebuf); -#endif /* HAVE_PCAP_CREATE */ +#endif /* HAVE_PCAP_FINDALLDEVS */ + } + /* * Let user own process after socket has been opened. */ @@ -1445,7 +1735,7 @@ main(int argc, char **argv) } #endif /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */ if (Lflag) - show_dlts_and_exit(device); + show_dlts_and_exit(pd, device); if (yflag_dlt >= 0) { #ifdef HAVE_PCAP_SET_DATALINK if (pcap_set_datalink(pd, yflag_dlt) < 0) @@ -1482,13 +1772,17 @@ main(int argc, char **argv) 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); - exit(0); + pcap_freecode(&fcode); + exit_tcpdump(0); } init_print(ndo, localnet, netmask, timezone_offset); @@ -1537,6 +1831,13 @@ main(int argc, char **argv) CAP_SETGID, -1); } + if (chroot_dir) { + capng_update( + CAPNG_ADD, + CAPNG_PERMITTED | CAPNG_EFFECTIVE, + CAP_SYS_CHROOT + ); + } if (WFileName) { capng_update( @@ -1557,9 +1858,14 @@ main(int argc, char **argv) error("%s", pcap_geterr(pd)); #ifdef HAVE_CAPSICUM if (RFileName == NULL && VFileName == NULL) { - static const unsigned long cmds[] = { BIOCGSTATS }; + static const unsigned long cmds[] = { BIOCGSTATS, BIOCROTZBUF }; - cap_rights_init(&rights, CAP_IOCTL, CAP_READ); + /* + * 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"); @@ -1673,7 +1979,6 @@ main(int argc, char **argv) #endif } -#ifndef _WIN32 if (RFileName == NULL) { /* * Live capture (if -V was specified, we set RFileName @@ -1698,7 +2003,6 @@ main(int argc, char **argv) } (void)fflush(stderr); } -#endif /* _WIN32 */ #ifdef HAVE_CAPSICUM cansandbox = (ndo->ndo_nflag && VFileName == NULL && zflag == NULL); @@ -1749,6 +2053,8 @@ main(int argc, char **argv) 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) @@ -1761,30 +2067,67 @@ main(int argc, char **argv) } #endif new_dlt = pcap_datalink(pd); - if (WFileName && new_dlt != dlt) - error("%s: new dlt does not match original", RFileName); - ndo->ndo_if_printer = get_if_printer(ndo, new_dlt); - dlt_name = pcap_datalink_val_to_name(new_dlt); + 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(ndo, dlt); + 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); if (dlt_name == NULL) { fprintf(stderr, "reading from file %s, link-type %u\n", - RFileName, new_dlt); + RFileName, dlt); } else { fprintf(stderr, "reading from file %s, link-type %s (%s)\n", - RFileName, dlt_name, - pcap_datalink_val_to_description(new_dlt)); + RFileName, dlt_name, + pcap_datalink_val_to_description(dlt)); } - if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) - error("%s", pcap_geterr(pd)); - if (pcap_setfilter(pd, &fcode) < 0) - error("%s", pcap_geterr(pd)); } } } while (ret != NULL); free(cmdbuf); - exit(status == -1 ? 1 : 0); + pcap_freecode(&fcode); + exit_tcpdump(status == -1 ? 1 : 0); } /* make a clean exit on interrupts */ @@ -1824,7 +2167,7 @@ cleanup(int signo _U_) (void)fflush(stdout); info(1); } - exit(0); + exit_tcpdump(0); #endif } @@ -1886,17 +2229,31 @@ info(register int verbose) } #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) { -# ifdef HAVE_FORK - if (fork()) -# else - if (vfork()) -# endif + 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; + } + /* - * Set to lowest priority so that this doesn't disturb the capture + * Child process. + * Set to lowest priority so that this doesn't disturb the capture. */ #ifdef NZERO setpriority(PRIO_PROCESS, 0, NZERO - 1); @@ -1905,15 +2262,15 @@ compress_savefile(const char *filename) #endif if (execlp(zflag, zflag, filename, (char *)NULL) == -1) fprintf(stderr, - "compress_savefile:execlp(%s, %s): %s\n", + "compress_savefile: execlp(%s, %s) failed: %s\n", zflag, filename, - strerror(errno)); -# ifdef HAVE_FORK + pcap_strerror(errno)); +#ifdef HAVE_FORK exit(1); -# else +#else _exit(1); -# endif +#endif } #else /* HAVE_FORK && HAVE_VFORK */ static void @@ -1983,7 +2340,8 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) { (void)fprintf(stderr, "Maximum file limit reached: %d\n", Wflag); - exit(0); + info(1); + exit_tcpdump(0); /* NOTREACHED */ } if (dump_info->CurrentFileName != NULL) @@ -2225,6 +2583,7 @@ print_version(void) static char pcap_version[] = "unknown"; #endif /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */ #endif /* HAVE_PCAP_LIB_VERSION */ + const char *smi_version_string; #ifdef HAVE_PCAP_LIB_VERSION #ifdef _WIN32 @@ -2247,9 +2606,17 @@ print_version(void) (void)fprintf (stderr, "%s\n", SSLeay_version(SSLEAY_VERSION)); #endif -#ifdef USE_LIBSMI - (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string); -#endif + smi_version_string = nd_smi_version_string(); + if (smi_version_string != NULL) + (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string); + +#if defined(__SANITIZE_ADDRESS__) + (void)fprintf (stderr, "Compiled with AddressSanitizer/GCC.\n"); +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) + (void)fprintf (stderr, "Compiled with AddressSanitizer/CLang.\n"); +# endif +#endif /* __SANITIZE_ADDRESS__ or __has_feature */ } USES_APPLE_RST @@ -2279,7 +2646,7 @@ print_usage(void) #endif (void)fprintf(stderr, "[ -T type ] [ --version ] [ -V file ]\n"); (void)fprintf(stderr, -"\t\t[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z command ]\n"); +"\t\t[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]\n"); (void)fprintf(stderr, "\t\t[ -Z user ] [ expression ]\n"); }