X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/e8ad3e5637b98fd4143505f0deb871f03b758003..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/savefile.c diff --git a/savefile.c b/savefile.c index 41897a29..db8a3aa0 100644 --- a/savefile.c +++ b/savefile.c @@ -28,49 +28,54 @@ * dependent values so we can print the dump file on any architecture. */ -#ifndef lint -static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.183 2008-12-23 20:13:29 guy Exp $ (LBL)"; -#endif - #ifdef HAVE_CONFIG_H -#include "config.h" +#include #endif -#ifdef WIN32 -#include -#else /* WIN32 */ -#if HAVE_INTTYPES_H -#include -#elif HAVE_STDINT_H -#include -#endif -#ifdef HAVE_SYS_BITYPES_H -#include -#endif -#include -#endif /* WIN32 */ +#include +#ifdef _WIN32 +#include +#include +#endif /* _WIN32 */ #include #include #include #include #include +#include /* for INT_MAX */ #include "pcap-int.h" -#include "pcap/usb.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif #include "sf-pcap.h" -#include "sf-pcap-ng.h" +#include "sf-pcapng.h" +#include "pcap-common.h" +#include "charconv.h" + +#ifdef _WIN32 +/* + * This isn't exported on Windows, because it would only work if both + * WinPcap/Npcap and the code using it were to use the Universal CRT; otherwise, + * a FILE structure in WinPcap/Npcap and a FILE structure in the code using it + * could be different if they're using different versions of the C runtime. + * + * Instead, pcap/pcap.h defines it as a macro that wraps the hopen version, + * with the wrapper calling _fileno() and _get_osfhandle() themselves, + * so that it convert the appropriate CRT version's FILE structure to + * a HANDLE (which is OS-defined, not CRT-defined, and is part of the Win32 + * and Win64 ABIs). + */ +static pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *, u_int, char *); +#endif /* * Setting O_BINARY on DOS/Windows is a bit tricky */ -#if defined(WIN32) +#if defined(_WIN32) #define SET_BINMODE(f) _setmode(_fileno(f), _O_BINARY) #elif defined(MSDOS) #if defined(__HIGHC__) @@ -81,7 +86,7 @@ static const char rcsid[] _U_ = #endif static int -sf_getnonblock(pcap_t *p, char *errbuf) +sf_getnonblock(pcap_t *p _U_) { /* * This is a savefile, not a live capture file, so never say @@ -91,7 +96,7 @@ sf_getnonblock(pcap_t *p, char *errbuf) } static int -sf_setnonblock(pcap_t *p, int nonblock, char *errbuf) +sf_setnonblock(pcap_t *p, int nonblock _U_) { /* * This is a savefile, not a live capture file, so reject @@ -107,16 +112,34 @@ sf_setnonblock(pcap_t *p, int nonblock, char *errbuf) } static int -sf_stats(pcap_t *p, struct pcap_stat *ps) +sf_cant_set_rfmon(pcap_t *p _U_) +{ + /* + * This is a savefile, not a device on which you can capture, + * so never say it supports being put into monitor mode. + */ + return (0); +} + +static int +sf_stats(pcap_t *p, struct pcap_stat *ps _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from savefiles"); return (-1); } -#ifdef WIN32 +#ifdef _WIN32 +static struct pcap_stat * +sf_stats_ex(pcap_t *p, int *size _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Statistics aren't available from savefiles"); + return (NULL); +} + static int -sf_setbuff(pcap_t *p, int dim) +sf_setbuff(pcap_t *p, int dim _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The kernel buffer size cannot be set while reading from a file"); @@ -124,7 +147,7 @@ sf_setbuff(pcap_t *p, int dim) } static int -sf_setmode(pcap_t *p, int mode) +sf_setmode(pcap_t *p, int mode _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "impossible to set mode while reading from a file"); @@ -132,18 +155,82 @@ sf_setmode(pcap_t *p, int mode) } static int -sf_setmintocopy(pcap_t *p, int size) +sf_setmintocopy(pcap_t *p, int size _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The mintocopy parameter cannot be set while reading from a file"); return (-1); } + +static HANDLE +sf_getevent(pcap_t *pcap) +{ + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "The read event cannot be retrieved while reading from a file"); + return (INVALID_HANDLE_VALUE); +} + +static int +sf_oid_get_request(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, + size_t *lenp _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID get request cannot be performed on a file"); + return (PCAP_ERROR); +} + +static int +sf_oid_set_request(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, + size_t *lenp _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID set request cannot be performed on a file"); + return (PCAP_ERROR); +} + +static u_int +sf_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) +{ + pcap_strlcpy(p->errbuf, "Sending packets isn't supported on savefiles", + PCAP_ERRBUF_SIZE); + return (0); +} + +static int +sf_setuserbuffer(pcap_t *p, int size _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The user buffer cannot be set when reading from a file"); + return (-1); +} + +static int +sf_live_dump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed when reading from a file"); + return (-1); +} + +static int +sf_live_dump_ended(pcap_t *p, int sync _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); + return (-1); +} + +static PAirpcapHandle +sf_get_airpcap_handle(pcap_t *pcap _U_) +{ + return (NULL); +} #endif static int -sf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +sf_inject(pcap_t *p, const void *buf _U_, int size _U_) { - strlcpy(p->errbuf, "Sending packets isn't supported on savefiles", + pcap_strlcpy(p->errbuf, "Sending packets isn't supported on savefiles", PCAP_ERRBUF_SIZE); return (-1); } @@ -153,7 +240,7 @@ sf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) * single device? IN, OUT or both? */ static int -sf_setdirection(pcap_t *p, pcap_direction_t d) +sf_setdirection(pcap_t *p, pcap_direction_t d _U_) { snprintf(p->errbuf, sizeof(p->errbuf), "Setting direction is not supported on savefiles"); @@ -170,17 +257,115 @@ sf_cleanup(pcap_t *p) pcap_freecode(&p->fcode); } +#ifdef _WIN32 +/* + * Wrapper for fopen() and _wfopen(). + * + * If we're in UTF-8 mode, map the pathname from UTF-8 to UTF-16LE and + * call _wfopen(). + * + * If we're not, just use fopen(); that'll treat it as being in the + * local code page. + */ +FILE * +charset_fopen(const char *path, const char *mode) +{ + wchar_t *utf16_path; +#define MAX_MODE_LEN 16 + wchar_t utf16_mode[MAX_MODE_LEN+1]; + int i; + char c; + FILE *fp; + int save_errno; + + if (pcap_utf_8_mode) { + /* + * Map from UTF-8 to UTF-16LE. + * Fail if there are invalid characters in the input + * string, rather than converting them to REPLACEMENT + * CHARACTER; the latter is appropriate for strings + * to be displayed to the user, but for file names + * you just want the attempt to open the file to fail. + */ + utf16_path = cp_to_utf_16le(CP_UTF8, path, + MB_ERR_INVALID_CHARS); + if (utf16_path == NULL) { + /* + * Error. Assume errno has been set. + * + * XXX - what about Windows errors? + */ + return (NULL); + } + + /* + * Now convert the mode to UTF-16LE as well. + * We assume the mode is ASCII, and that + * it's short, so that's easy. + */ + for (i = 0; (c = *mode) != '\0'; i++, mode++) { + if (c > 0x7F) { + /* Not an ASCII character; fail with EINVAL. */ + free(utf16_path); + errno = EINVAL; + return (NULL); + } + if (i >= MAX_MODE_LEN) { + /* The mode string is longer than we allow. */ + free(utf16_path); + errno = EINVAL; + return (NULL); + } + utf16_mode[i] = c; + } + utf16_mode[i] = '\0'; + + /* + * OK, we have UTF-16LE strings; hand them to + * _wfopen(). + */ + fp = _wfopen(utf16_path, utf16_mode); + + /* + * Make sure freeing the UTF-16LE string doesn't + * overwrite the error code we got from _wfopen(). + */ + save_errno = errno; + free(utf16_path); + errno = save_errno; + + return (fp); + } else { + /* + * This takes strings in the local code page as an + * argument. + */ + return (fopen(path, mode)); + } +} +#endif + pcap_t * pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, - char *errbuf) + char *errbuf) { FILE *fp; pcap_t *p; + if (fname == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "A null pointer was supplied as the file name"); + return (NULL); + } if (fname[0] == '-' && fname[1] == '\0') { fp = stdin; -#if defined(WIN32) || defined(MSDOS) + if (fp == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The standard input is not open"); + return (NULL); + } +#if defined(_WIN32) || defined(MSDOS) /* * We're reading from the standard input, so put it in binary * mode, as savefiles are binary files. @@ -189,14 +374,20 @@ pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, #endif } else { -#if !defined(WIN32) && !defined(MSDOS) - fp = fopen(fname, "r"); -#else - fp = fopen(fname, "rb"); -#endif + /* + * Use charset_fopen(); on Windows, it tests whether we're + * in "local code page" or "UTF-8" mode, and treats the + * pathname appropriately, and on other platforms, it just + * wraps fopen(). + * + * "b" is supported as of C90, so *all* UN*Xes should + * support it, even though it does nothing. For MS-DOS, + * we again need it. + */ + fp = charset_fopen(fname, "rb"); if (fp == NULL) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname, - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "%s", fname); return (NULL); } } @@ -215,7 +406,7 @@ pcap_open_offline(const char *fname, char *errbuf) PCAP_TSTAMP_PRECISION_MICRO, errbuf)); } -#ifdef WIN32 +#ifdef _WIN32 pcap_t* pcap_hopen_offline_with_tstamp_precision(intptr_t osfd, u_int precision, char *errbuf) { @@ -223,16 +414,19 @@ pcap_t* pcap_hopen_offline_with_tstamp_precision(intptr_t osfd, u_int precision, FILE *file; fd = _open_osfhandle(osfd, _O_RDONLY); - if ( fd < 0 ) + if ( fd < 0 ) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "_open_osfhandle"); return NULL; } file = _fdopen(fd, "rb"); - if ( file == NULL ) + if ( file == NULL ) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "_fdopen"); + _close(fd); return NULL; } @@ -247,14 +441,41 @@ pcap_t* pcap_hopen_offline(intptr_t osfd, char *errbuf) } #endif -static pcap_t *(*check_headers[])(bpf_u_int32, FILE *, u_int, char *, int *) = { +/* + * Given a link-layer header type and snapshot length, return a + * snapshot length to use when reading the file; it's guaranteed + * to be > 0 and <= INT_MAX. + * + * XXX - the only reason why we limit it to <= INT_MAX is so that + * it fits in p->snapshot, and the only reason that p->snapshot is + * signed is that pcap_snapshot() returns an int, not an unsigned int. + */ +bpf_u_int32 +pcap_adjust_snapshot(bpf_u_int32 linktype, bpf_u_int32 snaplen) +{ + if (snaplen == 0 || snaplen > INT_MAX) { + /* + * Bogus snapshot length; use the maximum for this + * link-layer type as a fallback. + * + * XXX - we don't clamp snapshot lengths that are + * <= INT_MAX but > max_snaplen_for_dlt(linktype), + * so a capture file could cause us to allocate + * a Really Big Buffer. + */ + snaplen = max_snaplen_for_dlt(linktype); + } + return snaplen; +} + +static pcap_t *(*check_headers[])(const uint8_t *, FILE *, u_int, char *, int *) = { pcap_check_header, pcap_ng_check_header }; #define N_FILE_TYPES (sizeof check_headers / sizeof check_headers[0]) -#ifdef WIN32 +#ifdef _WIN32 static #endif pcap_t * @@ -262,29 +483,40 @@ pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision, char *errbuf) { register pcap_t *p; - bpf_u_int32 magic; + uint8_t magic[4]; size_t amt_read; u_int i; int err; + /* + * Fail if we were passed a NULL fp. + * + * That shouldn't happen if we're opening with a path name, but + * it could happen if buggy code is opening with a FILE * and + * didn't bother to make sure the FILE * isn't null. + */ + if (fp == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Null FILE * pointer provided to savefile open routine"); + return (NULL); + } + /* * Read the first 4 bytes of the file; the network analyzer dump - * file formats we support (pcap and pcap-ng), and several other + * file formats we support (pcap and pcapng), and several other * formats we might support in the future (such as snoop, DOS and * Windows Sniffer, and Microsoft Network Monitor) all have magic * numbers that are unique in their first 4 bytes. */ - amt_read = fread((char *)&magic, 1, sizeof(magic), fp); + amt_read = fread(&magic, 1, sizeof(magic), fp); if (amt_read != sizeof(magic)) { if (ferror(fp)) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "error reading dump file: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "error reading dump file"); } else { snprintf(errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %lu file header bytes, only got %lu", - (unsigned long)sizeof(magic), - (unsigned long)amt_read); + "truncated dump file; tried to read %zu file header bytes, only got %zu", + sizeof(magic), amt_read); } return (NULL); } @@ -318,7 +550,7 @@ found: /* Padding only needed for live capture fcode */ p->fddipad = 0; -#if !defined(WIN32) && !defined(MSDOS) +#if !defined(_WIN32) && !defined(MSDOS) /* * You can do "select()" and "poll()" on plain files on most * platforms, and should be able to do so on pipes. @@ -329,6 +561,7 @@ found: p->selectable_fd = fileno(fp); #endif + p->can_set_rfmon_op = sf_cant_set_rfmon; p->read_op = pcap_offline_read; p->inject_op = sf_inject; p->setfilter_op = install_bpf_program; @@ -337,25 +570,55 @@ found: p->getnonblock_op = sf_getnonblock; p->setnonblock_op = sf_setnonblock; p->stats_op = sf_stats; -#ifdef WIN32 +#ifdef _WIN32 + p->stats_ex_op = sf_stats_ex; p->setbuff_op = sf_setbuff; p->setmode_op = sf_setmode; p->setmintocopy_op = sf_setmintocopy; + p->getevent_op = sf_getevent; + p->oid_get_request_op = sf_oid_get_request; + p->oid_set_request_op = sf_oid_set_request; + p->sendqueue_transmit_op = sf_sendqueue_transmit; + p->setuserbuffer_op = sf_setuserbuffer; + p->live_dump_op = sf_live_dump; + p->live_dump_ended_op = sf_live_dump_ended; + p->get_airpcap_handle_op = sf_get_airpcap_handle; #endif + + /* + * For offline captures, the standard one-shot callback can + * be used for pcap_next()/pcap_next_ex(). + */ + p->oneshot_callback = pcap_oneshot; + + /* + * Default breakloop operation. + */ + p->breakloop_op = pcap_breakloop_common; + + /* + * Savefiles never require special BPF code generation. + */ + p->bpf_codegen_flags = 0; + p->activated = 1; return (p); } -#ifdef WIN32 -static -#endif +/* + * This isn't needed on Windows; we #define pcap_fopen_offline() as + * a wrapper around pcap_hopen_offline(), and we don't call it from + * inside this file, so it's unused. + */ +#ifndef _WIN32 pcap_t * pcap_fopen_offline(FILE *fp, char *errbuf) { return (pcap_fopen_offline_with_tstamp_precision(fp, PCAP_TSTAMP_PRECISION_MICRO, errbuf)); } +#endif /* * Read packets from a capture file, and call the callback for each @@ -366,12 +629,27 @@ int pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { struct bpf_insn *fcode; - int status = 0; int n = 0; u_char *data; - while (status == 0) { + /* + * This can conceivably process more than INT_MAX packets, + * which would overflow the packet count, causing it either + * to look like a negative number, and thus cause us to + * return a value that looks like an error, or overflow + * back into positive territory, and thus cause us to + * return a too-low count. + * + * Therefore, if the packet count is unlimited, we clip + * it at INT_MAX; this routine is not expected to + * process packets indefinitely, so that's not an issue. + */ + if (PACKET_COUNT_IS_UNLIMITED(cnt)) + cnt = INT_MAX; + + for (;;) { struct pcap_pkthdr h; + int status; /* * Has "pcap_breakloop()" been called? @@ -391,16 +669,28 @@ pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } status = p->next_packet_op(p, &h, &data); - if (status) { - if (status == 1) - return (0); + if (status < 0) { + /* + * Error. Pass it back to the caller. + */ return (status); } + if (status == 0) { + /* + * EOF. Nothing more to process; + */ + break; + } + /* + * OK, we've read a packet; run it through the filter + * and, if it passes, process it. + */ if ((fcode = p->fcode.bf_insns) == NULL || - bpf_filter(fcode, data, h.len, h.caplen)) { + pcap_filter(fcode, data, h.len, h.caplen)) { (*callback)(user, &h, data); - if (++n >= cnt && cnt > 0) + n++; /* count the packet */ + if (n >= cnt) break; } }