X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/9c4af7213cc2543a1f5586d8f2c19f86aa0cbe72..fdd0467bcd46ea0d472111adedd21f43d6d4d15e:/tcpdump.c diff --git a/tcpdump.c b/tcpdump.c index 2ac7757f..65b7e769 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -122,7 +122,6 @@ The Regents of the University of California. All rights reserved.\n"; #include "interface.h" #include "addrtoname.h" #include "machdep.h" -#include "setsignal.h" #include "gmt2local.h" #include "pcap-missing.h" #include "ascii_strcasecmp.h" @@ -140,7 +139,11 @@ The Regents of the University of California. All rights reserved.\n"; #endif static int Bflag; /* buffer size */ +#ifdef HAVE_PCAP_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 */ static int Dflag; /* list available devices and exit */ /* @@ -185,26 +188,18 @@ cap_channel_t *capdns; #endif /* Forwards */ -static void error(const char *, ...) - __attribute__((noreturn)) -#ifdef __ATTRIBUTE___FORMAT_OK - __attribute__((format (printf, 1, 2))) -#endif /* __ATTRIBUTE___FORMAT_OK */ - ; -static void warning(const char *, ...) -#ifdef __ATTRIBUTE___FORMAT_OK - __attribute__((format (printf, 1, 2))) -#endif /* __ATTRIBUTE___FORMAT_OK */ - ; -static void exit_tcpdump(int) __attribute__((noreturn)); -static RETSIGTYPE cleanup(int); -static RETSIGTYPE child_cleanup(int); +static NORETURN void error(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2); +static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2); +static NORETURN void exit_tcpdump(int); +static void (*setsignal (int sig, void (*func)(int)))(int); +static void cleanup(int); +static void child_cleanup(int); static void print_version(void); static void print_usage(void); -static void show_tstamp_types_and_exit(pcap_t *, const char *device) __attribute__((noreturn)); -static void show_dlts_and_exit(pcap_t *, const char *device) __attribute__((noreturn)); +static NORETURN void show_tstamp_types_and_exit(pcap_t *, const char *device); +static NORETURN void show_dlts_and_exit(pcap_t *, const char *device); #ifdef HAVE_PCAP_FINDALLDEVS -static void show_devices_and_exit (void) __attribute__((noreturn)); +static NORETURN void show_devices_and_exit(void); #endif static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *); @@ -213,7 +208,7 @@ static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void droproot(const char *, const char *); #ifdef SIGNAL_REQ_INFO -RETSIGTYPE requestinfo(int); +void requestinfo(int); #endif #if defined(USE_WIN32_MM_TIMER) @@ -253,6 +248,7 @@ struct dump_info { char *CurrentFileName; pcap_t *pd; pcap_dumper_t *p; + netdissect_options *ndo; #ifdef HAVE_CAPSICUM int dirfd; #endif @@ -562,6 +558,7 @@ show_devices_and_exit (void) #define OPTION_VERSION 128 #define OPTION_TSTAMP_PRECISION 129 #define OPTION_IMMEDIATE_MODE 130 +#define OPTION_PRINT 131 static const struct option longopts[] = { #if defined(HAVE_PCAP_CREATE) || defined(_WIN32) @@ -601,6 +598,7 @@ static const struct option longopts[] = { #endif { "relinquish-privileges", required_argument, NULL, 'Z' }, { "number", no_argument, NULL, '#' }, + { "print", no_argument, NULL, OPTION_PRINT }, { "version", no_argument, NULL, OPTION_VERSION }, { NULL, 0, NULL, 0 } }; @@ -1045,10 +1043,16 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) 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)); + 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", @@ -1149,6 +1153,12 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) #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 = 262144; pc = pcap_open_live(device, ndo->ndo_snaplen, !pflag, 1000, ebuf); if (pc == NULL) { /* @@ -1172,15 +1182,16 @@ int main(int argc, char **argv) { register int cnt, op, i; - bpf_u_int32 localnet =0 , netmask = 0; + bpf_u_int32 localnet = 0, netmask = 0; int timezone_offset = 0; register 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 - RETSIGTYPE (*oldhandler)(int); + void (*oldhandler)(int); #endif struct dump_info dumpinfo; u_char *pcap_userdata; @@ -1203,6 +1214,7 @@ main(int argc, char **argv) int Oflag = 1; /* run filter code optimizer */ int yflag_dlt = -1; const char *yflag_dlt_name = NULL; + int print = 0; netdissect_options Ndo; netdissect_options *ndo = &Ndo; @@ -1215,7 +1227,6 @@ main(int argc, char **argv) memset(ndo, 0, sizeof(*ndo)); ndo_set_function_pointers(ndo); - ndo->ndo_snaplen = DEFAULT_SNAPLEN; cnt = -1; device = NULL; @@ -1276,9 +1287,25 @@ main(int argc, char **argv) break; case 'C': - Cflag = atoi(optarg) * 1000000; - if (Cflag <= 0) + errno = 0; +#ifdef HAVE_PCAP_FTELL64 + Cflag = strtoint64_t(optarg, &endp, 10); +#else + Cflag = strtol(optarg, &endp, 10); +#endif + if (endp == optarg || *endp != '\0' || errno != 0 + || Cflag <= 0) error("invalid file size %s", optarg); + /* + * Will multiplying it by 1000000 overflow? + */ +#ifdef HAVE_PCAP_FTELL64 + if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffffU) / 1000000) +#else + if (Cflag > LONG_MAX / 1000000) +#endif + error("file size %s is too large", optarg); + Cflag *= 1000000; break; case 'd': @@ -1445,8 +1472,6 @@ main(int argc, char **argv) if (optarg == end || *end != '\0' || ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); - else if (ndo->ndo_snaplen == 0) - ndo->ndo_snaplen = MAXIMUM_SNAPLEN; break; case 'S': @@ -1582,6 +1607,10 @@ main(int argc, char **argv) break; #endif + case OPTION_PRINT: + print = 1; + break; + default: print_usage(); exit_tcpdump(1); @@ -1620,11 +1649,14 @@ main(int argc, char **argv) #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE /* * If we're printing dissected packets to the standard output - * rather than saving raw packets to a file, and the standard - * output is a terminal, use immediate mode, as the user's - * probably expecting to see packets pop up immediately. + * and the standard output is a terminal, use immediate mode, + * as the user's probably expecting to see packets pop up + * immediately. + * + * XXX - set the timeout to a lower value, instead? If so, + * what value would be appropriate? */ - if (WFileName == NULL && isatty(1)) + if ((WFileName == NULL || print) && isatty(1)) immediate_mode = 1; #endif @@ -1724,20 +1756,21 @@ main(int argc, char **argv) * 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); - } + 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); -#endif if (device == NULL) error("%s", ebuf); +#endif } /* @@ -1784,7 +1817,8 @@ main(int argc, char **argv) } /* - * Let user own process after socket has been opened. + * Let user own process after capture device has + * been opened. */ #ifndef _WIN32 if (setgid(getgid()) != 0 || setuid(getuid()) != 0) @@ -1819,7 +1853,11 @@ main(int argc, char **argv) } i = pcap_snapshot(pd); if (ndo->ndo_snaplen < i) { - warning("snaplen raised from %d to %d", 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) { @@ -2009,8 +2047,18 @@ main(int argc, char **argv) pcap_userdata = (u_char *)&dumpinfo; } else { callback = dump_packet; - pcap_userdata = (u_char *)p; + dumpinfo.WFileName = WFileName; + dumpinfo.pd = pd; + dumpinfo.p = p; + pcap_userdata = (u_char *)&dumpinfo; } + if (print) { + dlt = pcap_datalink(pd); + ndo->ndo_if_printer = get_if_printer(ndo, dlt); + dumpinfo.ndo = ndo; + } else + dumpinfo.ndo = NULL; + #ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush(p); @@ -2031,11 +2079,11 @@ main(int argc, char **argv) (void)setsignal(SIGNAL_REQ_INFO, requestinfo); #endif - if (ndo->ndo_vflag > 0 && WFileName) { + if (ndo->ndo_vflag > 0 && WFileName && !print) { /* - * When capturing to a file, "-v" means tcpdump should, - * every 10 seconds, "v"erbosely report the number of - * packets captured. + * 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. */ #ifdef USE_WIN32_MM_TIMER /* call verbose_stats_dump() each 1000 +/-100msec */ @@ -2203,8 +2251,29 @@ main(int argc, char **argv) exit_tcpdump(status == -1 ? 1 : 0); } +/* + * 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) + 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 RETSIGTYPE +static void cleanup(int signo _U_) { #ifdef USE_WIN32_MM_TIMER @@ -2249,7 +2318,7 @@ cleanup(int signo _U_) waiting a child processes to die */ #if defined(HAVE_FORK) || defined(HAVE_VFORK) -static RETSIGTYPE +static void child_cleanup(int signo _U_) { wait(NULL); @@ -2480,7 +2549,17 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s * file could put it over Cflag. */ if (Cflag != 0) { +#ifdef HAVE_PCAP_FTELL64 + int64_t size = pcap_dump_ftell64(dump_info->p); +#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->p); +#endif if (size == -1) error("ftell fails on output file"); @@ -2551,6 +2630,9 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s pcap_dump_flush(dump_info->p); #endif + if (dump_info->ndo != NULL) + pretty_print_packet(dump_info->ndo, h, sp, packets_captured); + --infodelay; if (infoprint) info(0); @@ -2559,16 +2641,23 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s static void dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { + struct dump_info *dump_info; + ++packets_captured; ++infodelay; - pcap_dump(user, h, sp); + dump_info = (struct dump_info *)user; + + pcap_dump((u_char *)dump_info->p, h, sp); #ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) - pcap_dump_flush((pcap_dumper_t *)user); + pcap_dump_flush(dump_info->p); #endif + if (dump_info->ndo != NULL) + pretty_print_packet(dump_info->ndo, h, sp, packets_captured); + --infodelay; if (infoprint) info(0); @@ -2616,7 +2705,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) #endif #ifdef SIGNAL_REQ_INFO -RETSIGTYPE requestinfo(int signo _U_) +void requestinfo(int signo _U_) { if (infodelay) ++infoprint; @@ -2625,6 +2714,18 @@ RETSIGTYPE requestinfo(int signo _U_) } #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 */ @@ -2632,14 +2733,12 @@ RETSIGTYPE requestinfo(int signo _U_) void CALLBACK verbose_stats_dump (UINT timer_id _U_, UINT msg _U_, DWORD_PTR arg _U_, DWORD_PTR dw1 _U_, DWORD_PTR dw2 _U_) { - if (infodelay == 0) - fprintf(stderr, "Got %u\r", packets_captured); + print_packets_captured(); } #elif defined(HAVE_ALARM) static void verbose_stats_dump(int sig _U_) { - if (infodelay == 0) - fprintf(stderr, "Got %u\r", packets_captured); + print_packets_captured(); alarm(1); } #endif @@ -2682,6 +2781,9 @@ print_version(void) smi_version_string = nd_smi_version_string(); if (smi_version_string != NULL) (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string); +#ifdef HAVE_DNET_HTOA + (void)fprintf(stderr, "libdnet unknown version\n"); +#endif #if defined(__SANITIZE_ADDRESS__) (void)fprintf (stderr, "Compiled with AddressSanitizer/GCC.\n"); @@ -2703,14 +2805,17 @@ print_usage(void) "\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n"); (void)fprintf(stderr, "\t\t[ -i interface ]" j_FLAG_USAGE " [ -M secret ] [ --number ]\n"); + (void)fprintf(stderr, +"\t\t[ --print ]"); #ifdef HAVE_PCAP_SETDIRECTION (void)fprintf(stderr, -"\t\t[ -Q in|out|inout ]\n"); +" [ -Q in|out|inout ]"); #endif (void)fprintf(stderr, -"\t\t[ -r file ] [ -s snaplen ] "); +" [ -r file ] [ -s snaplen ]\n"); #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION - (void)fprintf(stderr, "[ --time-stamp-precision precision ]\n"); + (void)fprintf(stderr, +"\t\t[ --time-stamp-precision precision ]\n"); (void)fprintf(stderr, "\t\t"); #endif