*/
#ifdef HAVE_CONFIG_H
-#include "config.h"
+#include <config.h>
#endif
-#ifdef WIN32
-#include <pcap-stdinc.h>
-#else /* WIN32 */
-#if HAVE_INTTYPES_H
-#include <inttypes.h>
-#elif HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#ifdef HAVE_SYS_BITYPES_H
-#include <sys/bitypes.h>
-#endif
-#include <sys/types.h>
-#endif /* WIN32 */
+#include <pcap-types.h>
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif /* _WIN32 */
#include <errno.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <limits.h> /* 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__)
#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
}
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
}
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");
}
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");
}
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);
}
* 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");
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.
#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);
}
}
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)
{
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;
}
}
#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 *
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);
}
/* 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.
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;
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
/*
*/
p->oneshot_callback = pcap_oneshot;
+ /*
+ * Default breakloop operation.
+ */
+ p->breakloop_op = pcap_breakloop_common;
+
/*
* Savefiles never require special BPF code generation.
*/
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
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?
}
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;
}
}