/*
 * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
 * Copyright (c) 2005 - 2010 CACE Technologies, Davis (California)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Politecnico di Torino, CACE Technologies
 * nor the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "pcap-int.h"

#include <airpcap.h>

#include "pcap-airpcap.h"

/* Default size of the buffer we allocate in userland. */
#define	AIRPCAP_DEFAULT_USER_BUFFER_SIZE 256000

/* Default size of the buffer for the AirPcap adapter. */
#define	AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE 1000000

//
// We load the AirPcap DLL dynamically, so that the code will
// work whether you have it installed or not, and there don't
// have to be two different versions of the library, one linked
// to the AirPcap library and one not linked to it.
//
static pcap_code_handle_t airpcap_lib;

typedef PCHAR (*AirpcapGetLastErrorHandler)(PAirpcapHandle);
typedef BOOL (*AirpcapGetDeviceListHandler)(PAirpcapDeviceDescription *, PCHAR);
typedef VOID (*AirpcapFreeDeviceListHandler)(PAirpcapDeviceDescription);
typedef PAirpcapHandle (*AirpcapOpenHandler)(PCHAR, PCHAR);
typedef VOID (*AirpcapCloseHandler)(PAirpcapHandle);
typedef BOOL (*AirpcapSetDeviceMacFlagsHandler)(PAirpcapHandle, UINT);
typedef BOOL (*AirpcapSetLinkTypeHandler)(PAirpcapHandle, AirpcapLinkType);
typedef BOOL (*AirpcapGetLinkTypeHandler)(PAirpcapHandle, PAirpcapLinkType);
typedef BOOL (*AirpcapSetKernelBufferHandler)(PAirpcapHandle, UINT);
typedef BOOL (*AirpcapSetFilterHandler)(PAirpcapHandle, PVOID, UINT);
typedef BOOL (*AirpcapSetMinToCopyHandler)(PAirpcapHandle, UINT);
typedef BOOL (*AirpcapGetReadEventHandler)(PAirpcapHandle, HANDLE *);
typedef BOOL (*AirpcapReadHandler)(PAirpcapHandle, PBYTE, UINT, PUINT);
typedef BOOL (*AirpcapWriteHandler)(PAirpcapHandle, PCHAR, ULONG);
typedef BOOL (*AirpcapGetStatsHandler)(PAirpcapHandle, PAirpcapStats);

static AirpcapGetLastErrorHandler p_AirpcapGetLastError;
static AirpcapGetDeviceListHandler p_AirpcapGetDeviceList;
static AirpcapFreeDeviceListHandler p_AirpcapFreeDeviceList;
static AirpcapOpenHandler p_AirpcapOpen;
static AirpcapCloseHandler p_AirpcapClose;
static AirpcapSetDeviceMacFlagsHandler p_AirpcapSetDeviceMacFlags;
static AirpcapSetLinkTypeHandler p_AirpcapSetLinkType;
static AirpcapGetLinkTypeHandler p_AirpcapGetLinkType;
static AirpcapSetKernelBufferHandler p_AirpcapSetKernelBuffer;
static AirpcapSetFilterHandler p_AirpcapSetFilter;
static AirpcapSetMinToCopyHandler p_AirpcapSetMinToCopy;
static AirpcapGetReadEventHandler p_AirpcapGetReadEvent;
static AirpcapReadHandler p_AirpcapRead;
static AirpcapWriteHandler p_AirpcapWrite;
static AirpcapGetStatsHandler p_AirpcapGetStats;

typedef enum LONG
{
	AIRPCAP_API_UNLOADED = 0,
	AIRPCAP_API_LOADED,
	AIRPCAP_API_CANNOT_LOAD,
	AIRPCAP_API_LOADING
} AIRPCAP_API_LOAD_STATUS;

static AIRPCAP_API_LOAD_STATUS	airpcap_load_status;

/*
 * NOTE: this function should be called by the pcap functions that can
 *       theoretically deal with the AirPcap library for the first time,
 *       namely listing the adapters and creating a pcap_t for an adapter.
 *       All the other ones (activate, close, read, write, set parameters)
 *       work on a pcap_t for an AirPcap device, meaning we've already
 *       created the pcap_t and thus have loaded the functions, so we do
 *       not need to call this function.
 */
static AIRPCAP_API_LOAD_STATUS
load_airpcap_functions(void)
{
	AIRPCAP_API_LOAD_STATUS current_status;

	/*
	 * We don't use a mutex because there's no place that
	 * we can guarantee we'll be called before any threads
	 * other than the main thread exists.  (For example,
	 * this might be a static library, so we can't arrange
	 * to be called by DllMain(), and there's no guarantee
	 * that the application called pcap_init() - which is
	 * supposed to be called only from one thread - so
	 * we can't arrange to be called from it.)
	 *
	 * If nobody's tried to load it yet, mark it as
	 * loading; in any case, return the status before
	 * we modified it.
	 */
	current_status = InterlockedCompareExchange((LONG *)&airpcap_load_status,
	    AIRPCAP_API_LOADING, AIRPCAP_API_UNLOADED);

	/*
	 * If the status was AIRPCAP_API_UNLOADED, we've set it
	 * to AIRPCAP_API_LOADING, because we're going to be
	 * the ones to load the library but current_status is
	 * AIRPCAP_API_UNLOADED.
	 *
	 * if it was AIRPCAP_API_LOADING, meaning somebody else
	 * was trying to load it, spin until they finish and
	 * set the status to a value reflecting whether they
	 * succeeded.
	 */
	while (current_status == AIRPCAP_API_LOADING) {
		current_status = InterlockedCompareExchange((LONG*)&airpcap_load_status,
		    AIRPCAP_API_LOADING, AIRPCAP_API_LOADING);
		Sleep(10);
	}

	/*
	 * At this point, current_status is either:
	 *
	 *	AIRPCAP_API_LOADED, in which case another thread
	 *	loaded the library, so we're done;
	 *
	 *	AIRPCAP_API_CANNOT_LOAD, in which another thread
	 *	tried and failed to load the library, so we're
	 *	done - we won't try it ourselves;
	 *
	 *	AIRPCAP_API_LOADING, in which case *we're* the
	 *	ones loading it, and should now try to do so.
	 */
	if (current_status == AIRPCAP_API_LOADED)
		return AIRPCAP_API_LOADED;

	if (current_status == AIRPCAP_API_CANNOT_LOAD)
		return AIRPCAP_API_CANNOT_LOAD;

	/*
	 * Start out assuming we can't load it.
	 */
	current_status = AIRPCAP_API_CANNOT_LOAD;

	airpcap_lib = pcap_load_code("airpcap.dll");
	if (airpcap_lib != NULL) {
		/*
		 * OK, we've loaded the library; now try to find the
		 * functions we need in it.
		 */
		p_AirpcapGetLastError = (AirpcapGetLastErrorHandler) pcap_find_function(airpcap_lib, "AirpcapGetLastError");
		p_AirpcapGetDeviceList = (AirpcapGetDeviceListHandler) pcap_find_function(airpcap_lib, "AirpcapGetDeviceList");
		p_AirpcapFreeDeviceList = (AirpcapFreeDeviceListHandler) pcap_find_function(airpcap_lib, "AirpcapFreeDeviceList");
		p_AirpcapOpen = (AirpcapOpenHandler) pcap_find_function(airpcap_lib, "AirpcapOpen");
		p_AirpcapClose = (AirpcapCloseHandler) pcap_find_function(airpcap_lib, "AirpcapClose");
		p_AirpcapSetDeviceMacFlags = (AirpcapSetDeviceMacFlagsHandler) pcap_find_function(airpcap_lib, "AirpcapSetDeviceMacFlags");
		p_AirpcapSetLinkType = (AirpcapSetLinkTypeHandler) pcap_find_function(airpcap_lib, "AirpcapSetLinkType");
		p_AirpcapGetLinkType = (AirpcapGetLinkTypeHandler) pcap_find_function(airpcap_lib, "AirpcapGetLinkType");
		p_AirpcapSetKernelBuffer = (AirpcapSetKernelBufferHandler) pcap_find_function(airpcap_lib, "AirpcapSetKernelBuffer");
		p_AirpcapSetFilter = (AirpcapSetFilterHandler) pcap_find_function(airpcap_lib, "AirpcapSetFilter");
		p_AirpcapSetMinToCopy = (AirpcapSetMinToCopyHandler) pcap_find_function(airpcap_lib, "AirpcapSetMinToCopy");
		p_AirpcapGetReadEvent = (AirpcapGetReadEventHandler) pcap_find_function(airpcap_lib, "AirpcapGetReadEvent");
		p_AirpcapRead = (AirpcapReadHandler) pcap_find_function(airpcap_lib, "AirpcapRead");
		p_AirpcapWrite = (AirpcapWriteHandler) pcap_find_function(airpcap_lib, "AirpcapWrite");
		p_AirpcapGetStats = (AirpcapGetStatsHandler) pcap_find_function(airpcap_lib, "AirpcapGetStats");

		//
		// Make sure that we found everything
		//
		if (p_AirpcapGetLastError != NULL &&
		    p_AirpcapGetDeviceList != NULL &&
		    p_AirpcapFreeDeviceList != NULL &&
		    p_AirpcapOpen != NULL &&
		    p_AirpcapClose != NULL &&
		    p_AirpcapSetDeviceMacFlags != NULL &&
		    p_AirpcapSetLinkType != NULL &&
		    p_AirpcapGetLinkType != NULL &&
		    p_AirpcapSetKernelBuffer != NULL &&
		    p_AirpcapSetFilter != NULL &&
		    p_AirpcapSetMinToCopy != NULL &&
		    p_AirpcapGetReadEvent != NULL &&
		    p_AirpcapRead != NULL &&
		    p_AirpcapWrite != NULL &&
		    p_AirpcapGetStats != NULL) {
			/*
			 * We have all we need.
			 */
			current_status = AIRPCAP_API_LOADED;
		}
	}

	if (current_status != AIRPCAP_API_LOADED) {
		/*
		 * We failed; if we found the DLL, close the
		 * handle for it.
		 */
		if (airpcap_lib != NULL) {
			FreeLibrary(airpcap_lib);
			airpcap_lib = NULL;
		}
	}

	/*
	 * Now set the status appropriately - and atomically.
	 */
	InterlockedExchange((LONG *)&airpcap_load_status, current_status);

	return current_status;
}

/*
 * Private data for capturing on AirPcap devices.
 */
struct pcap_airpcap {
	PAirpcapHandle adapter;
	int filtering_in_kernel;
	int nonblock;
	int read_timeout;
	HANDLE read_event;
	struct pcap_stat stat;
};

static int
airpcap_setfilter(pcap_t *p, struct bpf_program *fp)
{
	struct pcap_airpcap *pa = p->priv;

	if (!p_AirpcapSetFilter(pa->adapter, fp->bf_insns,
	    fp->bf_len * sizeof(struct bpf_insn))) {
		/*
		 * Kernel filter not installed.
		 *
		 * XXX - we don't know whether this failed because:
		 *
		 *  the kernel rejected the filter program as invalid,
		 *  in which case we should fall back on userland
		 *  filtering;
		 *
		 *  the kernel rejected the filter program as too big,
		 *  in which case we should again fall back on
		 *  userland filtering;
		 *
		 *  there was some other problem, in which case we
		 *  should probably report an error;
		 *
		 * So we just fall back on userland filtering in
		 * all cases.
		 */

		/*
		 * install_bpf_program() validates the program.
		 *
		 * XXX - what if we already have a filter in the kernel?
		 */
		if (install_bpf_program(p, fp) < 0)
			return (-1);
		pa->filtering_in_kernel = 0;	/* filtering in userland */
		return (0);
	}

	/*
	 * It worked.
	 */
	pa->filtering_in_kernel = 1;	/* filtering in the kernel */

	/*
	 * Discard any previously-received packets, as they might have
	 * passed whatever filter was formerly in effect, but might
	 * not pass this filter (BIOCSETF discards packets buffered
	 * in the kernel, so you can lose packets in any case).
	 */
	p->cc = 0;
	return (0);
}

static int
airpcap_set_datalink(pcap_t *p, int dlt)
{
	struct pcap_airpcap *pa = p->priv;
	AirpcapLinkType type;

	switch (dlt) {

	case DLT_IEEE802_11_RADIO:
		type = AIRPCAP_LT_802_11_PLUS_RADIO;
		break;

	case DLT_PPI:
		type = AIRPCAP_LT_802_11_PLUS_PPI;
		break;

	case DLT_IEEE802_11:
		type = AIRPCAP_LT_802_11;
		break;

	default:
		/* This can't happen; just return. */
		return (0);
	}
	if (!p_AirpcapSetLinkType(pa->adapter, type)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapSetLinkType() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (-1);
	}
	p->linktype = dlt;
	return (0);
}

static int
airpcap_getnonblock(pcap_t *p)
{
	struct pcap_airpcap *pa = p->priv;

	return (pa->nonblock);
}

static int
airpcap_setnonblock(pcap_t *p, int nonblock)
{
	struct pcap_airpcap *pa = p->priv;
	int newtimeout;

	if (nonblock) {
		/*
		 * Set the packet buffer timeout to -1 for non-blocking
		 * mode.
		 */
		newtimeout = -1;
	} else {
		/*
		 * Restore the timeout set when the device was opened.
		 * (Note that this may be -1, in which case we're not
		 * really leaving non-blocking mode.  However, although
		 * the timeout argument to pcap_set_timeout() and
		 * pcap_open_live() is an int, you're not supposed to
		 * supply a negative value, so that "shouldn't happen".)
		 */
		newtimeout = p->opt.timeout;
	}
	pa->read_timeout = newtimeout;
	pa->nonblock = (newtimeout == -1);
	return (0);
}

static int
airpcap_stats(pcap_t *p, struct pcap_stat *ps)
{
	struct pcap_airpcap *pa = p->priv;
	AirpcapStats tas;

	/*
	 * Try to get statistics.
	 */
	if (!p_AirpcapGetStats(pa->adapter, &tas)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapGetStats() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (-1);
	}

	ps->ps_drop = tas.Drops;
	ps->ps_recv = tas.Recvs;
	ps->ps_ifdrop = tas.IfDrops;

	return (0);
}

/*
 * Win32-only routine for getting statistics.
 *
 * This way is definitely safer than passing the pcap_stat * from the userland.
 * In fact, there could happen than the user allocates a variable which is not
 * big enough for the new structure, and the library will write in a zone
 * which is not allocated to this variable.
 *
 * In this way, we're pretty sure we are writing on memory allocated to this
 * variable.
 *
 * XXX - but this is the wrong way to handle statistics.  Instead, we should
 * have an API that returns data in a form like the Options section of a
 * pcapng Interface Statistics Block:
 *
 *    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.
 */
static struct pcap_stat *
airpcap_stats_ex(pcap_t *p, int *pcap_stat_size)
{
	struct pcap_airpcap *pa = p->priv;
	AirpcapStats tas;

	*pcap_stat_size = sizeof (p->stat);

	/*
	 * Try to get statistics.
	 */
	if (!p_AirpcapGetStats(pa->adapter, &tas)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapGetStats() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (NULL);
	}

	p->stat.ps_recv = tas.Recvs;
	p->stat.ps_drop = tas.Drops;
	p->stat.ps_ifdrop = tas.IfDrops;
	/*
	 * Just in case this is ever compiled for a target other than
	 * Windows, which is extremely unlikely at best.
	 */
#ifdef _WIN32
	p->stat.ps_capt = tas.Capt;
#endif
	return (&p->stat);
}

/* Set the dimension of the kernel-level capture buffer */
static int
airpcap_setbuff(pcap_t *p, int dim)
{
	struct pcap_airpcap *pa = p->priv;

	if (!p_AirpcapSetKernelBuffer(pa->adapter, dim)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapSetKernelBuffer() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (-1);
	}
	return (0);
}

/* Set the driver working mode */
static int
airpcap_setmode(pcap_t *p, int mode)
{
	 if (mode != MODE_CAPT) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "Only MODE_CAPT is supported on an AirPcap adapter");
		return (-1);
	 }
	 return (0);
}

/*set the minimum amount of data that will release a read call*/
static int
airpcap_setmintocopy(pcap_t *p, int size)
{
	struct pcap_airpcap *pa = p->priv;

	if (!p_AirpcapSetMinToCopy(pa->adapter, size)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapSetMinToCopy() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (-1);
	}
	return (0);
}

static HANDLE
airpcap_getevent(pcap_t *p)
{
	struct pcap_airpcap *pa = p->priv;

	return (pa->read_event);
}

static int
airpcap_oid_get_request(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_,
    size_t *lenp _U_)
{
	snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
	    "Getting OID values is not supported on an AirPcap adapter");
	return (PCAP_ERROR);
}

static int
airpcap_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,
	    "Setting OID values is not supported on an AirPcap adapter");
	return (PCAP_ERROR);
}

static u_int
airpcap_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_)
{
	snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
	    "Cannot queue packets for transmission on an AirPcap adapter");
	return (0);
}

static int
airpcap_setuserbuffer(pcap_t *p, int size)
{
	unsigned char *new_buff;

	if (size <= 0) {
		/* Bogus parameter */
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "Error: invalid size %d",size);
		return (-1);
	}

	/* Allocate the buffer */
	new_buff = (unsigned char *)malloc(sizeof(char)*size);

	if (!new_buff) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "Error: not enough memory");
		return (-1);
	}

	free(p->buffer);

	p->buffer = new_buff;
	p->bufsize = size;

	return (0);
}

static int
airpcap_live_dump(pcap_t *p, char *filename _U_, int maxsize _U_,
    int maxpacks _U_)
{
	snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
	    "AirPcap adapters don't support live dump");
	return (-1);
}

static int
airpcap_live_dump_ended(pcap_t *p, int sync _U_)
{
	snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
	    "AirPcap adapters don't support live dump");
	return (-1);
}

static PAirpcapHandle
airpcap_get_airpcap_handle(pcap_t *p)
{
	struct pcap_airpcap *pa = p->priv;

	return (pa->adapter);
}

static int
airpcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
	struct pcap_airpcap *pa = p->priv;
	int cc;
	int n;
	register u_char *bp, *ep;
	UINT bytes_read;
	u_char *datap;

	cc = p->cc;
	if (cc == 0) {
		/*
		 * Has "pcap_breakloop()" been called?
		 */
		if (p->break_loop) {
			/*
			 * Yes - clear the flag that indicates that it
			 * has, and return PCAP_ERROR_BREAK to indicate
			 * that we were told to break out of the loop.
			 */
			p->break_loop = 0;
			return (PCAP_ERROR_BREAK);
		}

		//
		// If we're not in non-blocking mode, wait for data to
		// arrive.
		//
		if (pa->read_timeout != -1) {
			WaitForSingleObject(pa->read_event,
			    (pa->read_timeout ==0 )? INFINITE: pa->read_timeout);
		}

		//
		// Read the data.
		// p_AirpcapRead doesn't block.
		//
		if (!p_AirpcapRead(pa->adapter, (PBYTE)p->buffer,
		    p->bufsize, &bytes_read)) {
			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
			    "AirpcapRead() failed: %s",
			    p_AirpcapGetLastError(pa->adapter));
			return (-1);
		}
		cc = bytes_read;
		bp = (u_char *)p->buffer;
	} else
		bp = p->bp;

	/*
	 * 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 ((AirpcapBpfHeader *)bp)
	n = 0;
	ep = bp + cc;
	for (;;) {
		register u_int caplen, hdrlen;

		/*
		 * Has "pcap_breakloop()" been called?
		 * If so, return immediately - if we haven't read any
		 * packets, clear the flag and return PCAP_ERROR_BREAK
		 * to indicate that we were told to break out of the loop,
		 * otherwise leave the flag set, so that the *next* call
		 * will break out of the loop without having read any
		 * packets, and return the number of packets we've
		 * processed so far.
		 */
		if (p->break_loop) {
			if (n == 0) {
				p->break_loop = 0;
				return (PCAP_ERROR_BREAK);
			} else {
				p->bp = bp;
				p->cc = (int) (ep - bp);
				return (n);
			}
		}
		if (bp >= ep)
			break;

		caplen = bhp->Caplen;
		hdrlen = bhp->Hdrlen;
		datap = bp + hdrlen;
		/*
		 * Short-circuit evaluation: if using BPF filter
		 * in the AirPcap adapter, no need to do it now -
		 * we already know the packet passed the filter.
		 */
		if (pa->filtering_in_kernel ||
		    p->fcode.bf_insns == NULL ||
		    pcap_filter(p->fcode.bf_insns, datap, bhp->Originallen, caplen)) {
			struct pcap_pkthdr pkthdr;

			pkthdr.ts.tv_sec = bhp->TsSec;
			pkthdr.ts.tv_usec = bhp->TsUsec;
			pkthdr.caplen = caplen;
			pkthdr.len = bhp->Originallen;
			(*callback)(user, &pkthdr, datap);
			bp += AIRPCAP_WORDALIGN(caplen + hdrlen);
			if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) {
				p->bp = bp;
				p->cc = (int)(ep - bp);
				return (n);
			}
		} else {
			/*
			 * Skip this packet.
			 */
			bp += AIRPCAP_WORDALIGN(caplen + hdrlen);
		}
	}
#undef bhp
	p->cc = 0;
	return (n);
}

static int
airpcap_inject(pcap_t *p, const void *buf, int size)
{
	struct pcap_airpcap *pa = p->priv;

	/*
	 * XXX - the second argument to AirpcapWrite() *should* have
	 * been declared as a const pointer - a write function that
	 * stomps on what it writes is *extremely* rude - but such
	 * is life.  We assume it is, in fact, not going to write on
	 * our buffer.
	 */
	if (!p_AirpcapWrite(pa->adapter, (void *)buf, size)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapWrite() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (-1);
	}

	/*
	 * We assume it all got sent if "AirpcapWrite()" succeeded.
	 * "pcap_inject()" is expected to return the number of bytes
	 * sent.
	 */
	return (size);
}

static void
airpcap_cleanup(pcap_t *p)
{
	struct pcap_airpcap *pa = p->priv;

	if (pa->adapter != NULL) {
		p_AirpcapClose(pa->adapter);
		pa->adapter = NULL;
	}
	pcap_cleanup_live_common(p);
}

static void
airpcap_breakloop(pcap_t *p)
{
	HANDLE read_event;

	pcap_breakloop_common(p);
	struct pcap_airpcap *pa = p->priv;

	/* XXX - what if either of these fail? */
	/*
	 * XXX - will SetEvent() force a wakeup and, if so, will
	 * the AirPcap read code handle that sanely?
	 */
	if (!p_AirpcapGetReadEvent(pa->adapter, &read_event))
		return;
	SetEvent(read_event);
}

static int
airpcap_activate(pcap_t *p)
{
	struct pcap_airpcap *pa = p->priv;
	char *device = p->opt.device;
	char airpcap_errbuf[AIRPCAP_ERRBUF_SIZE];
	BOOL status;
	AirpcapLinkType link_type;

	pa->adapter = p_AirpcapOpen(device, airpcap_errbuf);
	if (pa->adapter == NULL) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", airpcap_errbuf);
		return (PCAP_ERROR);
	}

	/*
	 * Set monitor mode appropriately.
	 * Always turn off the "ACK frames sent to the card" mode.
	 */
	if (p->opt.rfmon) {
		status = p_AirpcapSetDeviceMacFlags(pa->adapter,
		    AIRPCAP_MF_MONITOR_MODE_ON);
	} else
		status = p_AirpcapSetDeviceMacFlags(pa->adapter,
		    AIRPCAP_MF_ACK_FRAMES_ON);
	if (!status) {
		p_AirpcapClose(pa->adapter);
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapSetDeviceMacFlags() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		return (PCAP_ERROR);
	}

	/*
	 * Turn a negative snapshot value (invalid), a snapshot value of
	 * 0 (unspecified), or a value bigger than the normal maximum
	 * value, into the maximum allowed value.
	 *
	 * If some application really *needs* a bigger snapshot
	 * length, we should just increase MAXIMUM_SNAPLEN.
	 */
	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
		p->snapshot = MAXIMUM_SNAPLEN;

	/*
	 * If the buffer size wasn't explicitly set, default to
	 * AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE.
	 */
	if (p->opt.buffer_size == 0)
		p->opt.buffer_size = AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE;

	if (!p_AirpcapSetKernelBuffer(pa->adapter, p->opt.buffer_size)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapSetKernelBuffer() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		goto bad;
	}

	if(!p_AirpcapGetReadEvent(pa->adapter, &pa->read_event)) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapGetReadEvent() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		goto bad;
	}

	/* Set the buffer size */
	p->bufsize = AIRPCAP_DEFAULT_USER_BUFFER_SIZE;
	p->buffer = malloc(p->bufsize);
	if (p->buffer == NULL) {
		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
		    errno, "malloc");
		goto bad;
	}

	if (p->opt.immediate) {
		/* Tell the driver to copy the buffer as soon as data arrives. */
		if (!p_AirpcapSetMinToCopy(pa->adapter, 0)) {
			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
			    "AirpcapSetMinToCopy() failed: %s",
			    p_AirpcapGetLastError(pa->adapter));
			goto bad;
		}
	} else {
		/*
		 * Tell the driver to copy the buffer only if it contains
		 * at least 16K.
		 */
		if (!p_AirpcapSetMinToCopy(pa->adapter, 16000)) {
			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
			    "AirpcapSetMinToCopy() failed: %s",
			    p_AirpcapGetLastError(pa->adapter));
			goto bad;
		}
	}

	/*
	 * Find out what the default link-layer header type is,
	 * and set p->datalink to that.
	 *
	 * We don't force it to another value because there
	 * might be some programs using WinPcap/Npcap that,
	 * when capturing on AirPcap devices, assume the
	 * default value set with the AirPcap configuration
	 * program is what you get.
	 *
	 * The out-of-the-box default appears to be radiotap.
	 */
	if (!p_AirpcapGetLinkType(pa->adapter, &link_type)) {
		/* That failed. */
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapGetLinkType() failed: %s",
		    p_AirpcapGetLastError(pa->adapter));
		goto bad;
	}
	switch (link_type) {

	case AIRPCAP_LT_802_11_PLUS_RADIO:
		p->linktype = DLT_IEEE802_11_RADIO;
		break;

	case AIRPCAP_LT_802_11_PLUS_PPI:
		p->linktype = DLT_PPI;
		break;

	case AIRPCAP_LT_802_11:
		p->linktype = DLT_IEEE802_11;
		break;

	case AIRPCAP_LT_UNKNOWN:
	default:
		/* OK, what? */
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapGetLinkType() returned unknown link type %u",
		    link_type);
		goto bad;
	}

	/*
	 * Now provide a list of all the supported types; we
	 * assume they all work.  We put radiotap at the top,
	 * followed by PPI, followed by "no radio metadata".
	 */
	p->dlt_list = (u_int *) malloc(sizeof(u_int) * 3);
	if (p->dlt_list == NULL)
		goto bad;
	p->dlt_list[0] = DLT_IEEE802_11_RADIO;
	p->dlt_list[1] = DLT_PPI;
	p->dlt_list[2] = DLT_IEEE802_11;
	p->dlt_count = 3;

	p->read_op = airpcap_read;
	p->inject_op = airpcap_inject;
	p->setfilter_op = airpcap_setfilter;
	p->setdirection_op = NULL;	/* Not implemented. */
	p->set_datalink_op = airpcap_set_datalink;
	p->getnonblock_op = airpcap_getnonblock;
	p->setnonblock_op = airpcap_setnonblock;
	p->breakloop_op = airpcap_breakloop;
	p->stats_op = airpcap_stats;
	p->stats_ex_op = airpcap_stats_ex;
	p->setbuff_op = airpcap_setbuff;
	p->setmode_op = airpcap_setmode;
	p->setmintocopy_op = airpcap_setmintocopy;
	p->getevent_op = airpcap_getevent;
	p->oid_get_request_op = airpcap_oid_get_request;
	p->oid_set_request_op = airpcap_oid_set_request;
	p->sendqueue_transmit_op = airpcap_sendqueue_transmit;
	p->setuserbuffer_op = airpcap_setuserbuffer;
	p->live_dump_op = airpcap_live_dump;
	p->live_dump_ended_op = airpcap_live_dump_ended;
	p->get_airpcap_handle_op = airpcap_get_airpcap_handle;
	p->cleanup_op = airpcap_cleanup;

	return (0);
 bad:
	airpcap_cleanup(p);
	return (PCAP_ERROR);
}

/*
 * Monitor mode is supported.
 */
static int
airpcap_can_set_rfmon(pcap_t *p)
{
	return (1);
}

int
device_is_airpcap(const char *device, char *ebuf)
{
	static const char airpcap_prefix[] = "\\\\.\\airpcap";

	/*
	 * We don't determine this by calling AirpcapGetDeviceList()
	 * and looking at the list, as that appears to be a costly
	 * operation.
	 *
	 * Instead, we just check whether it begins with "\\.\airpcap".
	 */
	if (strncmp(device, airpcap_prefix, sizeof airpcap_prefix - 1) == 0) {
		/*
		 * Yes, it's an AirPcap device.
		 */
		return (1);
	}

	/*
	 * No, it's not an AirPcap device.
	 */
	return (0);
}

pcap_t *
airpcap_create(const char *device, char *ebuf, int *is_ours)
{
	int ret;
	pcap_t *p;

	/*
	 * This can be called before we've tried loading the library,
	 * so do so if we haven't already tried to do so.
	 */
	if (load_airpcap_functions() != AIRPCAP_API_LOADED) {
		/*
		 * We assume this means that we don't have the AirPcap
		 * software installed, which probably means we don't
		 * have an AirPcap device.
		 *
		 * Don't treat that as an error.
		 */
		*is_ours = 0;
		return (NULL);
	}

	/*
	 * Is this an AirPcap device?
	 */
	ret = device_is_airpcap(device, ebuf);
	if (ret == 0) {
		/* No. */
		*is_ours = 0;
		return (NULL);
	}

	/*
	 * Yes.
	 */
	*is_ours = 1;
	p = PCAP_CREATE_COMMON(ebuf, struct pcap_airpcap);
	if (p == NULL)
		return (NULL);

	p->activate_op = airpcap_activate;
	p->can_set_rfmon_op = airpcap_can_set_rfmon;
	return (p);
}

/*
 * Add all AirPcap devices.
 */
int
airpcap_findalldevs(pcap_if_list_t *devlistp, char *errbuf)
{
	AirpcapDeviceDescription *airpcap_devices, *airpcap_device;
	char airpcap_errbuf[AIRPCAP_ERRBUF_SIZE];

	/*
	 * This can be called before we've tried loading the library,
	 * so do so if we haven't already tried to do so.
	 */
	if (load_airpcap_functions() != AIRPCAP_API_LOADED) {
		/*
		 * XXX - unless the error is "no such DLL", report this
		 * as an error rather than as "no AirPcap devices"?
		 */
		return (0);
	}

	if (!p_AirpcapGetDeviceList(&airpcap_devices, airpcap_errbuf)) {
		snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "AirpcapGetDeviceList() failed: %s", airpcap_errbuf);
		return (-1);
	}

	for (airpcap_device = airpcap_devices; airpcap_device != NULL;
	    airpcap_device = airpcap_device->next) {
		if (add_dev(devlistp, airpcap_device->Name, 0,
		    airpcap_device->Description, errbuf) == NULL) {
			/*
			 * Failure.
			 */
			p_AirpcapFreeDeviceList(airpcap_devices);
			return (-1);
		}
	}
	p_AirpcapFreeDeviceList(airpcap_devices);
	return (0);
}
