X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/113dc82c085f9a1101b614af832699467734ee1a..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/savefile.c?ds=inline diff --git a/savefile.c b/savefile.c index 152c9177..db8a3aa0 100644 --- a/savefile.c +++ b/savefile.c @@ -43,6 +43,7 @@ #include #include #include +#include /* for INT_MAX */ #include "pcap-int.h" @@ -52,22 +53,23 @@ #include "sf-pcap.h" #include "sf-pcapng.h" +#include "pcap-common.h" +#include "charconv.h" #ifdef _WIN32 /* - * These aren't exported on Windows, because they would only work if both - * WinPcap and the code using it were to use the Universal CRT; otherwise, - * a FILE structure in WinPcap and a FILE structure in the code using it + * 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 them as macros that wrap the hopen versions, - * with the wrappers calling _fileno() and _get_osfhandle() themselves, - * so that they convert the appropriate CRT version's FILE structure to + * 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 *); -static pcap_t *pcap_fopen_offline(FILE *, char *); #endif /* @@ -104,48 +106,58 @@ sf_setnonblock(pcap_t *p, int nonblock _U_) * as it would have to handle reading partial packets and * keeping the state of the read.) */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Savefiles cannot be put into non-blocking mode"); return (-1); } +static int +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_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from savefiles"); return (-1); } #ifdef _WIN32 static struct pcap_stat * -sf_stats_ex(pcap_t *p, int *size) +sf_stats_ex(pcap_t *p, int *size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + 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_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The kernel buffer size cannot be set while reading from a file"); return (-1); } static int -sf_setmode(pcap_t *p, int mode) +sf_setmode(pcap_t *p, int mode _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "impossible to set mode while reading from a file"); return (-1); } static int -sf_setmintocopy(pcap_t *p, int size) +sf_setmintocopy(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The mintocopy parameter cannot be set while reading from a file"); return (-1); } @@ -153,7 +165,7 @@ sf_setmintocopy(pcap_t *p, int size) static HANDLE sf_getevent(pcap_t *pcap) { - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), "The read event cannot be retrieved while reading from a file"); return (INVALID_HANDLE_VALUE); } @@ -162,7 +174,7 @@ static int sf_oid_get_request(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID get request cannot be performed on a file"); return (PCAP_ERROR); } @@ -171,13 +183,13 @@ static int sf_oid_set_request(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + 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, int sync) +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); @@ -185,31 +197,31 @@ sf_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue, int sync) } static int -sf_setuserbuffer(pcap_t *p, int size) +sf_setuserbuffer(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + 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, int maxsize, int maxpacks) +sf_live_dump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + 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) +sf_live_dump_ended(pcap_t *p, int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + 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) +sf_get_airpcap_handle(pcap_t *pcap _U_) { return (NULL); } @@ -230,7 +242,7 @@ sf_inject(pcap_t *p, const void *buf _U_, int size _U_) static int sf_setdirection(pcap_t *p, pcap_direction_t d _U_) { - pcap_snprintf(p->errbuf, sizeof(p->errbuf), + snprintf(p->errbuf, sizeof(p->errbuf), "Setting direction is not supported on savefiles"); return (-1); } @@ -245,6 +257,94 @@ 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) @@ -253,13 +353,18 @@ pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, pcap_t *p; if (fname == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + 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 (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 @@ -270,12 +375,16 @@ pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, } else { /* + * 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. It's - * required on Windows, as the file is a binary file - * and must be read in binary mode. + * support it, even though it does nothing. For MS-DOS, + * we again need it. */ - fp = fopen(fname, "rb"); + fp = charset_fopen(fname, "rb"); if (fp == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "%s", fname); @@ -332,7 +441,34 @@ 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 }; @@ -347,11 +483,24 @@ 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 pcapng), and several other @@ -359,14 +508,14 @@ pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision, * 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)) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %" PRIsize " file header bytes, only got %" PRIsize, + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %zu file header bytes, only got %zu", sizeof(magic), amt_read); } return (NULL); @@ -392,7 +541,7 @@ pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision, /* * Well, who knows what this mess is.... */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown file format"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown file format"); return (NULL); found: @@ -412,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; @@ -456,15 +606,19 @@ found: 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 @@ -475,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? @@ -500,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 || 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; } }