]> The Tcpdump Group git mirrors - libpcap/blobdiff - savefile.c
Update config.{guess,sub}, timestamps 2023-01-01,2023-01-21
[libpcap] / savefile.c
index abe1d2dd63b50de5d3b1f5c071a7324902f419cd..db8a3aa0597206f894cd1a8cca00156ad4c475b8 100644 (file)
  */
 
 #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__)
@@ -76,7 +86,7 @@
 #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
@@ -86,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
@@ -102,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");
@@ -119,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");
@@ -127,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);
 }
@@ -148,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");
@@ -165,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.
@@ -184,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);
                }
        }
@@ -210,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)
 {
@@ -218,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;
        }
 
@@ -242,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 *
@@ -257,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);
        }
@@ -313,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.
@@ -324,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;
@@ -332,10 +570,19 @@ 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
 
        /*
@@ -344,6 +591,11 @@ found:
         */
        p->oneshot_callback = pcap_oneshot;
 
+       /*
+        * Default breakloop operation.
+        */
+       p->breakloop_op = pcap_breakloop_common;
+
        /*
         * Savefiles never require special BPF code generation.
         */
@@ -354,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
@@ -373,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?
@@ -398,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;
                }
        }