From: Guy Harris Date: Fri, 8 May 2020 03:11:06 +0000 (-0700) Subject: Support AirPcap devices with a pcap module. X-Git-Tag: libpcap-1.10-bp~203 X-Git-Url: https://git.tcpdump.org/libpcap/commitdiff_plain/f32e357d40aba5464a3a31638921be5591b8e73e Support AirPcap devices with a pcap module. This allows us to update the support for newer APIs in the future - and in the present, with pcap_set_datalink() - and would allow Npcap to remove its AirPcap support. Add another test program, while we're at it. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index c2d84782..df3071e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,6 +185,7 @@ endif(WIN32) option(BUILD_SHARED_LIBS "Build shared libraries" ON) if(WIN32) set(Packet_ROOT "" CACHE PATH "Path to directory with include and lib subdirectories for packet.dll") + set(AirPcap_ROOT "" CACHE PATH "Path to directory with include and lib subdirectories for airpcap.dll") endif(WIN32) option(ENABLE_PROFILING "Enable code profiling" OFF) @@ -1794,6 +1795,27 @@ if(NOT DISABLE_SNF) endif() endif() +# Check for Riverbed AirPcap support. +if(NOT DISABLE_AIRPCAP) + # + # Try to find the AirPcap header file and library. + # + find_package(AirPcap) + + # + # Did we succeed? + # + if(AIRPCAP_FOUND) + # + # Yes. + # + include_directories(AFTER ${AIRPCAP_INCLUDE_DIRS}) + set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-airpcap.c) + set(HAVE_AIRPCAP_API TRUE) + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${AIRPCAP_LIBRARIES}) + endif() +endif() + # Check for Riverbed TurboCap support. if(NOT DISABLE_TC) # @@ -1811,7 +1833,7 @@ if(NOT DISABLE_TC) include_directories(AFTER ${TC_INCLUDE_DIRS}) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-tc.c) set(HAVE_TC_API TRUE) - set(PCAP_LINK_LIBRARIES "${PCAP_LINK_LIBRARIES} ${TC_LIBRARIES} ${CMAKE_USE_PTHREADS_INIT} stdc++") + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${TC_LIBRARIES} ${CMAKE_USE_PTHREADS_INIT} stdc++) endif() endif() diff --git a/Makefile.in b/Makefile.in index 0cdca03d..49d8b4c1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -310,6 +310,8 @@ EXTRA_DIST = \ msdos/readme.dos \ nomkdep \ org.tcpdump.chmod_bpf.plist \ + pcap-airpcap.c \ + pcap-airpcap.h \ pcap-bpf.c \ pcap-bt-linux.c \ pcap-bt-linux.h \ diff --git a/cmake/Modules/FindAirPcap.cmake b/cmake/Modules/FindAirPcap.cmake new file mode 100644 index 00000000..8198f70f --- /dev/null +++ b/cmake/Modules/FindAirPcap.cmake @@ -0,0 +1,69 @@ +# +# FindAirPcap +# ========== +# +# Find the AirPcap library and include files. +# +# This module defines the following variables: +# +# AIRPCAP_INCLUDE_DIR - absolute path to the directory containing airpcap.h. +# +# AIRPCAP_LIBRARY - relative or absolute path to the AirPcap library to +# link with. An absolute path is will be used if the +# AirPcap library is not located in the compiler's +# default search path. + +# AIRPCAP_FOUND - TRUE if the AirPcap library *and* header are found. +# +# Hints and Backward Compatibility +# ================================ +# +# To tell this module where to look, a user may set the environment variable +# AirPcap_ROOT to point cmake to the *root* of a directory with include and +# lib subdirectories for airpcap.dll (e.g Airpcap_Devpack). +# Alternatively, AirPcap_ROOT may also be set from the CMake command +# line or GUI (e.g cmake -DAirPcap_ROOT=C:\path\to\airpcap_sdk [...]) +# + +# The 64-bit airpcap.lib is located under /x64 +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # + # For the WinPcap and Npcap SDKs, the Lib subdirectory of the top-level + # directory contains 32-bit libraries; the 64-bit libraries are in the + # Lib/x64 directory. + # + # The only way to *FORCE* CMake to look in the Lib/x64 directory + # without searching in the Lib directory first appears to be to set + # CMAKE_LIBRARY_ARCHITECTURE to "x64". + # + # In newer versions of CMake, CMAKE_LIBRARY_ARCHITECTURE is set according to + # the language, e.g., CMAKE__LIBRARY_ARCHITECTURE. So, set the new + # variable, CMAKE_C_LIBRARY_ARCHITECTURE, so that CMAKE_LIBRARY_ARCHITECTURE + # inherits the correct value. + # + set(CMAKE_C_LIBRARY_ARCHITECTURE "x64") + set(CMAKE_LIBRARY_ARCHITECTURE "x64") +endif() + +# Find the header +find_path(AIRPCAP_INCLUDE_DIR airpcap.h + PATH_SUFFIXES include +) + +# Find the library +find_library(AIRPCAP_LIBRARY + NAMES airpcap +) + +# Set AIRPCAP_FOUND to TRUE if AIRPCAP_INCLUDE_DIR and AIRPCAP_LIBRARY are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AIRPCAP + DEFAULT_MSG + AIRPCAP_INCLUDE_DIR + AIRPCAP_LIBRARY +) + +mark_as_advanced(AIRPCAP_INCLUDE_DIR AIRPCAP_LIBRARY) + +set(AIRPCAP_INCLUDE_DIRS ${AIRPCAP_INCLUDE_DIR}) +set(AIRPCAP_LIBRARIES ${AIRPCAP_LIBRARY}) diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 84fb42ae..074a7be5 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -15,6 +15,9 @@ /* define if we have the AIX getprotobyname_r() */ #cmakedefine HAVE_AIX_GETPROTOBYNAME_R 1 +/* define if you have the AirPcap API */ +#cmakedefine HAVE_AIRPCAP_API 1 + /* Define to 1 if you have the `asprintf' function. */ #cmakedefine HAVE_ASPRINTF 1 diff --git a/pcap-airpcap.c b/pcap-airpcap.c new file mode 100644 index 00000000..a9ef47de --- /dev/null +++ b/pcap-airpcap.c @@ -0,0 +1,984 @@ +/* + * 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 +#endif + +#include "pcap-int.h" + +#include + +#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 (*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 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_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_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; +#ifdef ENABLE_REMOTE + 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, (char *)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. + */ +#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) { + AirpcapClose(pa->adapter); + pa->adapter = NULL; + } + pcap_cleanup_live_common(p); +} + +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; + + 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 = AirpcapSetDeviceMacFlags(pa->adapter, + AIRPCAP_MF_MONITOR_MODE_ON); + } else + status = AirpcapSetDeviceMacFlags(pa->adapter, + AIRPCAP_MF_ACK_FRAMES_ON); + if (!status) { + 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 (p->snapshot < 96) + /* + * NIT requires a snapshot length of at least 96. + */ + p->snapshot = 96; + + /* + * 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; + } + } + + /* + * AirPcap devices support Radiotap, PPI, and raw 802.11. + */ + p->linktype = DLT_IEEE802_11_RADIO; + p->dlt_list = (u_int *) malloc(sizeof(u_int) * 3); + if (p->dlt_list == NULL) + goto bad; + p->dlt_count = 3; + p->dlt_list[0] = DLT_IEEE802_11_RADIO; + p->dlt_list[1] = DLT_PPI; + p->dlt_list[2] = DLT_IEEE802_11; + + 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->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) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "Couldn't load AirPcap DLL\n"); + return (NULL); + } + + /* + * Is this an AirPcap device? + */ + ret = device_is_airpcap(device, ebuf); + if (ret == -1) { + /* Error. */ + return (NULL); + } + if (ret == 0) { + /* No. */ + *is_ours = 0; + return (NULL); + } + + /* + * Yes. + */ + *is_ours = 1; + p = pcap_create_common(ebuf, sizeof (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", 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. + */ + AirpcapFreeDeviceList(airpcap_devices); + return (-1); + } + } + AirpcapFreeDeviceList(airpcap_devices); + return (0); +} diff --git a/pcap-airpcap.h b/pcap-airpcap.h new file mode 100644 index 00000000..aa1164ed --- /dev/null +++ b/pcap-airpcap.h @@ -0,0 +1,36 @@ +/* + * 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. + * + */ + +pcap_t *airpcap_create(const char *, char *, int *); +int airpcap_findalldevs(pcap_if_list_t *devlistp, char *errbuf); +int device_is_airpcap(const char *device, char *ebuf); diff --git a/pcap-int.h b/pcap-int.h index f07dc22f..d67decef 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -545,6 +545,17 @@ FILE *charset_fopen(const char *path, const char *mode); #define charset_fopen(path, mode) fopen((path), (mode)) #endif +/* + * Internal interfaces for loading code at run time. + */ +#ifdef _WIN32 +#define pcap_code_handle_t HMODULE +#define pcap_funcptr_t FARPROC + +pcap_code_handle_t pcap_load_code(const char *); +pcap_funcptr_t pcap_find_function(pcap_code_handle_t, const char *); +#endif + /* * Internal interfaces for doing user-mode filtering of packets and * validating filter programs. diff --git a/pcap-npf.c b/pcap-npf.c index 407d64a0..e9c7226f 100644 --- a/pcap-npf.c +++ b/pcap-npf.c @@ -57,6 +57,8 @@ #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 *); @@ -1838,6 +1840,18 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) name = &AdaptersName[0]; while (*name != '\0') { bpf_u_int32 flags = 0; + + /* + * 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; + } + #ifdef HAVE_PACKET_IS_LOOPBACK_ADAPTER /* * Is this a loopback interface? diff --git a/pcap-tc.c b/pcap-tc.c index 16744335..ef409d32 100644 --- a/pcap-tc.c +++ b/pcap-tc.c @@ -252,58 +252,6 @@ typedef struct _PPI_HEADER #pragma pack(pop) #ifdef _WIN32 -// -// This wrapper around loadlibrary appends the system folder (usually c:\windows\system32) -// to the relative path of the DLL, so that the DLL is always loaded from an absolute path -// (It's no longer possible to load airpcap.dll from the application folder). -// This solves the DLL Hijacking issue discovered in August 2010 -// http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html -// -HMODULE LoadLibrarySafe(LPCTSTR lpFileName) -{ - TCHAR path[MAX_PATH]; - TCHAR fullFileName[MAX_PATH]; - UINT res; - HMODULE hModule = NULL; - do - { - res = GetSystemDirectory(path, MAX_PATH); - - if (res == 0) - { - // - // some bad failure occurred; - // - break; - } - - if (res > MAX_PATH) - { - // - // the buffer was not big enough - // - SetLastError(ERROR_INSUFFICIENT_BUFFER); - break; - } - - if (res + 1 + _tcslen(lpFileName) + 1 < MAX_PATH) - { - memcpy(fullFileName, path, res * sizeof(TCHAR)); - fullFileName[res] = _T('\\'); - memcpy(&fullFileName[res + 1], lpFileName, (_tcslen(lpFileName) + 1) * sizeof(TCHAR)); - - hModule = LoadLibrary(fullFileName); - } - else - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - } - - }while(FALSE); - - return hModule; -} - /* * NOTE: this function should be called by the pcap functions that can theoretically * deal with the Tc library for the first time, namely listing the adapters and @@ -340,34 +288,34 @@ TC_API_LOAD_STATUS LoadTcFunctions(void) currentStatus = TC_API_CANNOT_LOAD; - g_TcFunctions.hTcApiDllHandle = LoadLibrarySafe("TcApi.dll"); + g_TcFunctions.hTcApiDllHandle = pcap_load_code("TcApi.dll"); if (g_TcFunctions.hTcApiDllHandle == NULL) break; - g_TcFunctions.QueryPortList = (TcFcnQueryPortList) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList"); - g_TcFunctions.FreePortList = (TcFcnFreePortList) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcFreePortList"); + g_TcFunctions.QueryPortList = (TcFcnQueryPortList) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList"); + g_TcFunctions.FreePortList = (TcFcnFreePortList) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcFreePortList"); - g_TcFunctions.StatusGetString = (TcFcnStatusGetString) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString"); + g_TcFunctions.StatusGetString = (TcFcnStatusGetString) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString"); - g_TcFunctions.PortGetName = (TcFcnPortGetName) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetName"); - g_TcFunctions.PortGetDescription = (TcFcnPortGetDescription) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription"); + g_TcFunctions.PortGetName = (TcFcnPortGetName) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPortGetName"); + g_TcFunctions.PortGetDescription = (TcFcnPortGetDescription) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription"); - g_TcFunctions.InstanceOpenByName = (TcFcnInstanceOpenByName) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName"); - g_TcFunctions.InstanceClose = (TcFcnInstanceClose) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose"); - g_TcFunctions.InstanceSetFeature = (TcFcnInstanceSetFeature) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature"); - g_TcFunctions.InstanceQueryFeature = (TcFcnInstanceQueryFeature) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature"); - g_TcFunctions.InstanceReceivePackets = (TcFcnInstanceReceivePackets) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets"); - g_TcFunctions.InstanceGetReceiveWaitHandle = (TcFcnInstanceGetReceiveWaitHandle)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle"); - g_TcFunctions.InstanceTransmitPackets = (TcFcnInstanceTransmitPackets)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets"); - g_TcFunctions.InstanceQueryStatistics = (TcFcnInstanceQueryStatistics)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics"); + g_TcFunctions.InstanceOpenByName = (TcFcnInstanceOpenByName) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName"); + g_TcFunctions.InstanceClose = (TcFcnInstanceClose) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose"); + g_TcFunctions.InstanceSetFeature = (TcFcnInstanceSetFeature) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature"); + g_TcFunctions.InstanceQueryFeature = (TcFcnInstanceQueryFeature) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature"); + g_TcFunctions.InstanceReceivePackets = (TcFcnInstanceReceivePackets) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets"); + g_TcFunctions.InstanceGetReceiveWaitHandle = (TcFcnInstanceGetReceiveWaitHandle)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle"); + g_TcFunctions.InstanceTransmitPackets = (TcFcnInstanceTransmitPackets)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets"); + g_TcFunctions.InstanceQueryStatistics = (TcFcnInstanceQueryStatistics)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics"); - g_TcFunctions.PacketsBufferCreate = (TcFcnPacketsBufferCreate) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate"); - g_TcFunctions.PacketsBufferDestroy = (TcFcnPacketsBufferDestroy) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy"); - g_TcFunctions.PacketsBufferQueryNextPacket = (TcFcnPacketsBufferQueryNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket"); - g_TcFunctions.PacketsBufferCommitNextPacket = (TcFcnPacketsBufferCommitNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket"); + g_TcFunctions.PacketsBufferCreate = (TcFcnPacketsBufferCreate) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate"); + g_TcFunctions.PacketsBufferDestroy = (TcFcnPacketsBufferDestroy) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy"); + g_TcFunctions.PacketsBufferQueryNextPacket = (TcFcnPacketsBufferQueryNextPacket)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket"); + g_TcFunctions.PacketsBufferCommitNextPacket = (TcFcnPacketsBufferCommitNextPacket)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket"); - g_TcFunctions.StatisticsDestroy = (TcFcnStatisticsDestroy) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy"); - g_TcFunctions.StatisticsUpdate = (TcFcnStatisticsUpdate) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate"); - g_TcFunctions.StatisticsQueryValue = (TcFcnStatisticsQueryValue) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue"); + g_TcFunctions.StatisticsDestroy = (TcFcnStatisticsDestroy) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy"); + g_TcFunctions.StatisticsUpdate = (TcFcnStatisticsUpdate) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate"); + g_TcFunctions.StatisticsQueryValue = (TcFcnStatisticsQueryValue) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue"); if ( g_TcFunctions.QueryPortList == NULL || g_TcFunctions.FreePortList == NULL diff --git a/pcap.c b/pcap.c index 19670ab0..38aaf379 100644 --- a/pcap.c +++ b/pcap.c @@ -123,6 +123,10 @@ struct rtentry; /* declarations in */ #include "pcap-dpdk.h" #endif +#ifdef HAVE_AIRPCAP_API +#include "pcap-airpcap.h" +#endif + #ifdef _WIN32 /* * DllMain(), required when built as a Windows DLL. @@ -141,8 +145,10 @@ struct rtentry; /* declarations in */ * be called from the DllMain function in a application DLL. This can * potentially cause deadlocks. * - * So we don't actually do anything here. pcap_init() should be called - * to initialize pcap on both UN*X and Windows. + * So we don't initialize Winsock here. pcap_init() should be called + * to initialize pcap on both UN*X and Windows; it will initialize + * Winsock on Windows. (It will also be initialized as needed if + * pcap_init() hasn't been called.) */ BOOL WINAPI DllMain( HANDLE hinstDLL _U_, @@ -686,6 +692,9 @@ static struct capture_source_type { #endif #ifdef PCAP_SUPPORT_DPDK { pcap_dpdk_findalldevs, pcap_dpdk_create }, +#endif +#ifdef HAVE_AIRPCAP_API + { airpcap_findalldevs, airpcap_create }, #endif { NULL, NULL } }; @@ -4057,6 +4066,71 @@ pcap_close(pcap_t *p) free(p); } +/* + * Helpers for safely loding code at run time. + * Currently Windows-only. + */ +#ifdef _WIN32 +// +// This wrapper around loadlibrary appends the system folder (usually +// C:\Windows\System32) to the relative path of the DLL, so that the DLL +// is always loaded from an absolute path (it's no longer possible to +// load modules from the application folder). +// This solves the DLL Hijacking issue discovered in August 2010 +// http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html +// +pcap_code_handle_t +pcap_load_code(const char *name) +{ + /* + * XXX - should this work in UTF-16LE rather than in the local + * ANSI code page? + */ + CHAR path[MAX_PATH]; + CHAR fullFileName[MAX_PATH]; + UINT res; + HMODULE hModule = NULL; + + do + { + res = GetSystemDirectoryA(path, MAX_PATH); + + if (res == 0) { + // + // some bad failure occurred; + // + break; + } + + if (res > MAX_PATH) { + // + // the buffer was not big enough + // + SetLastError(ERROR_INSUFFICIENT_BUFFER); + break; + } + + if (res + 1 + strlen(name) + 1 < MAX_PATH) { + memcpy(fullFileName, path, res * sizeof(TCHAR)); + fullFileName[res] = '\\'; + memcpy(&fullFileName[res + 1], name, (strlen(name) + 1) * sizeof(TCHAR)); + + hModule = LoadLibraryA(fullFileName); + } else + SetLastError(ERROR_INSUFFICIENT_BUFFER); + + } while(FALSE); + + return hModule; +} + +pcap_funcptr_t +pcap_find_function(pcap_code_handle_t code, const char *func) +{ + return (GetProcAddress(code, func)); +} +#endif + /* * Given a BPF program, a pcap_pkthdr structure for a packet, and the raw * data for the packet, check whether the packet passes the filter. diff --git a/testprogs/.gitignore b/testprogs/.gitignore index b37eda1d..b57c7bb4 100644 --- a/testprogs/.gitignore +++ b/testprogs/.gitignore @@ -12,3 +12,4 @@ opentest reactivatetest selpolltest threadsignaltest +writecaptest diff --git a/testprogs/CMakeLists.txt b/testprogs/CMakeLists.txt index 9491a9dc..bf573613 100644 --- a/testprogs/CMakeLists.txt +++ b/testprogs/CMakeLists.txt @@ -33,6 +33,7 @@ add_test_executable(findalldevstest) add_test_executable(findalldevstest-perf) add_test_executable(opentest) add_test_executable(reactivatetest) +add_test_executable(writecaptest) if(NOT WIN32) add_test_executable(selpolltest) diff --git a/testprogs/Makefile.in b/testprogs/Makefile.in index 8acfe213..7b91b32c 100644 --- a/testprogs/Makefile.in +++ b/testprogs/Makefile.in @@ -88,7 +88,8 @@ SRC = @VALGRINDTEST_SRC@ \ opentest.c \ reactivatetest.c \ selpolltest.c \ - threadsignaltest.c + threadsignaltest.c \ + writecaptest.c TESTS = $(SRC:.c=) @@ -129,6 +130,9 @@ threadsignaltest: $(srcdir)/threadsignaltest.c ../libpcap.a valgrindtest: $(srcdir)/valgrindtest.c ../libpcap.a $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c ../libpcap.a $(LIBS) +writecaptest: $(srcdir)/writecaptest.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o writecaptest $(srcdir)/writecaptest.c ../libpcap.a $(LIBS) + clean: rm -f $(CLEANFILES) rm -rf *.dSYM diff --git a/testprogs/writecaptest.c b/testprogs/writecaptest.c new file mode 100644 index 00000000..947904a3 --- /dev/null +++ b/testprogs/writecaptest.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "varattrs.h" + +#ifndef lint +static const char copyright[] _U_ = + "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\ +The Regents of the University of California. All rights reserved.\n"; +#endif + +#include +#include +#include +#include +#include +#ifdef _WIN32 + #include "getopt.h" +#else + #include +#endif +#include +#ifndef _WIN32 + #include +#endif +#include + +#include + +#include "pcap/funcattrs.h" + +#ifdef _WIN32 + #include "portability.h" +#endif + +static char *program_name; + +/* Forwards */ +static void PCAP_NORETURN usage(void); +static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2); +static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2); +static char *copy_argv(char **); + +static pcap_t *pd; + +#ifdef _WIN32 +static BOOL WINAPI +stop_capture(DWORD ctrltype _U_) +{ + pcap_breakloop(pd); + return TRUE; +} +#else +static void +stop_capture(int signum _U_) +{ + pcap_breakloop(pd); +} +#endif + +#define COMMAND_OPTIONS "Li:w:y:" + +int +main(int argc, char **argv) +{ + register int op; + register char *cp, *cmdbuf, *device, *savefile; + pcap_if_t *devlist; + int show_dlt_types = 0; + int ndlts; + int *dlts; + bpf_u_int32 localnet, netmask; + struct bpf_program fcode; + char ebuf[PCAP_ERRBUF_SIZE]; +#ifndef _WIN32 + struct sigaction action; +#endif + int dlt; + const char *dlt_name = NULL; + int status; + pcap_dumper_t *pdd; + + device = NULL; + if ((cp = strrchr(argv[0], '/')) != NULL) + program_name = cp + 1; + else + program_name = argv[0]; + + opterr = 0; + while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) { + switch (op) { + + case 'L': + show_dlt_types = 1; + break; + + case 'i': + device = optarg; + break; + + case 'w': + savefile = optarg; + break; + + case 'y': + dlt_name = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + if (device == NULL) { + if (pcap_findalldevs(&devlist, ebuf) == -1) + error("%s", ebuf); + if (devlist == NULL) + error("no interfaces available for capture"); + device = strdup(devlist->name); + pcap_freealldevs(devlist); + } + if (show_dlt_types) { + pd = pcap_create(device, ebuf); + if (pd == NULL) + error("%s", ebuf); + status = pcap_activate(pd); + if (status < 0) { + /* + * pcap_activate() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + ndlts = pcap_list_datalinks(pd, &dlts); + if (ndlts < 0) { + /* + * pcap_list_datalinks() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + for (int i = 0; i < ndlts; i++) { + dlt_name = pcap_datalink_val_to_name(dlts[i]); + if (dlt_name == NULL) + printf("DLT %d", dlts[i]); + else + printf("%s", dlt_name); + printf("\n"); + } + pcap_free_datalinks(dlts); + pcap_close(pd); + return 0; + } + + if (savefile == NULL) + error("no savefile specified"); + + *ebuf = '\0'; + + pd = pcap_create(device, ebuf); + if (pd == NULL) + error("%s", ebuf); + status = pcap_set_snaplen(pd, 262144); + if (status != 0) + error("%s: pcap_set_snaplen failed: %s", + device, pcap_statustostr(status)); + status = pcap_set_timeout(pd, 100); + if (status != 0) + error("%s: pcap_set_timeout failed: %s", + device, pcap_statustostr(status)); + status = pcap_activate(pd); + if (status < 0) { + /* + * pcap_activate() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } else if (status > 0) { + /* + * pcap_activate() succeeded, but it's warning us + * of a problem it had. + */ + warning("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { + localnet = 0; + netmask = 0; + warning("%s", ebuf); + } + + if (dlt_name != NULL) { + dlt = pcap_datalink_name_to_val(dlt_name); + if (dlt == PCAP_ERROR) + error("%s isn't a valid DLT name", dlt_name); + if (pcap_set_datalink(pd, dlt) == PCAP_ERROR) + error("%s: %s", device, pcap_geterr(pd)); + } + + cmdbuf = copy_argv(&argv[optind]); + + if (pcap_compile(pd, &fcode, cmdbuf, 1, netmask) < 0) + error("%s", pcap_geterr(pd)); + + if (pcap_setfilter(pd, &fcode) < 0) + error("%s", pcap_geterr(pd)); + + pdd = pcap_dump_open(pd, savefile); + if (pdd == NULL) + error("%s", pcap_geterr(pd)); + +#ifdef _WIN32 + SetConsoleCtrlHandler(stop_capture, TRUE); +#else + action.sa_handler = stop_capture; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + if (sigaction(SIGINT, &action, NULL) == -1) + error("Can't catch SIGINT: %s\n", strerror(errno)); +#endif + + printf("Listening on %s, link-type ", device); + dlt = pcap_datalink(pd); + dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name == NULL) + printf("DLT %d", dlt); + else + printf("%s", dlt_name); + printf("\n"); + for (;;) { + status = pcap_dispatch(pd, -1, pcap_dump, (u_char *)pdd); + if (status < 0) + break; + if (status != 0) { + printf("%d packets seen\n", status); + struct pcap_stat ps; + pcap_stats(pd, &ps); + printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n", + ps.ps_recv, ps.ps_drop, ps.ps_ifdrop); + } + } + if (status == -2) { + /* + * We got interrupted, so perhaps we didn't + * manage to finish a line we were printing. + * Print an extra newline, just in case. + */ + putchar('\n'); + printf("Broken out of loop from SIGINT handler\n"); + } + (void)fflush(stdout); + if (status == -1) { + /* + * Error. Report it. + */ + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", + program_name, pcap_geterr(pd)); + } + pcap_close(pd); + pcap_freecode(&fcode); + free(cmdbuf); + exit(status == -1 ? 1 : 0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s -L [ -i interface ] [ -w file ] [ -y dlt ] [expression]\n", + program_name); + exit(1); +} + +/* VARARGS */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } + exit(1); + /* NOTREACHED */ +} + +/* VARARGS */ +static void +warning(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: WARNING: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } +} + +/* + * Copy arg vector into a new buffer, concatenating arguments with spaces. + */ +static char * +copy_argv(register char **argv) +{ + register char **p; + register size_t len = 0; + char *buf; + char *src, *dst; + + p = argv; + if (*p == 0) + return 0; + + while (*p) + len += strlen(*p++) + 1; + + buf = (char *)malloc(len); + if (buf == NULL) + error("copy_argv: malloc"); + + p = argv; + dst = buf; + while ((src = *p++) != NULL) { + while ((*dst++ = *src++) != '\0') + ; + dst[-1] = ' '; + } + dst[-1] = '\0'; + + return buf; +}