]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-npf.c
Update config.{guess,sub}, timestamps 2023-01-01,2023-01-21
[libpcap] / pcap-npf.c
index f33f311461a655b6365bed2a5a6346abdbd93faa..99b5981e5a571a97896bcc4ad2ebd5aaa8787c1f 100644 (file)
 #endif
 
 #include <errno.h>
+#include <limits.h> /* for INT_MAX */
 #define PCAP_DONT_INCLUDE_PCAP_BPF_H
 #include <Packet32.h>
 #include <pcap-int.h>
 #include <pcap/dlt.h>
 
+/*
+ * XXX - Packet32.h defines bpf_program, so we can't include
+ * <pcap/bpf.h>, which also defines it; that's why we define
+ * PCAP_DONT_INCLUDE_PCAP_BPF_H,
+ *
+ * However, no header in the WinPcap or Npcap SDKs defines the
+ * macros for BPF code, so we have to define them ourselves.
+ */
+#define                BPF_RET         0x06
+#define                BPF_K           0x00
+
 /* Old-school MinGW have these headers in a different place.
  */
 #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
   #include <dagapi.h>
 #endif /* HAVE_DAG_API */
 
+#include "diag-control.h"
+
+#include "pcap-airpcap.h"
+
 static int pcap_setfilter_npf(pcap_t *, struct bpf_program *);
 static int pcap_setfilter_win32_dag(pcap_t *, struct bpf_program *);
 static int pcap_getnonblock_npf(pcap_t *);
@@ -70,7 +86,7 @@ static int pcap_setnonblock_npf(pcap_t *, int);
 #define SWAPS(_X) ((_X & 0xff) << 8) | (_X >> 8)
 
 /*
- * Private data for capturing on WinPcap devices.
+ * Private data for capturing on WinPcap/Npcap devices.
  */
 struct pcap_win {
        ADAPTER *adapter;               /* the packet32 ADAPTER for the device */
@@ -134,7 +150,7 @@ PacketGetMonitorMode(PCHAR AdapterName _U_)
  * or NDIS_STATUS_NOT_RECOGNIZED if the OID request isn't
  * supported by the OS or the driver, but that doesn't seem
  * to make it to the caller of PacketRequest() in a
- * reiable fashion.
+ * reliable fashion.
  */
 #define NDIS_STATUS_INVALID_OID                0xc0010017
 #define NDIS_STATUS_NOT_SUPPORTED      0xc00000bb      /* STATUS_NOT_SUPPORTED */
@@ -155,7 +171,7 @@ oid_get_request(ADAPTER *adapter, bpf_u_int32 oid, void *data, size_t *lenp,
         */
        oid_data_arg = malloc(sizeof (PACKET_OID_DATA) + *lenp);
        if (oid_data_arg == NULL) {
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(errbuf, PCAP_ERRBUF_SIZE,
                    "Couldn't allocate argument buffer for PacketRequest");
                return (PCAP_ERROR);
        }
@@ -166,11 +182,8 @@ oid_get_request(ADAPTER *adapter, bpf_u_int32 oid, void *data, size_t *lenp,
        oid_data_arg->Oid = oid;
        oid_data_arg->Length = (ULONG)(*lenp);  /* XXX - check for ridiculously large value? */
        if (!PacketRequest(adapter, FALSE, oid_data_arg)) {
-               char errmsgbuf[PCAP_ERRBUF_SIZE+1];
-
-               pcap_win32_err_to_str(GetLastError(), errmsgbuf);
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "Error calling PacketRequest: %s", errmsgbuf);
+               pcap_fmt_errmsg_for_win32_err(errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "Error calling PacketRequest");
                free(oid_data_arg);
                return (-1);
        }
@@ -193,7 +206,6 @@ pcap_stats_npf(pcap_t *p, struct pcap_stat *ps)
 {
        struct pcap_win *pw = p->priv;
        struct bpf_stat bstats;
-       char errbuf[PCAP_ERRBUF_SIZE+1];
 
        /*
         * Try to get statistics.
@@ -209,9 +221,8 @@ pcap_stats_npf(pcap_t *p, struct pcap_stat *ps)
         * to us.
         */
        if (!PacketGetStats(pw->adapter, &bstats)) {
-               pcap_win32_err_to_str(GetLastError(), errbuf);
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "PacketGetStats error: %s", errbuf);
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "PacketGetStats error");
                return (-1);
        }
        ps->ps_recv = bstats.bs_recv;
@@ -245,18 +256,17 @@ pcap_stats_npf(pcap_t *p, struct pcap_stat *ps)
  * have an API that returns data in a form like the Options section of a
  * pcapng Interface Statistics Block:
  *
- *    http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.6
+ *    https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.6
  *
  * which would let us add new statistics straightforwardly and indicate which
  * statistics we are and are *not* providing, rather than having to provide
  * possibly-bogus values for statistics we can't provide.
  */
-struct pcap_stat *
+static struct pcap_stat *
 pcap_stats_ex_npf(pcap_t *p, int *pcap_stat_size)
 {
        struct pcap_win *pw = p->priv;
        struct bpf_stat bstats;
-       char errbuf[PCAP_ERRBUF_SIZE+1];
 
        *pcap_stat_size = sizeof (p->stat);
 
@@ -268,15 +278,19 @@ pcap_stats_ex_npf(pcap_t *p, int *pcap_stat_size)
         * same layout, but let's not cheat.)
         */
        if (!PacketGetStatsEx(pw->adapter, &bstats)) {
-               pcap_win32_err_to_str(GetLastError(), errbuf);
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "PacketGetStatsEx error: %s", errbuf);
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "PacketGetStatsEx error");
                return (NULL);
        }
        p->stat.ps_recv = bstats.bs_recv;
        p->stat.ps_drop = bstats.bs_drop;
        p->stat.ps_ifdrop = bstats.ps_ifdrop;
-#ifdef ENABLE_REMOTE
+       /*
+        * Just in case this is ever compiled for a target other than
+        * Windows, which is somewhere between extremely unlikely and
+        * impossible.
+        */
+#ifdef _WIN32
        p->stat.ps_capt = bstats.bs_capt;
 #endif
        return (&p->stat);
@@ -290,7 +304,7 @@ pcap_setbuff_npf(pcap_t *p, int dim)
 
        if(PacketSetBuff(pw->adapter,dim)==FALSE)
        {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer");
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer");
                return (-1);
        }
        return (0);
@@ -304,7 +318,7 @@ pcap_setmode_npf(pcap_t *p, int mode)
 
        if(PacketSetMode(pw->adapter,mode)==FALSE)
        {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: working mode not recognized");
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: working mode not recognized");
                return (-1);
        }
 
@@ -319,7 +333,7 @@ pcap_setmintocopy_npf(pcap_t *p, int size)
 
        if(PacketSetMinToCopy(pw->adapter, size)==FALSE)
        {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: unable to set the requested mintocopy size");
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: unable to set the requested mintocopy size");
                return (-1);
        }
        return (0);
@@ -347,7 +361,6 @@ pcap_oid_set_request_npf(pcap_t *p, bpf_u_int32 oid, const void *data,
 {
        struct pcap_win *pw = p->priv;
        PACKET_OID_DATA *oid_data_arg;
-       char errbuf[PCAP_ERRBUF_SIZE+1];
 
        /*
         * Allocate a PACKET_OID_DATA structure to hand to PacketRequest().
@@ -358,7 +371,7 @@ pcap_oid_set_request_npf(pcap_t *p, bpf_u_int32 oid, const void *data,
         */
        oid_data_arg = malloc(sizeof (PACKET_OID_DATA) + *lenp);
        if (oid_data_arg == NULL) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "Couldn't allocate argument buffer for PacketRequest");
                return (PCAP_ERROR);
        }
@@ -367,9 +380,8 @@ pcap_oid_set_request_npf(pcap_t *p, bpf_u_int32 oid, const void *data,
        oid_data_arg->Length = (ULONG)(*lenp);  /* XXX - check for ridiculously large value? */
        memcpy(oid_data_arg->Data, data, *lenp);
        if (!PacketRequest(pw->adapter, TRUE, oid_data_arg)) {
-               pcap_win32_err_to_str(GetLastError(), errbuf);
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "Error calling PacketRequest: %s", errbuf);
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "Error calling PacketRequest");
                free(oid_data_arg);
                return (PCAP_ERROR);
        }
@@ -391,13 +403,6 @@ pcap_sendqueue_transmit_npf(pcap_t *p, pcap_send_queue *queue, int sync)
 {
        struct pcap_win *pw = p->priv;
        u_int res;
-       char errbuf[PCAP_ERRBUF_SIZE+1];
-
-       if (pw->adapter==NULL) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "Cannot transmit a queue to an offline capture or to a TurboCap port");
-               return (0);
-       }
 
        res = PacketSendPackets(pw->adapter,
                queue->buffer,
@@ -405,9 +410,8 @@ pcap_sendqueue_transmit_npf(pcap_t *p, pcap_send_queue *queue, int sync)
                (BOOLEAN)sync);
 
        if(res != queue->len){
-               pcap_win32_err_to_str(GetLastError(), errbuf);
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "Error opening adapter: %s", errbuf);
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "Error queueing packets");
        }
 
        return (res);
@@ -420,7 +424,7 @@ pcap_setuserbuffer_npf(pcap_t *p, int size)
 
        if (size<=0) {
                /* Bogus parameter */
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "Error: invalid size %d",size);
                return (-1);
        }
@@ -429,7 +433,7 @@ pcap_setuserbuffer_npf(pcap_t *p, int size)
        new_buff=(unsigned char*)malloc(sizeof(char)*size);
 
        if (!new_buff) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "Error: not enough memory");
                return (-1);
        }
@@ -442,6 +446,31 @@ pcap_setuserbuffer_npf(pcap_t *p, int size)
        return (0);
 }
 
+#ifdef HAVE_NPCAP_PACKET_API
+/*
+ * Kernel dump mode isn't supported in Npcap; calls to PacketSetDumpName(),
+ * PacketSetDumpLimits(), and PacketIsDumpEnded() will get compile-time
+ * deprecation warnings.
+ *
+ * Avoid calling them; just return errors indicating that kernel dump
+ * mode isn't supported in Npcap.
+ */
+static int
+pcap_live_dump_npf(pcap_t *p, char *filename _U_, int maxsize _U_,
+    int maxpacks _U_)
+{
+       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+           "Npcap doesn't support kernel dump mode");
+       return (-1);
+}
+static int
+pcap_live_dump_ended_npf(pcap_t *p, int sync)
+{
+       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+           "Npcap doesn't support kernel dump mode");
+       return (-1);
+}
+#else /* HAVE_NPCAP_PACKET_API */
 static int
 pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks)
 {
@@ -451,7 +480,7 @@ pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks)
        /* Set the packet driver in dump mode */
        res = PacketSetMode(pw->adapter, PACKET_MODE_DUMP);
        if(res == FALSE){
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "Error setting dump mode");
                return (-1);
        }
@@ -459,7 +488,7 @@ pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks)
        /* Set the name of the dump file */
        res = PacketSetDumpName(pw->adapter, filename, (int)strlen(filename));
        if(res == FALSE){
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "Error setting kernel dump file name");
                return (-1);
        }
@@ -467,8 +496,8 @@ pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks)
        /* Set the limits of the dump file */
        res = PacketSetDumpLimits(pw->adapter, maxsize, maxpacks);
        if(res == FALSE) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                               "Error setting dump limit");
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                               "Error setting dump limit");
                return (-1);
        }
 
@@ -482,31 +511,36 @@ pcap_live_dump_ended_npf(pcap_t *p, int sync)
 
        return (PacketIsDumpEnded(pw->adapter, (BOOLEAN)sync));
 }
+#endif /* HAVE_NPCAP_PACKET_API */
 
+#ifdef HAVE_AIRPCAP_API
 static PAirpcapHandle
 pcap_get_airpcap_handle_npf(pcap_t *p)
 {
-#ifdef HAVE_AIRPCAP_API
        struct pcap_win *pw = p->priv;
 
        return (PacketGetAirPcapHandle(pw->adapter));
-#else
+}
+#else /* HAVE_AIRPCAP_API */
+static PAirpcapHandle
+pcap_get_airpcap_handle_npf(pcap_t *p _U_)
+{
        return (NULL);
-#endif /* HAVE_AIRPCAP_API */
 }
+#endif /* HAVE_AIRPCAP_API */
 
 static int
 pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 {
        PACKET Packet;
        int cc;
-       int n = 0;
+       int n;
        register u_char *bp, *ep;
        u_char *datap;
        struct pcap_win *pw = p->priv;
 
        cc = p->cc;
-       if (p->cc == 0) {
+       if (cc == 0) {
                /*
                 * Has "pcap_breakloop()" been called?
                 */
@@ -534,7 +568,60 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                 */
                PacketInitPacket(&Packet, (BYTE *)p->buffer, p->bufsize);
                if (!PacketReceivePacket(pw->adapter, &Packet, TRUE)) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error: PacketReceivePacket failed");
+                       /*
+                        * Did the device go away?
+                        * If so, the error we get can either be
+                        * ERROR_GEN_FAILURE or ERROR_DEVICE_REMOVED.
+                        */
+                       DWORD errcode = GetLastError();
+
+                       if (errcode == ERROR_GEN_FAILURE ||
+                           errcode == ERROR_DEVICE_REMOVED) {
+                               /*
+                                * The device on which we're capturing
+                                * went away, or it became unusable
+                                * by NPF due to a suspend/resume.
+                                *
+                                * ERROR_GEN_FAILURE comes from
+                                * STATUS_UNSUCCESSFUL, as well as some
+                                * other NT status codes that the Npcap
+                                * driver is unlikely to return.
+                                * XXX - hopefully no other error
+                                * conditions are indicated by this.
+                                *
+                                * ERROR_DEVICE_REMOVED comes from
+                                * STATUS_DEVICE_REMOVED.
+                                *
+                                * We report the Windows status code
+                                * name and the corresponding NT status
+                                * code name, for the benefit of attempts
+                                * to debug cases where this error is
+                                * reported when the device *wasn't*
+                                * removed, either because it's not
+                                * removable, it's removable but wasn't
+                                * removed, or it's a device that doesn't
+                                * correspond to a physical device.
+                                *
+                                * XXX - we really should return an
+                                * appropriate error for that, but
+                                * pcap_dispatch() etc. aren't
+                                * documented as having error returns
+                                * other than PCAP_ERROR or PCAP_ERROR_BREAK.
+                                */
+                               const char *errcode_msg;
+
+                               if (errcode == ERROR_GEN_FAILURE)
+                                       errcode_msg = "ERROR_GEN_FAILURE/STATUS_UNSUCCESSFUL";
+                               else
+                                       errcode_msg = "ERROR_DEVICE_REMOVED/STATUS_DEVICE_REMOVED";
+                               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                                   "The interface disappeared (error code %s)",
+                                   errcode_msg);
+                       } else {
+                               pcap_fmt_errmsg_for_win32_err(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errcode,
+                                   "PacketReceivePacket error");
+                       }
                        return (PCAP_ERROR);
                }
 
@@ -547,11 +634,15 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 
        /*
         * Loop through each packet.
+        *
+        * This assumes that a single buffer of packets will have
+        * <= INT_MAX packets, so the packet count doesn't overflow.
         */
 #define bhp ((struct bpf_hdr *)bp)
+       n = 0;
        ep = bp + cc;
        for (;;) {
-               register int caplen, hdrlen;
+               register u_int caplen, hdrlen;
 
                /*
                 * Has "pcap_breakloop()" been called?
@@ -585,13 +676,13 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                 * in kernel, no need to do it now - we already know
                 * the packet passed the filter.
                 *
-                * XXX - bpf_filter() should always return TRUE if
+                * XXX - pcap_filter() should always return TRUE if
                 * handed a null pointer for the program, but it might
                 * just try to "run" the filter, so we check here.
                 */
                if (pw->filtering_in_kernel ||
                    p->fcode.bf_insns == NULL ||
-                   bpf_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) {
+                   pcap_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) {
 #ifdef ENABLE_REMOTE
                        switch (p->rmt_samp.method) {
 
@@ -690,7 +781,7 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                 */
                PacketInitPacket(&Packet, (BYTE *)p->buffer, p->bufsize);
                if (!PacketReceivePacket(pw->adapter, &Packet, TRUE)) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error: PacketReceivePacket failed");
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error: PacketReceivePacket failed");
                        return (-1);
                }
 
@@ -705,6 +796,21 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 
        endofbuf = (char*)header + cc;
 
+       /*
+        * 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;
+
        /*
         * Cycle through the packets
         */
@@ -795,10 +901,10 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                        }
                }
 
-               /* No underlaying filtering system. We need to filter on our own */
+               /* No underlying filtering system. We need to filter on our own */
                if (p->fcode.bf_insns)
                {
-                       if (bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen) == 0)
+                       if (pcap_filter(p->fcode.bf_insns, dp, packet_len, caplen) == 0)
                        {
                                /* Move to next packet */
                                header = (dag_record_t*)((char*)header + erf_record_len);
@@ -806,7 +912,7 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                        }
                }
 
-               /* Fill the header for the user suppplied callback function */
+               /* Fill the header for the user supplied callback function */
                pcap_header.caplen = caplen;
                pcap_header.len = packet_len;
 
@@ -832,14 +938,15 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 
 /* Send a packet to the network */
 static int
-pcap_inject_npf(pcap_t *p, const void *buf, size_t size)
+pcap_inject_npf(pcap_t *p, const void *buf, int size)
 {
        struct pcap_win *pw = p->priv;
        PACKET pkt;
 
        PacketInitPacket(&pkt, (PVOID)buf, size);
        if(PacketSendPacket(pw->adapter,&pkt,TRUE) == FALSE) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: PacketSendPacket failed");
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "send error: PacketSendPacket failed");
                return (-1);
        }
 
@@ -848,7 +955,7 @@ pcap_inject_npf(pcap_t *p, const void *buf, size_t size)
         * "pcap_inject()" is expected to return the number of bytes
         * sent.
         */
-       return ((int)size);
+       return (size);
 }
 
 static void
@@ -867,13 +974,57 @@ pcap_cleanup_npf(pcap_t *p)
        pcap_cleanup_live_common(p);
 }
 
+static void
+pcap_breakloop_npf(pcap_t *p)
+{
+       pcap_breakloop_common(p);
+       struct pcap_win *pw = p->priv;
+
+       /* XXX - what if this fails? */
+       SetEvent(PacketGetReadEvent(pw->adapter));
+}
+
+/*
+ * These are NTSTATUS values:
+ *
+ *    https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781
+ *
+ * with the "Customer" bit set.  If a driver returns them, they are not
+ * mapped to Windows error values in userland; they're returned by
+ * GetLastError().
+ *
+ * Note that "driver" here includes the Npcap NPF driver, as various
+ * versions would take NT status values and set the "Customer" bit
+ * before returning the status code.  The commit message for the
+ * change that started doing that is
+ *
+ *    Returned a customer-defined NTSTATUS in OID requests to avoid
+ *    NTSTATUS-to-Win32 Error code translation.
+ *
+ * but I don't know why the goal was to avoid that translation.
+ *
+ * Attempting to set the hardware filter on a Microsoft Surface Pro's
+ * Mobile Broadband Adapter returns an error that appears to be
+ * NDIS_STATUS_NOT_SUPPORTED ORed with the "Customer" bit, so it's
+ * probably indicating that it doesn't support that.
+ *
+ * It is likely that there are other devices which throw spurious errors,
+ * at which point this will need refactoring to efficiently check against
+ * a list, but for now we can just check this one value.  Perhaps the
+ * right way to do this is compare against various NDIS errors with
+ * the "customer" bit ORed in.
+ */
+#define NT_STATUS_CUSTOMER_DEFINED     0x20000000
+
 static int
 pcap_activate_npf(pcap_t *p)
 {
        struct pcap_win *pw = p->priv;
        NetType type;
        int res;
-       char errbuf[PCAP_ERRBUF_SIZE+1];
+       int status = 0;
+       struct bpf_insn total_insn;
+       struct bpf_program total_prog;
 
        if (p->opt.rfmon) {
                /*
@@ -905,40 +1056,69 @@ pcap_activate_npf(pcap_t *p)
                }
        }
 
-       /* Init WinSock */
+       /* Init Winsock if it hasn't already been initialized */
        pcap_wsockinit();
 
        pw->adapter = PacketOpenAdapter(p->opt.device);
 
        if (pw->adapter == NULL)
        {
-               /* Adapter detected but we are not able to open it. Return failure. */
-               pcap_win32_err_to_str(GetLastError(), errbuf);
-               if (pw->rfmon_selfstart)
-               {
-                       PacketSetMonitorMode(p->opt.device, 0);
+               DWORD errcode = GetLastError();
+
+               /*
+                * What error did we get when trying to open the adapter?
+                */
+               switch (errcode) {
+
+               case ERROR_BAD_UNIT:
+                       /*
+                        * There's no such device.
+                        * There's nothing to add, so clear the error
+                        * message.
+                        */
+                       p->errbuf[0] = '\0';
+                       return (PCAP_ERROR_NO_SUCH_DEVICE);
+
+               case ERROR_ACCESS_DENIED:
+                       /*
+                        * There is, but we don't have permission to
+                        * use it.
+                        *
+                        * XXX - we currently get ERROR_BAD_UNIT if the
+                        * user says "no" to the UAC prompt.
+                        */
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                           "The helper program for \"Admin-only Mode\" must be allowed to make changes to your device");
+                       return (PCAP_ERROR_PERM_DENIED);
+
+               default:
+                       /*
+                        * Unknown - report details.
+                        */
+                       pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errcode, "Error opening adapter");
+                       if (pw->rfmon_selfstart)
+                       {
+                               PacketSetMonitorMode(p->opt.device, 0);
+                       }
+                       return (PCAP_ERROR);
                }
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "Error opening adapter: %s", errbuf);
-               return (PCAP_ERROR);
        }
 
        /*get network type*/
        if(PacketGetNetType (pw->adapter,&type) == FALSE)
        {
-               pcap_win32_err_to_str(GetLastError(), errbuf);
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "Cannot determine the network type: %s", errbuf);
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "Cannot determine the network type");
                goto bad;
        }
 
        /*Set the linktype*/
        switch (type.LinkType)
        {
-       case NdisMediumWan:
-               p->linktype = DLT_EN10MB;
-               break;
-
+       /*
+        * NDIS-defined medium types.
+        */
        case NdisMedium802_3:
                p->linktype = DLT_EN10MB;
                /*
@@ -962,12 +1142,19 @@ pcap_activate_npf(pcap_t *p)
                }
                break;
 
+       case NdisMedium802_5:
+               /*
+                * Token Ring.
+                */
+               p->linktype = DLT_IEEE802;
+               break;
+
        case NdisMediumFddi:
                p->linktype = DLT_FDDI;
                break;
 
-       case NdisMedium802_5:
-               p->linktype = DLT_IEEE802;
+       case NdisMediumWan:
+               p->linktype = DLT_EN10MB;
                break;
 
        case NdisMediumArcnetRaw:
@@ -982,18 +1169,29 @@ pcap_activate_npf(pcap_t *p)
                p->linktype = DLT_ATM_RFC1483;
                break;
 
-       case NdisMediumCHDLC:
-               p->linktype = DLT_CHDLC;
+       case NdisMediumWirelessWan:
+               p->linktype = DLT_RAW;
                break;
 
-       case NdisMediumPPPSerial:
-               p->linktype = DLT_PPP_SERIAL;
+       case NdisMediumIP:
+               p->linktype = DLT_RAW;
                break;
 
+       /*
+        * Npcap-defined medium types.
+        */
        case NdisMediumNull:
                p->linktype = DLT_NULL;
                break;
 
+       case NdisMediumCHDLC:
+               p->linktype = DLT_CHDLC;
+               break;
+
+       case NdisMediumPPPSerial:
+               p->linktype = DLT_PPP_SERIAL;
+               break;
+
        case NdisMediumBare80211:
                p->linktype = DLT_IEEE802_11;
                break;
@@ -1007,10 +1205,84 @@ pcap_activate_npf(pcap_t *p)
                break;
 
        default:
-               p->linktype = DLT_EN10MB;                       /*an unknown adapter is assumed to be ethernet*/
+               /*
+                * An unknown medium type is assumed to supply Ethernet
+                * headers; if not, the user will have to report it,
+                * so that the medium type and link-layer header type
+                * can be determined.  If we were to fail here, we
+                * might get the link-layer type in the error, but
+                * the user wouldn't get a capture, so we wouldn't
+                * be able to determine the link-layer type; we report
+                * a warning with the link-layer type, so at least
+                * some programs will report the warning.
+                */
+               p->linktype = DLT_EN10MB;
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                   "Unknown NdisMedium value %d, defaulting to DLT_EN10MB",
+                   type.LinkType);
+               status = PCAP_WARNING;
                break;
        }
 
+#ifdef HAVE_PACKET_GET_TIMESTAMP_MODES
+       /*
+        * Set the timestamp type.
+        * (Yes, we require PacketGetTimestampModes(), not just
+        * PacketSetTimestampMode().  If we have the former, we
+        * have the latter, unless somebody's using a version
+        * of Npcap that they've hacked to provide the former
+        * but not the latter; if they've done that, either
+        * they're confused or they're trolling us.)
+        */
+       switch (p->opt.tstamp_type) {
+
+       case PCAP_TSTAMP_HOST_HIPREC_UNSYNCED:
+               /*
+                * Better than low-res, but *not* synchronized with
+                * the OS clock.
+                */
+               if (!PacketSetTimestampMode(pw->adapter, TIMESTAMPMODE_SINGLE_SYNCHRONIZATION))
+               {
+                       pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                           GetLastError(), "Cannot set the time stamp mode to TIMESTAMPMODE_SINGLE_SYNCHRONIZATION");
+                       goto bad;
+               }
+               break;
+
+       case PCAP_TSTAMP_HOST_LOWPREC:
+               /*
+                * Low-res, but synchronized with the OS clock.
+                */
+               if (!PacketSetTimestampMode(pw->adapter, TIMESTAMPMODE_QUERYSYSTEMTIME))
+               {
+                       pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                           GetLastError(), "Cannot set the time stamp mode to TIMESTAMPMODE_QUERYSYSTEMTIME");
+                       goto bad;
+               }
+               break;
+
+       case PCAP_TSTAMP_HOST_HIPREC:
+               /*
+                * High-res, and synchronized with the OS clock.
+                */
+               if (!PacketSetTimestampMode(pw->adapter, TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE))
+               {
+                       pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                           GetLastError(), "Cannot set the time stamp mode to TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE");
+                       goto bad;
+               }
+               break;
+
+       case PCAP_TSTAMP_HOST:
+               /*
+                * XXX - do whatever the default is, for now.
+                * Set to the highest resolution that's synchronized
+                * with the system clock?
+                */
+               break;
+       }
+#endif /* HAVE_PACKET_GET_TIMESTAMP_MODES */
+
        /*
         * Turn a negative snapshot value (invalid), a snapshot value of
         * 0 (unspecified), or a value bigger than the normal maximum
@@ -1028,16 +1300,86 @@ pcap_activate_npf(pcap_t *p)
 
                if (PacketSetHwFilter(pw->adapter,NDIS_PACKET_TYPE_PROMISCUOUS) == FALSE)
                {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to promiscuous mode");
-                       goto bad;
+                       DWORD errcode = GetLastError();
+
+                       /*
+                        * Suppress spurious error generated by non-compiant
+                        * MS Surface mobile adapters that appear to
+                        * return NDIS_STATUS_NOT_SUPPORTED for attempts
+                        * to set the hardware filter.
+                        *
+                        * It appears to be reporting NDIS_STATUS_NOT_SUPPORTED,
+                        * but with the NT status value "Customer" bit set;
+                        * the Npcap NPF driver sets that bit in some cases.
+                        *
+                        * If we knew that this meant "promiscuous mode
+                        * isn't supported", we could add a "promiscuous
+                        * mode isn't supported" error code and return
+                        * that, but:
+                        *
+                        *    1) we don't know that it means that
+                        *    rather than meaning "we reject attempts
+                        *    to set the filter, even though the NDIS
+                        *    specifications say you shouldn't do that"
+                        *
+                        * and
+                        *
+                        *    2) other interface types that don't
+                        *    support promiscuous mode, at least
+                        *    on UN*Xes, just silently ignore
+                        *    attempts to set promiscuous mode
+                        *
+                        * and rejecting it with an error could disrupt
+                        * attempts to capture, as many programs (tcpdump,
+                        * *shark) default to promiscuous mode.
+                        *
+                        * Alternatively, we could return the "promiscuous
+                        * mode not supported" *warning* value, so that
+                        * correct code will either ignore it or report
+                        * it and continue capturing.  (This may require
+                        * a pcap_init() flag to request that return
+                        * value, so that old incorrect programs that
+                        * assume a non-zero return from pcap_activate()
+                        * is an error don't break.)
+                        */
+                       if (errcode != (NDIS_STATUS_NOT_SUPPORTED|NT_STATUS_CUSTOMER_DEFINED))
+                       {
+                               pcap_fmt_errmsg_for_win32_err(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errcode,
+                                   "failed to set hardware filter to promiscuous mode");
+                               goto bad;
+                       }
                }
        }
        else
        {
-               if (PacketSetHwFilter(pw->adapter,NDIS_PACKET_TYPE_ALL_LOCAL) == FALSE)
+               /*
+                * NDIS_PACKET_TYPE_ALL_LOCAL selects "All packets sent by
+                * installed protocols and all packets indicated by the NIC",
+                * but if no protocol drivers (like TCP/IP) are installed,
+                * NDIS_PACKET_TYPE_DIRECTED, NDIS_PACKET_TYPE_BROADCAST,
+                * and NDIS_PACKET_TYPE_MULTICAST are needed to capture
+                * incoming frames.
+                */
+               if (PacketSetHwFilter(pw->adapter,
+                       NDIS_PACKET_TYPE_ALL_LOCAL |
+                       NDIS_PACKET_TYPE_DIRECTED |
+                       NDIS_PACKET_TYPE_BROADCAST |
+                       NDIS_PACKET_TYPE_MULTICAST) == FALSE)
                {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to non-promiscuous mode");
-                       goto bad;
+                       DWORD errcode = GetLastError();
+
+                       /*
+                        * Suppress spurious error generated by non-compiant
+                        * MS Surface mobile adapters.
+                        */
+                       if (errcode != (NDIS_STATUS_NOT_SUPPORTED|NT_STATUS_CUSTOMER_DEFINED))
+                       {
+                               pcap_fmt_errmsg_for_win32_err(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errcode,
+                                   "failed to set hardware filter to non-promiscuous mode");
+                               goto bad;
+                       }
                }
        }
 
@@ -1053,12 +1395,12 @@ pcap_activate_npf(pcap_t *p)
                 * If the buffer size wasn't explicitly set, default to
                 * WIN32_DEFAULT_KERNEL_BUFFER_SIZE.
                 */
-               if (p->opt.buffer_size == 0)
-                       p->opt.buffer_size = WIN32_DEFAULT_KERNEL_BUFFER_SIZE;
+               if (p->opt.buffer_size == 0)
+                       p->opt.buffer_size = WIN32_DEFAULT_KERNEL_BUFFER_SIZE;
 
                if(PacketSetBuff(pw->adapter,p->opt.buffer_size)==FALSE)
                {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer");
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer");
                        goto bad;
                }
 
@@ -1075,10 +1417,9 @@ pcap_activate_npf(pcap_t *p)
                        /* tell the driver to copy the buffer as soon as data arrives */
                        if(PacketSetMinToCopy(pw->adapter,0)==FALSE)
                        {
-                               pcap_win32_err_to_str(GetLastError(), errbuf);
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "Error calling PacketSetMinToCopy: %s",
-                                   errbuf);
+                               pcap_fmt_errmsg_for_win32_err(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, GetLastError(),
+                                   "Error calling PacketSetMinToCopy");
                                goto bad;
                        }
                }
@@ -1087,10 +1428,9 @@ pcap_activate_npf(pcap_t *p)
                        /* tell the driver to copy the buffer only if it contains at least 16K */
                        if(PacketSetMinToCopy(pw->adapter,16000)==FALSE)
                        {
-                               pcap_win32_err_to_str(GetLastError(), errbuf);
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "Error calling PacketSetMinToCopy: %s",
-                                   errbuf);
+                               pcap_fmt_errmsg_for_win32_err(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, GetLastError(),
+                                   "Error calling PacketSetMinToCopy");
                                goto bad;
                        }
                }
@@ -1109,7 +1449,7 @@ pcap_activate_npf(pcap_t *p)
                int             postype = 0;
                char    keyname[512];
 
-               pcap_snprintf(keyname, sizeof(keyname), "%s\\CardParams\\%s",
+               snprintf(keyname, sizeof(keyname), "%s\\CardParams\\%s",
                        "SYSTEM\\CurrentControlSet\\Services\\DAG",
                        strstr(_strlwr(p->opt.device), "dag"));
                do
@@ -1148,6 +1488,29 @@ pcap_activate_npf(pcap_t *p)
 #endif /* HAVE_DAG_API */
        }
 
+       /*
+        * If there's no filter program installed, there's
+        * no indication to the kernel of what the snapshot
+        * length should be, so no snapshotting is done.
+        *
+        * Therefore, when we open the device, we install
+        * an "accept everything" filter with the specified
+        * snapshot length.
+        */
+       total_insn.code = (u_short)(BPF_RET | BPF_K);
+       total_insn.jt = 0;
+       total_insn.jf = 0;
+       total_insn.k = p->snapshot;
+
+       total_prog.bf_len = 1;
+       total_prog.bf_insns = &total_insn;
+       if (!PacketSetBpf(pw->adapter, &total_prog)) {
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "PacketSetBpf");
+               status = PCAP_ERROR;
+               goto bad;
+       }
+
        PacketSetReadTimeout(pw->adapter, p->opt.timeout);
 
        /* disable loopback capture if requested */
@@ -1155,7 +1518,7 @@ pcap_activate_npf(pcap_t *p)
        {
                if (!PacketSetLoopbackBehavior(pw->adapter, NPF_DISABLE_LOOPBACK))
                {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                            "Unable to disable the capture of loopback packets.");
                        goto bad;
                }
@@ -1184,6 +1547,7 @@ pcap_activate_npf(pcap_t *p)
        p->getnonblock_op = pcap_getnonblock_npf;
        p->setnonblock_op = pcap_setnonblock_npf;
        p->stats_op = pcap_stats_npf;
+       p->breakloop_op = pcap_breakloop_npf;
        p->stats_ex_op = pcap_stats_ex_npf;
        p->setbuff_op = pcap_setbuff_npf;
        p->setmode_op = pcap_setmode_npf;
@@ -1211,7 +1575,7 @@ pcap_activate_npf(pcap_t *p)
         */
        p->handle = pw->adapter->hFile;
 
-       return (0);
+       return (status);
 bad:
        pcap_cleanup_npf(p);
        return (PCAP_ERROR);
@@ -1226,17 +1590,295 @@ pcap_can_set_rfmon_npf(pcap_t *p)
        return (PacketIsMonitorModeSupported(p->opt.device) == 1);
 }
 
+/*
+ * Get a list of time stamp types.
+ */
+#ifdef HAVE_PACKET_GET_TIMESTAMP_MODES
+static int
+get_ts_types(const char *device, pcap_t *p, char *ebuf)
+{
+       char *device_copy = NULL;
+       ADAPTER *adapter = NULL;
+       ULONG num_ts_modes;
+       BOOL ret;
+       DWORD error = ERROR_SUCCESS;
+       ULONG *modes = NULL;
+       int status = 0;
+
+       do {
+               /*
+                * First, find out how many time stamp modes we have.
+                * To do that, we have to open the adapter.
+                *
+                * XXX - PacketOpenAdapter() takes a non-const pointer
+                * as an argument, so we make a copy of the argument and
+                * pass that to it.
+                */
+               device_copy = strdup(device);
+               if (device_copy == NULL) {
+                       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc");
+                       status = -1;
+                       break;
+               }
+
+               adapter = PacketOpenAdapter(device_copy);
+               if (adapter == NULL)
+               {
+                       error = GetLastError();
+                       /*
+                        * If we can't open the device now, we won't be
+                        * able to later, either.
+                        *
+                        * If the error is something that indicates
+                        * that the device doesn't exist, or that they
+                        * don't have permission to open the device - or
+                        * perhaps that they don't have permission to get
+                        * a list of devices, if PacketOpenAdapter() does
+                        * that - the user will find that out when they try
+                        * to activate the device; just return an empty
+                        * list of time stamp types.
+                        *
+                        * Treating either of those as errors will, for
+                        * example, cause "tcpdump -i <number>" to fail,
+                        * because it first tries to pass the interface
+                        * name to pcap_create() and pcap_activate(),
+                        * in order to handle OSes where interfaces can
+                        * have names that are just numbers (stand up
+                        * and say hello, Linux!), and, if pcap_activate()
+                        * fails with a "no such device" error, checks
+                        * whether the interface name is a valid number
+                        * and, if so, tries to use it as an index in
+                        * the list of interfaces.
+                        *
+                        * That means pcap_create() must succeed even
+                        * for interfaces that don't exist, with the
+                        * failure occurring at pcap_activate() time.
+                        */
+                       if (error == ERROR_BAD_UNIT ||
+                           error == ERROR_ACCESS_DENIED) {
+                               p->tstamp_type_count = 0;
+                               p->tstamp_type_list = NULL;
+                               status = 0;
+                       } else {
+                               pcap_fmt_errmsg_for_win32_err(ebuf,
+                                   PCAP_ERRBUF_SIZE, error,
+                                   "Error opening adapter");
+                               status = -1;
+                       }
+                       break;
+               }
+
+               /*
+                * Get the total number of time stamp modes.
+                *
+                * The buffer for PacketGetTimestampModes() is
+                * a sequence of 1 or more ULONGs.  What's
+                * passed to PacketGetTimestampModes() should have
+                * the total number of ULONGs in the first ULONG;
+                * what's returned *from* PacketGetTimestampModes()
+                * has the total number of time stamp modes in
+                * the first ULONG.
+                *
+                * Yes, that means if there are N time stamp
+                * modes, the first ULONG should be set to N+1
+                * on input, and will be set to N on output.
+                *
+                * We first make a call to PacketGetTimestampModes()
+                * with a pointer to a single ULONG set to 1; the
+                * call should fail with ERROR_MORE_DATA (unless
+                * there are *no* modes, but that should never
+                * happen), and that ULONG should be set to the
+                * number of modes.
+                */
+               num_ts_modes = 1;
+               ret = PacketGetTimestampModes(adapter, &num_ts_modes);
+               if (!ret) {
+                       /*
+                        * OK, it failed.  Did it fail with
+                        * ERROR_MORE_DATA?
+                        */
+                       error = GetLastError();
+                       if (error != ERROR_MORE_DATA) {
+                               /*
+                                * No, did it fail with ERROR_INVALID_FUNCTION?
+                                */
+                               if (error == ERROR_INVALID_FUNCTION) {
+                                       /*
+                                        * This is probably due to
+                                        * the driver with which Packet.dll
+                                        * communicates being older, or
+                                        * being a WinPcap driver, so
+                                        * that it doesn't support
+                                        * BIOCGTIMESTAMPMODES.
+                                        *
+                                        * Tell the user to try uninstalling
+                                        * Npcap - and WinPcap if installed -
+                                        * and re-installing it, to flush
+                                        * out all older drivers.
+                                        */
+                                       snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                                           "PacketGetTimestampModes() failed with ERROR_INVALID_FUNCTION; try uninstalling Npcap, and WinPcap if installed, and re-installing it from npcap.com");
+                                       status = -1;
+                                       break;
+                               }
+
+                               /*
+                                * No, some other error.  Fail.
+                                */
+                               pcap_fmt_errmsg_for_win32_err(ebuf,
+                                   PCAP_ERRBUF_SIZE, error,
+                                   "Error calling PacketGetTimestampModes");
+                               status = -1;
+                               break;
+                       }
+               }
+               /* else (ret == TRUE)
+                * Unexpected success. Let's act like we got ERROR_MORE_DATA.
+                * If it doesn't work, we'll hit some other error condition farther on.
+                */
+
+               /* If the driver reports no modes supported *and*
+                * ERROR_MORE_DATA, something is seriously wrong.
+                * We *could* ignore the error and continue without supporting
+                * settable timestamp modes, but that would hide a bug.
+                */
+               if (num_ts_modes == 0) {
+                       snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                           "PacketGetTimestampModes() reports 0 modes supported.");
+                       status = -1;
+                       break;
+               }
+
+               /*
+                * Yes, so we now know how many types to fetch.
+                *
+                * The buffer needs to have one ULONG for the
+                * count and num_ts_modes ULONGs for the
+                * num_ts_modes time stamp types.
+                */
+               modes = (ULONG *)malloc((1 + num_ts_modes) * sizeof(ULONG));
+               if (modes == NULL) {
+                       /* Out of memory. */
+                       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc");
+                       status = -1;
+                       break;
+               }
+               modes[0] = 1 + num_ts_modes;
+               if (!PacketGetTimestampModes(adapter, modes)) {
+                       pcap_fmt_errmsg_for_win32_err(ebuf,
+                           PCAP_ERRBUF_SIZE, GetLastError(),
+                           "Error calling PacketGetTimestampModes");
+                       status = -1;
+                       break;
+               }
+               if (modes[0] != num_ts_modes) {
+                       snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                           "First PacketGetTimestampModes() call gives %lu modes, second call gives %lu modes",
+                           num_ts_modes, modes[0]);
+                       status = -1;
+                       break;
+               }
+
+               /*
+                * Allocate a buffer big enough for
+                * PCAP_TSTAMP_HOST (default) plus
+                * the explicitly specified modes.
+                */
+               p->tstamp_type_list = malloc((1 + num_ts_modes) * sizeof(u_int));
+               if (p->tstamp_type_list == NULL) {
+                       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc");
+                       status = -1;
+                       break;
+               }
+               u_int num_ts_types = 0;
+               p->tstamp_type_list[num_ts_types] =
+                   PCAP_TSTAMP_HOST;
+               num_ts_types++;
+               for (ULONG i = 0; i < num_ts_modes; i++) {
+                       switch (modes[i + 1]) {
+
+                       case TIMESTAMPMODE_SINGLE_SYNCHRONIZATION:
+                               /*
+                                * Better than low-res,
+                                * but *not* synchronized
+                                * with the OS clock.
+                                */
+                               p->tstamp_type_list[num_ts_types] =
+                                   PCAP_TSTAMP_HOST_HIPREC_UNSYNCED;
+                               num_ts_types++;
+                               break;
+
+                       case TIMESTAMPMODE_QUERYSYSTEMTIME:
+                               /*
+                                * Low-res, but synchronized
+                                * with the OS clock.
+                                */
+                               p->tstamp_type_list[num_ts_types] =
+                                   PCAP_TSTAMP_HOST_LOWPREC;
+                               num_ts_types++;
+                               break;
+
+                       case TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE:
+                               /*
+                                * High-res, and synchronized
+                                * with the OS clock.
+                                */
+                               p->tstamp_type_list[num_ts_types] =
+                                   PCAP_TSTAMP_HOST_HIPREC;
+                               num_ts_types++;
+                               break;
+
+                       default:
+                               /*
+                                * Unknown, so we can't
+                                * report it.
+                                */
+                               break;
+                       }
+               }
+               p->tstamp_type_count = num_ts_types;
+       } while (0);
+
+       /* Clean up temporary allocations */
+       if (device_copy != NULL) {
+               free(device_copy);
+       }
+       if (modes != NULL) {
+               free(modes);
+       }
+       if (adapter != NULL) {
+               PacketCloseAdapter(adapter);
+       }
+
+       return status;
+}
+#else /* HAVE_PACKET_GET_TIMESTAMP_MODES */
+static int
+get_ts_types(const char *device _U_, pcap_t *p _U_, char *ebuf _U_)
+{
+       /*
+        * Nothing to fetch, so it always "succeeds".
+        */
+       return 0;
+}
+#endif /* HAVE_PACKET_GET_TIMESTAMP_MODES */
+
 pcap_t *
 pcap_create_interface(const char *device _U_, char *ebuf)
 {
        pcap_t *p;
 
-       p = pcap_create_common(ebuf, sizeof(struct pcap_win));
+       p = PCAP_CREATE_COMMON(ebuf, struct pcap_win);
        if (p == NULL)
                return (NULL);
 
        p->activate_op = pcap_activate_npf;
        p->can_set_rfmon_op = pcap_can_set_rfmon_npf;
+
+       if (get_ts_types(device, p, ebuf) == -1) {
+               pcap_close(p);
+               return (NULL);
+       }
        return (p);
 }
 
@@ -1299,14 +1941,14 @@ pcap_setfilter_npf(pcap_t *p, struct bpf_program *fp)
 }
 
 /*
- * We filter at user level, since the kernel driver does't process the packets
+ * We filter at user level, since the kernel driver doesn't process the packets
  */
 static int
 pcap_setfilter_win32_dag(pcap_t *p, struct bpf_program *fp) {
 
        if(!fp)
        {
-               strlcpy(p->errbuf, "setfilter: No filter specified", sizeof(p->errbuf));
+               pcap_strlcpy(p->errbuf, "setfilter: No filter specified", sizeof(p->errbuf));
                return (-1);
        }
 
@@ -1335,7 +1977,6 @@ pcap_setnonblock_npf(pcap_t *p, int nonblock)
 {
        struct pcap_win *pw = p->priv;
        int newtimeout;
-       char win_errbuf[PCAP_ERRBUF_SIZE+1];
 
        if (nonblock) {
                /*
@@ -1355,9 +1996,8 @@ pcap_setnonblock_npf(pcap_t *p, int nonblock)
                newtimeout = p->opt.timeout;
        }
        if (!PacketSetReadTimeout(pw->adapter, newtimeout)) {
-               pcap_win32_err_to_str(GetLastError(), win_errbuf);
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "PacketSetReadTimeout: %s", win_errbuf);
+               pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "PacketSetReadTimeout");
                return (-1);
        }
        pw->nonblock = (newtimeout == -1);
@@ -1445,8 +2085,8 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
   #ifdef OID_GEN_PHYSICAL_MEDIUM_EX
                OID_GEN_PHYSICAL_MEDIUM_EX,
   #endif
-               OID_GEN_PHYSICAL_MEDIUM
-       };
+               OID_GEN_PHYSICAL_MEDIUM
+       };
 #define N_GEN_PHYSICAL_MEDIUM_OIDS     (sizeof gen_physical_medium_oids / sizeof gen_physical_medium_oids[0])
        size_t i;
 #endif /* OID_GEN_PHYSICAL_MEDIUM */
@@ -1499,7 +2139,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
                *flags |= PCAP_IF_WIRELESS;
 
                /*
-                * A "network assosiation state" makes no sense for airpcap.
+                * A "network association state" makes no sense for airpcap.
                 */
                *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
                PacketCloseAdapter(adapter);
@@ -1541,6 +2181,12 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
                         * running.
                         */
                        break;
+
+               default:
+                       /*
+                        * Unknown.
+                        */
+                       break;
                }
        } else {
                /*
@@ -1577,7 +2223,13 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
        if (status == 0) {
                /*
                 * We got the physical medium.
+                *
+                * XXX - we might want to check for NdisPhysicalMediumWiMax
+                * and NdisPhysicalMediumNative802_15_4 being
+                * part of the enum, and check for those in the "wireless"
+                * case.
                 */
+DIAG_OFF_ENUM_SWITCH
                switch (phys_medium) {
 
                case NdisPhysicalMediumWirelessLan:
@@ -1594,10 +2246,11 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
 
                default:
                        /*
-                        * Not wireless.
+                        * Not wireless or unknown
                         */
                        break;
                }
+DIAG_ON_ENUM_SWITCH
        }
 #endif
 
@@ -1628,6 +2281,13 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
                         */
                        *flags |= PCAP_IF_CONNECTION_STATUS_DISCONNECTED;
                        break;
+
+               case MediaConnectStateUnknown:
+               default:
+                       /*
+                        * It's unknown whether it's connected or not.
+                        */
+                       break;
                }
        }
 #else
@@ -1674,7 +2334,6 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
        char *AdaptersName;
        ULONG NameLength;
        char *name;
-       char our_errbuf[PCAP_ERRBUF_SIZE+1];
 
        /*
         * Find out how big a buffer we need.
@@ -1700,9 +2359,8 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
 
                if (last_error != ERROR_INSUFFICIENT_BUFFER)
                {
-                       pcap_win32_err_to_str(last_error, our_errbuf);
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "PacketGetAdapterNames: %s", our_errbuf);
+                       pcap_fmt_errmsg_for_win32_err(errbuf, PCAP_ERRBUF_SIZE,
+                           last_error, "PacketGetAdapterNames");
                        return (-1);
                }
        }
@@ -1712,14 +2370,13 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
        AdaptersName = (char*) malloc(NameLength);
        if (AdaptersName == NULL)
        {
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot allocate enough memory to list the adapters.");
+               snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot allocate enough memory to list the adapters.");
                return (-1);
        }
 
        if (!PacketGetAdapterNames(AdaptersName, &NameLength)) {
-               pcap_win32_err_to_str(GetLastError(), our_errbuf);
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "PacketGetAdapterNames: %s",
-                   our_errbuf);
+               pcap_fmt_errmsg_for_win32_err(errbuf, PCAP_ERRBUF_SIZE,
+                   GetLastError(), "PacketGetAdapterNames");
                free(AdaptersName);
                return (-1);
        }
@@ -1741,7 +2398,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
                desc++;
 
        /*
-        * Found it - "desc" points to the first of the two
+        * Found it - "desc" points to the first of the two
         * nulls at the end of the list of names, so the
         * first byte of the list of descriptions is two bytes
         * after it.
@@ -1754,6 +2411,20 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
        name = &AdaptersName[0];
        while (*name != '\0') {
                bpf_u_int32 flags = 0;
+
+#ifdef HAVE_AIRPCAP_API
+               /*
+                * Is this an AirPcap device?
+                * If so, ignore it; it'll get added later, by the
+                * AirPcap code.
+                */
+               if (device_is_airpcap(name, errbuf) == 1) {
+                       name += strlen(name) + 1;
+                       desc += strlen(desc) + 1;
+                       continue;
+               }
+#endif
+
 #ifdef HAVE_PACKET_IS_LOOPBACK_ADAPTER
                /*
                 * Is this a loopback interface?
@@ -1808,12 +2479,27 @@ pcap_lookupdev(char *errbuf)
 {
        DWORD dwVersion;
        DWORD dwWindowsMajorVersion;
-       char our_errbuf[PCAP_ERRBUF_SIZE+1];
 
-#pragma warning (push)
-#pragma warning (disable: 4996) /* disable MSVC's GetVersion() deprecated warning here */
+       /*
+        * We disable this in "new API" mode, because 1) in WinPcap/Npcap,
+        * it may return UTF-16 strings, for backwards-compatibility
+        * reasons, and we're also disabling the hack to make that work,
+        * for not-going-past-the-end-of-a-string reasons, and 2) we
+        * want its behavior to be consistent.
+        *
+        * In addition, it's not thread-safe, so we've marked it as
+        * deprecated.
+        */
+       if (pcap_new_api) {
+               snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "pcap_lookupdev() is deprecated and is not supported in programs calling pcap_init()");
+               return (NULL);
+       }
+
+/* disable MSVC's GetVersion() deprecated warning here */
+DIAG_OFF_DEPRECATION
        dwVersion = GetVersion();       /* get the OS version */
-#pragma warning (pop)
+DIAG_ON_DEPRECATION
        dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
 
        if (dwVersion >= 0x80000000 && dwWindowsMajorVersion >= 4) {
@@ -1844,15 +2530,14 @@ pcap_lookupdev(char *errbuf)
 
                if(TAdaptersName == NULL)
                {
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "memory allocation failure");
+                       (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "memory allocation failure");
                        return NULL;
                }
 
                if ( !PacketGetAdapterNames((PTSTR)TAdaptersName,&NameLength) )
                {
-                       pcap_win32_err_to_str(GetLastError(), our_errbuf);
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                               "PacketGetAdapterNames: %s", our_errbuf);
+                       pcap_fmt_errmsg_for_win32_err(errbuf, PCAP_ERRBUF_SIZE,
+                           GetLastError(), "PacketGetAdapterNames");
                        free(TAdaptersName);
                        return NULL;
                }
@@ -1990,59 +2675,48 @@ static const char *pcap_lib_version_string;
  * tree.  Include version.h from that source tree to get the WinPcap/Npcap
  * version.
  *
- * XXX - it'd be nice if we could somehow generate the WinPcap version number
- * when building WinPcap.  (It'd be nice to do so for the packet.dll version
- * number as well.)
+ * XXX - it'd be nice if we could somehow generate the WinPcap/Npcap version
+ * number when building as part of WinPcap/Npcap.  (It'd be nice to do so
+ * for the packet.dll version number as well.)
  */
 #include "../../version.h"
 
 static const char pcap_version_string[] =
        WINPCAP_PRODUCT_NAME " version " WINPCAP_VER_STRING ", based on " PCAP_VERSION_STRING;
-static const char pcap_version_string_packet_dll_fmt[] =
-       WINPCAP_PRODUCT_NAME " version " WINPCAP_VER_STRING " (packet.dll version %s), based on " PCAP_VERSION_STRING;
 
 const char *
 pcap_lib_version(void)
 {
-       char *packet_version_string;
-       size_t full_pcap_version_string_len;
-       char *full_pcap_version_string;
-
        if (pcap_lib_version_string == NULL) {
                /*
                 * Generate the version string.
                 */
-               packet_version_string = PacketGetVersion();
+               const char *packet_version_string = PacketGetVersion();
+
                if (strcmp(WINPCAP_VER_STRING, packet_version_string) == 0) {
                        /*
-                        * WinPcap version string and packet.dll version
-                        * string are the same; just report the WinPcap
+                        * WinPcap/Npcap version string and packet.dll version
+                        * string are the same; just report the WinPcap/Npcap
                         * version.
                         */
                        pcap_lib_version_string = pcap_version_string;
                } else {
                        /*
-                        * WinPcap version string and packet.dll version
+                        * WinPcap/Npcap version string and packet.dll version
                         * string are different; that shouldn't be the
                         * case (the two libraries should come from the
-                        * same version of WinPcap), so we report both
+                        * same version of WinPcap/Npcap), so we report both
                         * versions.
-                        *
-                        * The -2 is for the %s in the format string,
-                        * which will be replaced by packet_version_string.
                         */
-                       full_pcap_version_string_len =
-                           (sizeof pcap_version_string_packet_dll_fmt - 2) +
-                           strlen(packet_version_string);
-                       full_pcap_version_string = malloc(full_pcap_version_string_len);
-                       if (full_pcap_version_string == NULL)
-                               return (NULL);
-                       pcap_snprintf(full_pcap_version_string,
-                           full_pcap_version_string_len,
-                           pcap_version_string_packet_dll_fmt,
-                           packet_version_string);
+                       char *full_pcap_version_string;
+
+                       if (pcap_asprintf(&full_pcap_version_string,
+                           WINPCAP_PRODUCT_NAME " version " WINPCAP_VER_STRING " (packet.dll version %s), based on " PCAP_VERSION_STRING,
+                           packet_version_string) != -1) {
+                               /* Success */
+                               pcap_lib_version_string = full_pcap_version_string;
+                       }
                }
-               pcap_lib_version_string = full_pcap_version_string;
        }
        return (pcap_lib_version_string);
 }
@@ -2053,35 +2727,22 @@ pcap_lib_version(void)
  * libpcap being built for Windows, not as part of a WinPcap/Npcap source
  * tree.
  */
-static const char pcap_version_string_packet_dll_fmt[] =
-       PCAP_VERSION_STRING " (packet.dll version %s)";
 const char *
 pcap_lib_version(void)
 {
-       char *packet_version_string;
-       size_t full_pcap_version_string_len;
-       char *full_pcap_version_string;
-
        if (pcap_lib_version_string == NULL) {
                /*
                 * Generate the version string.  Report the packet.dll
                 * version.
-                *
-                * The -2 is for the %s in the format string, which will
-                * be replaced by packet_version_string.
                 */
-               packet_version_string = PacketGetVersion();
-               full_pcap_version_string_len =
-                   (sizeof pcap_version_string_packet_dll_fmt - 2) +
-                   strlen(packet_version_string);
-               full_pcap_version_string = malloc(full_pcap_version_string_len);
-               if (full_pcap_version_string == NULL)
-                       return (NULL);
-               pcap_snprintf(full_pcap_version_string,
-                   full_pcap_version_string_len,
-                   pcap_version_string_packet_dll_fmt,
-                   packet_version_string);
-               pcap_lib_version_string = full_pcap_version_string;
+               char *full_pcap_version_string;
+
+               if (pcap_asprintf(&full_pcap_version_string,
+                   PCAP_VERSION_STRING " (packet.dll version %s)",
+                   PacketGetVersion()) != -1) {
+                       /* Success */
+                       pcap_lib_version_string = full_pcap_version_string;
+               }
        }
        return (pcap_lib_version_string);
 }