From 7fee50274f36a5feba113aa9ad3e72ca37bf1466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Valverde?= Date: Wed, 24 Mar 2021 22:09:19 +0000 Subject: Merge the caputils/ and capchild/ directories The distinction between the different kinds of capture utility may not warrant a special subfolfer for each, and sometimes the distinction is not be clear or some functions could stradle multiple "categories" (like capture_ifinfo.[ch]). Simplify by having only a generic 'capture' subfolder. The separate CMake libraries are kept as a way to reuse object code efficiently. --- capture/.editorconfig | 21 + capture/CMakeLists.txt | 124 +++ capture/airpcap.h | 905 ++++++++++++++++ capture/airpcap_loader.c | 1230 ++++++++++++++++++++++ capture/airpcap_loader.h | 407 ++++++++ capture/capture-pcap-util-int.h | 61 ++ capture/capture-pcap-util-unix.c | 202 ++++ capture/capture-pcap-util.c | 1626 +++++++++++++++++++++++++++++ capture/capture-pcap-util.h | 93 ++ capture/capture-wpcap.c | 915 +++++++++++++++++ capture/capture-wpcap.h | 32 + capture/capture_ifinfo.c | 373 +++++++ capture/capture_ifinfo.h | 138 +++ capture/capture_session.h | 135 +++ capture/capture_sync.c | 2100 ++++++++++++++++++++++++++++++++++++++ capture/capture_sync.h | 112 ++ capture/capture_win_ifnames.c | 271 +++++ capture/capture_win_ifnames.h | 34 + capture/iface_monitor.c | 398 ++++++++ capture/iface_monitor.h | 71 ++ capture/ws80211_utils.c | 1275 +++++++++++++++++++++++ capture/ws80211_utils.h | 134 +++ 22 files changed, 10657 insertions(+) create mode 100644 capture/.editorconfig create mode 100644 capture/CMakeLists.txt create mode 100644 capture/airpcap.h create mode 100644 capture/airpcap_loader.c create mode 100644 capture/airpcap_loader.h create mode 100644 capture/capture-pcap-util-int.h create mode 100644 capture/capture-pcap-util-unix.c create mode 100644 capture/capture-pcap-util.c create mode 100644 capture/capture-pcap-util.h create mode 100644 capture/capture-wpcap.c create mode 100644 capture/capture-wpcap.h create mode 100644 capture/capture_ifinfo.c create mode 100644 capture/capture_ifinfo.h create mode 100644 capture/capture_session.h create mode 100644 capture/capture_sync.c create mode 100644 capture/capture_sync.h create mode 100644 capture/capture_win_ifnames.c create mode 100644 capture/capture_win_ifnames.h create mode 100644 capture/iface_monitor.c create mode 100644 capture/iface_monitor.h create mode 100644 capture/ws80211_utils.c create mode 100644 capture/ws80211_utils.h (limited to 'capture') diff --git a/capture/.editorconfig b/capture/.editorconfig new file mode 100644 index 0000000000..9411ef0157 --- /dev/null +++ b/capture/.editorconfig @@ -0,0 +1,21 @@ +# +# Editor configuration +# +# https://editorconfig.org +# + +[capture-pcap-util-unix.[ch]] +indent_style = tab +indent_size = tab + +[capture-pcap-util.[ch]] +indent_style = tab +indent_size = tab + +[capture-wpcap.[ch]] +indent_style = tab +indent_size = tab + +[ws80211_utils.[ch]] +indent_style = tab +indent_size = tab diff --git a/capture/CMakeLists.txt b/capture/CMakeLists.txt new file mode 100644 index 0000000000..57a94d78c7 --- /dev/null +++ b/capture/CMakeLists.txt @@ -0,0 +1,124 @@ +# CMakeLists.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + + +if(UNIX) + set(PLATFORM_CAPUTILS_SRC + capture-pcap-util-unix.c + ) +endif() + +if(WIN32) + set(PLATFORM_CAPUTILS_SRC + capture_win_ifnames.c + capture-wpcap.c + ) +endif() + +set(CAPUTILS_SRC + ${PLATFORM_CAPUTILS_SRC} + capture-pcap-util.c + iface_monitor.c + ws80211_utils.c +) + +if (AIRPCAP_FOUND) + set(CAPUTILS_SRC + ${CAPUTILS_SRC} + airpcap_loader.c + ) +endif() + +set_source_files_properties( + ${CAPUTILS_SRC} + PROPERTIES + COMPILE_FLAGS "${WERROR_COMMON_FLAGS}" +) + + +add_library(caputils STATIC + ${CAPUTILS_SRC} +) + +target_link_libraries(caputils + PUBLIC + wsutil + $<$:pcap::pcap> + PRIVATE + ${NL_LIBRARIES} +) + +target_include_directories(caputils SYSTEM + PRIVATE + ${NL_INCLUDE_DIRS} +) + +if(WIN32) + target_link_libraries(caputils PRIVATE "iphlpapi.lib") +endif(WIN32) + +set_target_properties(caputils PROPERTIES + LINK_FLAGS "${WS_LINK_FLAGS}" + FOLDER "Libs") + +set(CAPCHILD_SRC + capture_ifinfo.c + capture_sync.c +) + +set_source_files_properties( + ${CAPCHILD_SRC} + PROPERTIES + COMPILE_FLAGS "${WERROR_COMMON_FLAGS}" +) + +file(GLOB CAPCHILD_HEADERS capture_session.h) + +add_library(capchild STATIC + ${CAPCHILD_SRC} +) + +target_link_libraries(capchild + PRIVATE + wsutil + $<$:pcap::pcap> +) + +set_target_properties(capchild PROPERTIES + LINK_FLAGS "${WS_LINK_FLAGS}" + FOLDER "Libs") + +CHECKAPI( + NAME + caputils-base + SWITCHES + SOURCES + ${CAPUTILS_SRC} +) +CHECKAPI( + NAME + caputils-todo + SWITCHES + -M + SOURCES + ${CAPUTILS_SRC} +) + +# +# Editor modelines - https://www.wireshark.org/tools/modelines.html +# +# Local variables: +# c-basic-offset: 8 +# tab-width: 8 +# indent-tabs-mode: t +# End: +# +# vi: set shiftwidth=8 tabstop=8 noexpandtab: +# :indentSize=8:tabSize=8:noTabs=false: +# diff --git a/capture/airpcap.h b/capture/airpcap.h new file mode 100644 index 0000000000..9ca0688834 --- /dev/null +++ b/capture/airpcap.h @@ -0,0 +1,905 @@ +/* + * Copyright (c) 2006-2007 CACE Technologies, Davis (California) + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#if !defined(AIRPCAP_H__EAE405F5_0171_9592_B3C2_C19EC426AD34__INCLUDED_) +#define AIRPCAP_H__EAE405F5_0171_9592_B3C2_C19EC426AD34__INCLUDED_ + +#ifdef _MSC_VER +/* This stops VS2005 ranting against stdio. */ +#pragma warning( disable : 4996) +#endif + +#ifdef _WIN32 +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + \mainpage AirPcap interface documentation + + \section Introduction + + This document describes the data structures and the functions exported by the CACE Technologies AirPcap library. + The AirPcap library provides low-level access to the AirPcap driver including advanced capabilities such as channel setting, + link type control and WEP configuration.
+ This manual includes the following sections: + + \note throughout this documentation, \e device refers to a physical USB AirPcap device, while \e adapter is an open API + instance. Most of the AirPcap API operations are adapter-specific but some of them, like setting the channel, are + per-device and will be reflected on all the open adapters. These functions will have "Device" in their name, e.g. + AirpcapSetDeviceChannel(). + + \b Sections: + + - \ref airpcapfuncs + - \ref airpcapdefs + - \ref radiotap +*/ + +/** @defgroup airpcapdefs AirPcap definitions and data structures + * @{ + */ + +/*! + \brief This string is the fixed prefix in the airpcap adapter name. + It can be used to parse the name field in an AirpcapDeviceDescription structure. +*/ +#define AIRPCAP_DEVICE_NAME_PREFIX "\\\\.\\airpcap" + +/*! + \brief This string is the scanf modifier to extract the adapter number from an adapter name. + It can be used to parse the name field in an AirpcapDeviceDescription structure with scanf. +*/ +#define AIRPCAP_DEVICE_NUMBER_EXTRACT_STRING "\\\\.\\airpcap%u" + +#define AIRPCAP_DEVICE_ANY_EXTRACT_STRING "\\\\.\\airpcap_any" + +/*! + \brief Entry in the list returned by \ref AirpcapGetDeviceList(); +*/ +typedef struct _AirpcapDeviceDescription +{ + struct _AirpcapDeviceDescription *next; /* < Next element in the list */ + gchar * Name; /* < Device name */ + gchar * Description; /* < Device description */ +} AirpcapDeviceDescription, *PAirpcapDeviceDescription; + +#define MAX_ENCRYPTION_KEYS 64 + +#define WEP_KEY_MAX_SIZE 32 /* < Maximum size of a WEP key, in bytes. This is the size of an entry in the + < AirpcapWepKeysCollection structure. */ + +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma pack(push) +#pragma pack(1) +#endif +#endif + +#define AIRPCAP_KEYTYPE_WEP 0 /* < Key type: WEP. The key can have an arbitrary length smaller than 32 bytes. */ +#define AIRPCAP_KEYTYPE_TKIP 1 /* < Key type: TKIP (WPA). NOT SUPPORTED YET. */ +#define AIRPCAP_KEYTYPE_CCMP 2 /* < Key type: CCMP (WPA2). NOT SUPPORTED YET. */ + +/*! + \brief WEP key container +*/ +typedef struct _AirpcapKey +{ + guint KeyType; /* < Type of key, can be on of: \ref AIRPCAP_KEYTYPE_WEP, \ref AIRPCAP_KEYTYPE_TKIP, \ref AIRPCAP_KEYTYPE_CCMP. Only AIRPCAP_KEYTYPE_WEP is supported by the driver at the moment. */ + guint KeyLen; /* < Length of the key, in bytes */ + guint8 KeyData[WEP_KEY_MAX_SIZE]; /* < Key Data */ +} +#ifdef __MINGW32__ +__attribute__((__packed__)) +#endif +AirpcapKey, *PAirpcapKey; + +/*! + \brief frequency Band. + 802.11 adapters can support different frequency bands, the most important of which are: 2.4GHz (802.11b/g/n) + and 5GHz (802.11a/n). +*/ +typedef enum _AirpcapChannelBand +{ + AIRPCAP_CB_AUTO = 1, /* < Automatically pick the best frequency band */ + AIRPCAP_CB_2_4_GHZ = 2, /* < 2.4 GHz frequency band */ + AIRPCAP_CB_4_GHZ = 4, /* < 4 GHz frequency band */ + AIRPCAP_CB_5_GHZ = 5 /* < 5 GHz frequency band */ +}AirpcapChannelBand, *PAirpcapChannelBand; + +/*! + \brief Type of frame validation the adapter performs. + An adapter can be instructed to accept different kind of frames: correct frames only, frames with wrong Frame Check Sequence (FCS) only, all frames. +*/ +typedef enum _AirpcapValidationType +{ + AIRPCAP_VT_ACCEPT_EVERYTHING = 1, /* < Accept all the frames the device captures */ + AIRPCAP_VT_ACCEPT_CORRECT_FRAMES = 2, /* < Accept correct frames only, i.e. frames with correct Frame Check Sequence (FCS). */ + AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES = 3, /* < Accept corrupt frames only, i.e. frames with wrong Frame Check Sequence (FCS). */ + AIRPCAP_VT_UNKNOWN = 4 /* < Unknown validation type. You should see it only in case of error. */ +}AirpcapValidationType, *PAirpcapValidationType; + +/*! + \brief Type of decryption the adapter performs. + An adapter can be instructed to turn decryption (based on the device-configured keys configured + with \ref AirpcapSetDeviceKeys()) on or off. +*/ +typedef enum _AirpcapDecryptionState +{ + AIRPCAP_DECRYPTION_ON = 1, /* < This adapter performs decryption */ + AIRPCAP_DECRYPTION_OFF = 2 /* < This adapter does not perform decryption */ +}AirpcapDecryptionState, *PAirpcapDecryptionState; + + +/*! + \brief Storage for a MAC address +*/ +typedef struct _AirpcapMacAddress +{ + guint8 Address[6]; /* < MAC address bytes */ +} +#ifdef __MINGW32__ +__attribute__((__packed__)) +#endif +AirpcapMacAddress, *PAirpcapMacAddress; + +/*! + \brief This structure is used to store a collection of WEP keys. + Note that the definition of the structure has one key in it + (so that this code can be compiled by compilers that don't + support zero-length arrays), so be careful to allocate a buffer + with the size of the set of keys, as per the following example: + + \code + PAirpcapKeysCollection KeysCollection; + guint KeysCollectionSize; + + KeysCollectionSize = AirpcapKeysCollectionSize(NumKeys); + + KeysCollection = (PAirpcapKeysCollection)malloc(KeysCollectionSize); + if(!KeysCollection) + { + Error + } + \endcode +*/ +typedef struct _AirpcapKeysCollection +{ + guint nKeys; /* < Number of keys in the collection */ + AirpcapKey Keys[1]; /* < Array of nKeys keys. */ +} AirpcapKeysCollection, *PAirpcapKeysCollection; + +#define AirpcapKeysCollectionSize(nKeys) \ + ((sizeof(AirpcapKeysCollection) - sizeof(AirpcapKey)) + ((nKeys) * sizeof(AirpcapKey))) +#define AirpcapKeysCollectionSizeToKeyCount(size) \ + (guint)(((size) - AirpcapKeysCollectionSize(0))/sizeof(AirpcapKey)) + +/*! + \brief Packet header. + + This structure defines the BPF that precedes every packet delivered to the application. +*/ +typedef struct _AirpcapBpfHeader +{ + guint TsSec; /* < Timestamp associated with the captured packet. SECONDS. */ + guint TsUsec; /* < Timestamp associated with the captured packet. MICROSECONDS. */ + guint Caplen; /* < Length of captured portion. The captured portion can be different from the original packet, because it is possible (with a proper filter) to instruct the driver to capture only a portion of the packets. */ + guint Originallen; /* < Original length of packet */ + guint16 Hdrlen; /* < Length of bpf header (this struct plus alignment padding). In some cases, a padding could be added between the end of this structure and the packet data for performance reasons. This field can be used to retrieve the actual data of the packet. */ +} +#ifdef __MINGW32__ +__attribute__((__packed__)) +#endif +AirpcapBpfHeader, *PAirpcapBpfHeader; + +/* Helper macros to extract packets coming from the driver. Rounds up to the next even multiple of AIRPCAP_ALIGNMENT. */ +#define AIRPCAP_ALIGNMENT sizeof(int) +#define AIRPCAP_WORDALIGN(x) (((x)+(AIRPCAP_ALIGNMENT-1))&~(AIRPCAP_ALIGNMENT-1)) + +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma pack(pop) +#endif +#endif + +#define AIRPCAP_ERRBUF_SIZE 512 /* < Size of the error buffer, in bytes */ + +#ifndef __AIRPCAP_DRIVER__ + +/*! + \brief Link type. + AirPcap supports two kind of 802.11 linktypes: plain 802.11 and radiotap. +*/ +#undef _AirpcapLinkType +typedef enum _AirpcapLinkType +{ + AIRPCAP_LT_802_11 = 1, /* < plain 802.11 linktype. Every packet in the buffer contains the raw 802.11 frame, including MAC FCS. */ + AIRPCAP_LT_802_11_PLUS_RADIO = 2, /* < 802.11 plus radiotap linktype. Every packet in the buffer contains a radiotap header followed by the 802.11 frame. MAC FCS is included. */ + AIRPCAP_LT_UNKNOWN = 3, /* < Unknown linktype. You should see it only in case of error. */ + AIRPCAP_LT_802_11_PLUS_PPI = 4 /* < 802.11 plus PPI header linktype. Every packet in the buffer contains a PPI header followed by the 802.11 frame. MAC FCS is included. */ +}AirpcapLinkType, *PAirpcapLinkType; + +#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_) +#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ +/*! + \brief Adapter handle. +*/ +typedef struct _AirpcapHandle AirpcapHandle, *PAirpcapHandle; +#endif + +/*! + \brief Capture statistics. + Returned by \ref AirpcapGetStats(); +*/ +typedef struct _AirpcapStats +{ + guint Recvs; /* < Number of packets that the driver received by the adapter */ + /* < from the beginning of the current capture. This value includes the packets */ + /* < dropped because of buffer full. */ + guint Drops; /* < number of packets that the driver dropped from the beginning of a capture. */ + /* < A packet is lost when the the buffer of the driver is full. */ + guint IfDrops; /* < Packets dropped by the card before going to the USB bus. */ + /* < Not supported at the moment. */ + guint Capt; /* < number of packets that pass the BPF filter, find place in the kernel buffer and */ + /* < therefore reach the application. */ +}AirpcapStats, *PAirpcapStats; + +/*! + \brief Channel information. + Used by \ref AirpcapSetDeviceChannelEx(), \ref AirpcapGetDeviceChannelEx(), \ref AirpcapGetDeviceSupportedChannels() +*/ +typedef struct _AirpcapChannelInfo +{ + guint Frequency; /* < Channel frequency, in MHz. */ + /*! + \brief 802.11n specific. Offset of the extension channel in case of 40MHz channels. + + Possible values are -1, 0 +1: + - -1 means that the extension channel should be below the control channel (e.g. Control = 5 and Extension = 1) + - 0 means that no extension channel should be used (20MHz channels or legacy mode) + - +1 means that the extension channel should be above the control channel (e.g. Control = 1 and Extension = 5) + + In case of 802.11a/b/g channels (802.11n legacy mode), this field should be set to 0. + */ + gint8 ExtChannel; + guint8 Reserved[3]; /* < Reserved. It should be set to {0,0,0}. */ +} + AirpcapChannelInfo, *PAirpcapChannelInfo; + + +/*@}*/ + +/** @defgroup airpcapfuncs AirPcap functions + * @{ + */ + +/*! + \brief Return a string with the API version + \param VersionMajor Pointer to a variable that will be filled with the major version number. + \param VersionMinor Pointer to a variable that will be filled with the minor version number. + \param VersionRev Pointer to a variable that will be filled with the revision number. + \param VersionBuild Pointer to a variable that will be filled with the build number. +*/ +void AirpcapGetVersion(guint * VersionMajor, guint * VersionMinor, guint * VersionRev, guint * VersionBuild); + +/*! + \brief Return the last error related to the specified handle + \param AdapterHandle Handle to an open adapter. + \return The string with the last error. +*/ +gchar * AirpcapGetLastError(PAirpcapHandle AdapterHandle); + +/*! + \brief Return the list of available devices + \param PPAllDevs Address to a caller allocated pointer. On success this pointer will receive the head of a list of available devices. + \param Ebuf String that will contain error information if FALSE is returned. The size of the string must be AIRPCAP_ERRBUF_SIZE bytes. + \return TRUE on success. FALSE is returned on failure, in which case Ebuf is filled in with an appropriate error message. + + Here's a snippet of code that shows how to use AirpcapGetDeviceList(): + + \code + gchar Ebuf[AIRPCAP_ERRBUF_SIZE]; + AirpcapDeviceDescription *Desc, *tDesc; + + if(AirpcapGetDeviceList(&Desc, Ebuf) == -1) + { + printf("Unable to get the list of devices: %s\n", Ebuf); + return -1; + } + + for(tDesc = Desc; tDesc; tDesc = tDesc->next) + { + printf("%u) %s (%s)\n", + ++i, + tDesc->Name, + tDesc->Description); + } + + AirpcapFreeDeviceList(Desc); + \endcode +*/ +gboolean AirpcapGetDeviceList(PAirpcapDeviceDescription *PPAllDevs, gchar * Ebuf); + +/*! + \brief Free a list of devices returned by AirpcapGetDeviceList() + \param PAllDevs Head of the list of devices returned by \ref AirpcapGetDeviceList(). +*/ +void AirpcapFreeDeviceList(PAirpcapDeviceDescription PAllDevs); + +/*! + \brief Open an adapter + \param DeviceName Name of the device to open. Use \ref AirpcapGetDeviceList() to get the list of devices. + \param Ebuf String that will contain error information in case of failure. The size of the string must be AIRPCAP_ERRBUF_SIZE bytes. + \return A PAirpcapHandle handle on success. NULL is returned on failure, in which case Ebuf is filled in with an appropriate error message. +*/ +PAirpcapHandle AirpcapOpen(gchar * DeviceName, gchar * Ebuf); + +/*! + \brief Close an adapter + \param AdapterHandle Handle to the adapter to close. +*/ +void AirpcapClose(PAirpcapHandle AdapterHandle); + +/*! + \brief Sets the monitor mode for the specified adapter + \param AdapterHandle Handle to the adapter. + \param MonitorModeEnabled If TRUE, the adapter will be put in monitor mode. If FALSE, the adapter will be configured + for normal operation. + \return TRUE on success. + + When monitor mode is on, the adapter captures all the packets transmitted on the channel. This includes: + + - unicast packets + - multicast packets + - broadcast packets + - control and management packets + + When monitor mode is off, the adapter has a filter on unicast packets to capture only the packets whose MAC + destination address equals to the adapter's address. This means the following frames will be received: + + - unicast packets with the address of the adapter + - multicast packets + - broadcast packets + - beacons and probe requests + + The main reason to turn monitor mode off is that, when not in monitor mode, the adapter will acknowledge the + data frames sent to its address. This is useful when the adapter needs to interact with other devices on the + 802.11 network, because handling the ACKs in software is too slow. + + \note When an adapter is plugged into the system, it's always configured with monitor mode ON. The monitor mode + configuration is not stored persistently, so if you want to turn monitor mode off, you will need to do it + every time you open the adapter. +*/ +gboolean AirpcapSetMonitorMode(PAirpcapHandle AdapterHandle, gboolean MonitorModeEnabled); + +/*! + \brief Returns TRUE if the specified adapter is in monitor mode. + \param AdapterHandle Handle to the adapter. + \param PMonitorModeEnabled User-provided variable that will be set to true if the adapter is in monitor mode. + \return TRUE if the operation is successful. FALSE otherwise. + + \note When an adapter is plugged into the system, it's always configured with monitor mode ON. The monitor mode + configuration is not stored persistently, so if you want to turn monitor mode off, you will need to do it + every time you open the adapter. +*/ +gboolean AirpcapGetMonitorMode(PAirpcapHandle AdapterHandle, gboolean * PMonitorModeEnabled); + +/*! + \brief Set the link type of an adapter + \param AdapterHandle Handle to the adapter. + \param NewLinkType the "link type", i.e. the format of the frames that will be received from the adapter. + \return TRUE on success. + + the "link type" determines how the driver will encode the packets captured from the network. + Aircap supports two link types: + - AIRPCAP_LT_802_11, to capture 802.11 frames (including control frames) without any + power information. Look at the Capture_no_radio example application in the developer's pack + for a reference on how to decode 802.11 frames with this link type. + - AIRPCAP_LT_802_11_PLUS_RADIO, to capture 802.11 frames (including control frames) with a radiotap header + that contains power and channel information. More information about the radiotap header can be found in the + radiotap section. Moreover, the "Capture_radio" example application in + the developer's pack can be used as a reference on how to decode 802.11 frames with radiotap headers. + - AIRPCAP_LT_802_11_PLUS_PPI, to capture 802.11 frames (including control frames) with a Per Packet Information (PPI) + header that contains per-packet meta information like channel and power information. More details on the PPI header can + be found in the PPI online documentation (TODO). +*/ +gboolean AirpcapSetLinkType(PAirpcapHandle AdapterHandle, AirpcapLinkType NewLinkType); + +/*! + \brief Get the link type of the specified adapter + \param AdapterHandle Handle to the adapter. + \param PLinkType Pointer to a caller allocated AirpcapLinkType variable that will contain the link type of the adapter. + \return TRUE on success. + + the "link type" determines how the driver will encode the packets captured from the network. + Aircap supports two link types: + - AIRPCAP_LT_802_11, to capture 802.11 frames (including control frames) without any + power information. Look at the Capture_no_radio example application in the developer's pack + for a reference on how to decode 802.11 frames with this link type. + - AIRPCAP_LT_802_11_PLUS_RADIO, to capture 802.11 frames (including control frames) with a radiotap header + that contains power and channel information. More information about the radiotap header can be found int the + radiotap section. Moreover, the "Capture_radio" example application in + the developer's pack can be used as a reference on how to decode 802.11 frames with radiotap headers. +*/ +gboolean AirpcapGetLinkType(PAirpcapHandle AdapterHandle, PAirpcapLinkType PLinkType); + +/*! + \brief Configures the adapter on whether to include the MAC Frame Check Sequence in the captured packets. + \param AdapterHandle Handle to the adapter. + \param IsFcsPresent TRUE if the packets should include the FCS. FALSE otherwise + \return TRUE on success. + + In the default configuration, the adapter includes the FCS in the captured packets. The MAC Frame Check Sequence + is 4 bytes and is located at the end of the 802.11 packet, with both AIRPCAP_LT_802_11 and AIRPCAP_LT_802_11_PLUS_RADIO + link types. + When the FCS inclusion is turned on, and if the link type is AIRPCAP_LT_802_11_PLUS_RADIO, the radiotap header + that precedes each frame has two additional fields at the end: Padding and FCS. These two fields are not present + when FCS inclusion is off. +*/ +gboolean AirpcapSetFcsPresence(PAirpcapHandle AdapterHandle, gboolean IsFcsPresent); + +/*! + \brief Returns TRUE if the specified adapter includes the MAC Frame Check Sequence in the captured packets + \param AdapterHandle Handle to the adapter. + \param PIsFcsPresent User-provided variable that will be set to true if the adapter is including the FCS. + \return TRUE if the operation is successful. FALSE otherwise. + + In the default configuration, the adapter has FCS inclusion turned on. The MAC Frame Check Sequence is 4 bytes + and is located at the end of the 802.11 packet, with both AIRPCAP_LT_802_11 and AIRPCAP_LT_802_11_PLUS_RADIO + link types. + When the FCS inclusion is turned on, and if the link type is AIRPCAP_LT_802_11_PLUS_RADIO, the radiotap header + that precedes each frame has two additional fields at the end: Padding and FCS. These two fields are not present + when FCS inclusion is off. +*/ +gboolean AirpcapGetFcsPresence(PAirpcapHandle AdapterHandle, gboolean * PIsFcsPresent); + +/*! + \brief Configures the adapter to accept or drop frames with an incorrect Frame Check sequence (FCS). + \param AdapterHandle Handle to the adapter. + \param ValidationType The type of validation the driver will perform. See the documentation of \ref AirpcapValidationType for details. + \return TRUE on success. + + \note By default, the driver is configured in AIRPCAP_VT_ACCEPT_EVERYTHING mode. +*/ +gboolean AirpcapSetFcsValidation(PAirpcapHandle AdapterHandle, AirpcapValidationType ValidationType); + +/*! + \brief Checks if the specified adapter is configured to capture frames with incorrect an incorrect Frame Check Sequence (FCS). + \param AdapterHandle Handle to the adapter. + \param ValidationType Pointer to a user supplied variable that will contain the type of validation the driver will perform. See the documentation of \ref AirpcapValidationType for details. + \return TRUE if the operation is successful. FALSE otherwise. + + \note By default, the driver is configured in AIRPCAP_VT_ACCEPT_EVERYTHING mode. +*/ +gboolean AirpcapGetFcsValidation(PAirpcapHandle AdapterHandle, PAirpcapValidationType ValidationType); + +/*! + \brief Set the list of decryption keys that the driver is going to use with the specified device. + \param AdapterHandle Handle an open adapter instance. + \param KeysCollection Pointer to a PAirpcapKeysCollection structure that contains the keys to be set in the driver. + \return TRUE if the operation is successful. FALSE otherwise. + + The AirPcap driver is able to use a set of decryption keys to decrypt the traffic transmitted on a specific SSID. If one of the + keys corresponds to the one the frame has been encrypted with, the driver will perform decryption and return the cleartext frames + to the application. + + This function allows to set the adapter-specific set of keys. These keys will be used by the specified adapter only, + and will not be used by other airpcap devices besides the specified one. + + At this time, the only supported decryption method is WEP. + + The keys are applied to the packets in the same order they appear in the KeysCollection structure until the packet is + correctly decrypted, therefore putting frequently used keys at the beginning of the structure improves performance. + + \note: when you change the set of keys from an open capture instance, the change will be + immediately reflected on all the other capture instances. +*/ +gboolean AirpcapSetDeviceKeys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection); + +/*! + \brief Returns the list of decryption keys in the driver that are currently associated with the specified device + \param AdapterHandle Handle to an open adapter instance. + \param KeysCollection User-allocated PAirpcapKeysCollection structure that will be filled with the keys. + \param PKeysCollectionSize \b IN: pointer to a user-allocated variable that contains the length of the KeysCollection structure, in bytes. + \b OUT: amount of data moved by the driver in the buffer pointed by KeysBuffer, in bytes. + \return TRUE if the operation is successful. If an error occurs, the return value is FALSE and KeysCollectionSize is zero. + If the provided buffer is too small to contain the keys, the return value is FALSE and KeysCollectionSize contains the + needed KeysCollection length, in bytes. If the device doesn't have any decryption key configured, the return value is TRUE, and + KeysCollectionSize will be zero. + + This function returns the adapter-specific set of keys. These keys are used by the specified adapter only, + and not by other airpcap devices besides the specified one. + + The AirPcap driver is able to use a set of decryption keys to decrypt the traffic transmitted on a specific SSID. If one of the + keys corresponds to the one the frame has been encrypted with, the driver will perform decryption and return the cleartext frames + to the application. + The driver supports, for every device, multiple keys at the same time. + + The configured decryption keys are device-specific, therefore AirpcapGetDeviceKeys() will return a different set of keys + when called on different devices. + + At this time, the only supported decryption method is WEP. +*/ +gboolean AirpcapGetDeviceKeys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize); + +/*! + \brief Set the global list of decryption keys that the driver is going to use with all the devices. + \param AdapterHandle Handle an open adapter instance. + \param KeysCollection Pointer to a PAirpcapKeysCollection structure that contains the keys to be set in the driver. + \return TRUE if the operation is successful. FALSE otherwise. + + The AirPcap driver is able to use a set of decryption keys to decrypt the traffic transmitted on a specific SSID. If one of the + keys corresponds to the one the frame has been encrypted with, the driver will perform decryption and return the cleartext frames + to the application. + + This function allows to set the global driver set of keys. These keys will be used by all the adapters plugged in + the machine. + + At this time, the only supported decryption method is WEP. + + The keys are applied to the packets in the same order they appear in the KeysCollection structure until the packet is + correctly decrypted, therefore putting frequently used keys at the beginning of the structure improves performance. + + \note: when you change the set of keys from an open capture instance, the change will be + immediately reflected on all the other capture instances. +*/ +gboolean AirpcapSetDriverKeys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection); + +/*! + \brief Returns the global list of decryption keys in the driver that are associated with all the devices. + \param AdapterHandle Handle to an open adapter instance. + \param KeysCollection User-allocated PAirpcapKeysCollection structure that will be filled with the keys. + \param PKeysCollectionSize \b IN: pointer to a user-allocated variable that contains the length of the KeysCollection structure, in bytes. + \b OUT: amount of data moved by the driver in the buffer pointed by KeysBuffer, in bytes. + \return TRUE if the operation is successful. If an error occurs, the return value is FALSE and KeysCollectionSize is zero. + If the provided buffer is too small to contain the keys, the return value is FALSE and KeysCollectionSize contains the + needed KeysCollection length, in bytes. If the device doesn't have any decryption key configured, the return value is TRUE, and + KeysCollectionSize will be zero. + + This function returns the global driver set of keys. These keys will be used by all the adapters plugged in + the machine. + + The AirPcap driver is able to use a set of decryption keys to decrypt the traffic transmitted on a specific SSID. If one of the + keys corresponds to the one the frame has been encrypted with, the driver will perform decryption and return the cleartext frames + to the application. + + At this time, the only supported decryption method is WEP. +*/ +gboolean AirpcapGetDriverKeys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize); + +/*! + \brief Turns on or off the decryption of the incoming frames with the adapter-specific keys. + \param AdapterHandle Handle to the adapter. + \param Enable Either AIRPCAP_DECRYPTION_ON or AIRPCAP_DECRYPTION_OFF + \return TRUE on success. + + The adapter-specific decryption keys can be configured with the \ref AirpcapSetDeviceKeys() function. + \note By default, the driver is configured with AIRPCAP_DECRYPTION_ON. +*/ +gboolean AirpcapSetDecryptionState(PAirpcapHandle AdapterHandle, AirpcapDecryptionState Enable); + +/*! + \brief Tells if this open instance is configured to perform the decryption of the incoming frames with the adapter-specific keys. + \param AdapterHandle Handle to the adapter. + \param PEnable Pointer to a user supplied variable that will contain the decryption configuration. See \ref _AirpcapDecryptionState for details. + \return TRUE if the operation is successful. FALSE otherwise. + + The adapter-specific decryption keys can be configured with the \ref AirpcapSetDeviceKeys() function. + \note By default, the driver is configured with AIRPCAP_DECRYPTION_ON. +*/ +gboolean AirpcapGetDecryptionState(PAirpcapHandle AdapterHandle, PAirpcapDecryptionState PEnable); + +/*! + \brief Turns on or off the decryption of the incoming frames with the global driver set of keys. + \param AdapterHandle Handle to the adapter. + \param Enable Either AIRPCAP_DECRYPTION_ON or AIRPCAP_DECRYPTION_OFF + \return TRUE on success. + + The global decryption keys can be configured with the \ref AirpcapSetDriverKeys() function. + \note By default, the driver is configured with AIRPCAP_DECRYPTION_ON. +*/ +gboolean AirpcapSetDriverDecryptionState(PAirpcapHandle AdapterHandle, AirpcapDecryptionState Enable); + +/*! + \brief Tells if this open instance is configured to perform the decryption of the incoming frames with the global driver set of keys. + \param AdapterHandle Handle to the adapter. + \param PEnable Pointer to a user supplied variable that will contain the decryption configuration. See \ref _AirpcapDecryptionState for details. + \return TRUE if the operation is successful. FALSE otherwise. + + The global decryption keys can be configured with the \ref AirpcapSetDriverKeys() function. + \note By default, the driver is configured with AIRPCAP_DECRYPTION_ON. +*/ +gboolean AirpcapGetDriverDecryptionState(PAirpcapHandle AdapterHandle, PAirpcapDecryptionState PEnable); + +/*! + \brief Set the radio channel of a device + \param AdapterHandle Handle to the adapter. + \param Channel the new channel to set. + \return TRUE on success. + + The list of available channels can be retrieved with \ref AirpcapGetDeviceSupportedChannels(). The default channel setting is 6. + + \note this is a device-related function: when you change the channel from an open capture instance, the change will be + immediately reflected on all the other capture instances. +*/ +gboolean AirpcapSetDeviceChannel(PAirpcapHandle AdapterHandle, guint Channel); + +/*! + \brief Get the radio channel of a device + \param AdapterHandle Handle to the adapter. + \param PChannel Pointer to a user-supplied variable into which the function will copy the currently configured radio channel. + \return TRUE on success. + + The list of available channels can be retrieved with \ref AirpcapGetDeviceSupportedChannels(). The default channel setting is 6. + + \note this is a device-related function: when you change the channel from an open capture instance, the change will be + immediately reflected on all the other capture instances. +*/ +gboolean AirpcapGetDeviceChannel(PAirpcapHandle AdapterHandle, guint * PChannel); + +/*! + \brief Set the size of the kernel packet buffer for this adapter + \param AdapterHandle Handle to the adapter. + \param BufferSize New size, in bytes. + \return TRUE on success. + + Every AirPcap open instance has an associated kernel buffer, whose default size is 1 Mbyte. + This function can be used to change the size of this buffer, and can be called at any time. + A bigger kernel buffer size decreases the risk of dropping packets during network bursts or when the + application is busy, at the cost of higher kernel memory usage. + + \note don't use this function unless you know what you are doing. Due to caching issues and bigger non-paged + memory consumption, bigger buffer sizes can decrease the capture performance instead of improving it. +*/ +gboolean AirpcapSetKernelBuffer(PAirpcapHandle AdapterHandle, guint BufferSize); + +/*! + \brief Get the size of the kernel packet buffer for this adapter + \param AdapterHandle Handle to the adapter. + \param PSizeBytes User-allocated variable that will be filled with the size of the kernel buffer. + \return TRUE on success. + + Every AirPcap open instance has an associated kernel buffer, whose default size is 1 Mbyte. + This function can be used to get the size of this buffer. +*/ +gboolean AirpcapGetKernelBufferSize(PAirpcapHandle AdapterHandle, guint * PSizeBytes); + +/*! + \brief Saves the configuration of the specified adapter in the registry, so that it becomes the default for this adapter. + \param AdapterHandle Handle to the adapter. + \return TRUE on success. FALSE on failure. + + Almost all the AirPcap calls that modify the configuration (\ref AirpcapSetLinkType(), \ref AirpcapSetFcsPresence(), + \ref AirpcapSetFcsValidation(), \ref AirpcapSetKernelBuffer(), \ref AirpcapSetMinToCopy()) + affect only the referenced AirPcap open instance. This means that if you do another \ref AirpcapOpen() on the same + adapter, the configuration changes will not be remembered, and the new adapter handle will have default configuration + settings. + + Exceptions to this rule are the \ref AirpcapSetDeviceChannel() and \ref AirpcapSetDeviceKeys() functions: a channel change is + reflected on all the open instances, and remembered until the next call to \ref AirpcapSetDeviceChannel(), until the adapter + is unplugged, or until the machine is powered off. Same thing for the configuration of the WEP keys. + + AirpcapStoreCurConfigAsAdapterDefault() stores the configuration of the give open instance as the default for the adapter: + all the instances opened in the future will have the same configuration that this adapter currently has. + The configuration is stored in the registry, therefore it is remembered even when the adapter is unplugged or the + machine is turned off. However, an adapter doesn't bring its configuration with it from machine to machine. + + the configuration information saved in the registry includes the following parameters: + - channel + - kernel buffer size + - mintocopy + - link type + - CRC presence + - Encryption keys + - Encryption Enabled/Disabled state + + The configuration is adapter-specific. This means that changing the configuration of an adapter + doesn't modify the one of the other adapters that are currently used or that will be used in the future. + + \note AirpcapStoreCurConfigAsAdapterDefault() must have exclusive access to the adapter -- it + will fail if more than one AirPcap handle is opened at the same time for this adapter. + AirpcapStoreCurConfigAsAdapterDefault() needs administrator privileges. It will fail if the calling user + is not a local machine administrator. +*/ +gboolean AirpcapStoreCurConfigAsAdapterDefault(PAirpcapHandle AdapterHandle); + +/*! + \brief Set the BPF kernel filter for an adapter + \param AdapterHandle Handle to the adapter. + \param Instructions pointer to the first BPF instruction in the array. Corresponds to the bf_insns + in a bpf_program structure (see the WinPcap documentation at https://www.winpcap.org/devel.htm). + \param Len Number of instructions in the array pointed by the previous field. Corresponds to the bf_len in + a a bpf_program structure (see the WinPcap documentation at https://www.winpcap.org/devel.htm). + \return TRUE on success. + + The AirPcap driver is able to perform kernel-level filtering using the standard BPF pseudo-machine format. You can read + the WinPcap documentation at https://www.winpcap.org/devel.htm for more details on the BPF filtering mechanism. + + A filter can be automatically created by using the pcap_compile() function of the WinPcap API. This function + converts a human readable text expression with the tcpdump/libpcap syntax into a BPF program. + If your program doesn't link wpcap, but you need to generate the code for a particular filter, you can run WinDump + with the -d or -dd or -ddd flags to obtain the pseudocode. + +*/ +gboolean AirpcapSetFilter(PAirpcapHandle AdapterHandle, void * Instructions, guint Len); + +/*! + \brief Return the MAC address of an adapter. + \param AdapterHandle Handle to the adapter. + \param PMacAddress Pointer to a user allocated MAC address. + The size of this buffer needs to be at least 6 bytes. + \return TRUE on success. +*/ +gboolean AirpcapGetMacAddress(PAirpcapHandle AdapterHandle, PAirpcapMacAddress PMacAddress); + +/*! + \brief Set the mintocopy parameter for an open adapter + \param AdapterHandle Handle to the adapter. + \param MinToCopy is the mintocopy size in bytes. + \return TRUE on success. + + When the number of bytes in the kernel buffer changes from less than mintocopy bytes to greater than or equal to mintocopy bytes, + the read event is signalled (see \ref AirpcapGetReadEvent()). A high value for mintocopy results in poor responsiveness since the + driver may signal the application "long" after the arrival of the packet. And a high value results in low CPU loading + by minimizing the number of user/kernel context switches. + A low MinToCopy results in good responsiveness since the driver will signal the application close to the arrival time of + the packet. This has higher CPU loading over the first approach. +*/ +gboolean AirpcapSetMinToCopy(PAirpcapHandle AdapterHandle, guint MinToCopy); + +/*! + \brief Gets an event that is signaled when that is signalled when packets are available in the kernel buffer (see \ref AirpcapSetMinToCopy()). + \param AdapterHandle Handle to the adapter. + \param PReadEvent Pointer to a user-supplied handle that in which the read event will be copied. + \return TRUE on success. + + \note the event is signalled when at least mintocopy bytes are present in the kernel buffer (see \ref AirpcapSetMinToCopy()). + This event can be used by WaitForSingleObject() and WaitForMultipleObjects() to create blocking behavior when reading + packets from one or more adapters (see \ref AirpcapRead()). +*/ +gboolean AirpcapGetReadEvent(PAirpcapHandle AdapterHandle, void *** PReadEvent); + +/*! + \brief Fills a user-provided buffer with zero or more packets that have been captured on the referenced adapter. + \param AdapterHandle Handle to the adapter. + \param Buffer pointer to the buffer that will be filled with captured packets. + \param BufSize size of the input buffer that will contain the packets, in bytes. + \param PReceievedBytes Pointer to a user supplied variable that will receive the number of bytes copied by AirpcapRead. + Can be smaller than BufSize. + \return TRUE on success. + + 802.11 frames are returned by the driver in buffers. Every 802.11 frame in the buffer is preceded by a \ref AirpcapBpfHeader structure. + The suggested way to use an AirPcap adapter is through the pcap API exported by wpcap.dll. If this is not + possible, the Capture_radio and Capture_no_radio examples in the AirPcap developer's pack show how to properly decode the + packets in the read buffer returned by AirpcapRead(). + + \note this function is NOT blocking. Blocking behavior can be obtained using the event returned + by \ref AirpcapGetReadEvent(). See also \ref AirpcapSetMinToCopy(). +*/ +gboolean AirpcapRead(PAirpcapHandle AdapterHandle, guint8 * Buffer, guint BufSize, guint * PReceievedBytes); + +/*! + \brief Transmits a packet. + \param AdapterHandle Handle to the adapter. + \param TxPacket Pointer to a buffer that contains the packet to be transmitted. + \param PacketLen Length of the buffer pointed by the TxPacket argument, in bytes. + \return TRUE on success. + + The packet will be transmitted on the channel the device is currently set. To change the device adapter, use the + \ref AirpcapSetDeviceChannel() function. + + If the linktype of the adapter is AIRPCAP_LT_802_11, the buffer pointed by TxPacket should contain just the 802.11 + packet, without additional information. The packet will be transmitted at 1Mbps. + + If the linktype of the adapter is AIRPCAP_LT_802_11_PLUS_RADIO, the buffer pointed by TxPacket should contain a radiotap + header followed by the 802.11 packet. AirpcapWrite will use the rate information in the radiotap header when + transmitting the packet. +*/ +gboolean AirpcapWrite(PAirpcapHandle AdapterHandle, gchar * TxPacket, guint32 PacketLen); + +/*! + \brief Get per-adapter WinPcap-compatible capture statistics. + \param AdapterHandle Handle to the adapter. + \param PStats pointer to a user-allocated AirpcapStats structure that will be filled with statistical information. + \return TRUE on success. +*/ +gboolean AirpcapGetStats(PAirpcapHandle AdapterHandle, PAirpcapStats PStats); + +/*! + \brief Get the number of LEDs the referenced adapter has available. + \param AdapterHandle Handle to the adapter. + \param NumberOfLeds Number of LEDs available on this adapter. + \return TRUE on success. +*/ +gboolean AirpcapGetLedsNumber(PAirpcapHandle AdapterHandle, guint * NumberOfLeds); + +/*! + \brief Turn on one of the adapter's LEDs. + \param AdapterHandle Handle to the adapter. + \param LedNumber zero-based identifier of the LED to turn on. + \return TRUE on success. +*/ +gboolean AirpcapTurnLedOn(PAirpcapHandle AdapterHandle, guint LedNumber); + +/*! + \brief Turn off one of the adapter's LEDs. + \param AdapterHandle Handle to the adapter. + \param LedNumber zero-based identifier of the LED to turn off. + \return TRUE on success. +*/ +gboolean AirpcapTurnLedOff(PAirpcapHandle AdapterHandle, guint LedNumber); + +/*! + \brief Set the channel of a device through its radio frequency. In case of 802.11n enabled devices, it sets the extension channel, if used. + \param AdapterHandle Handle to the adapter. + \param ChannelInfo The new channel information to set. + \return TRUE on success. + + \note this is a device-related function: when you change the channel from an open capture instance, the change will be + immediately reflected on all the other capture instances. +*/ +gboolean AirpcapSetDeviceChannelEx(PAirpcapHandle AdapterHandle, AirpcapChannelInfo ChannelInfo); + +/*! + \brief Get the channel of a device through its radiofrequency. In case of 802.11n enabled devices, it gets the extension channel, if in use. + \param AdapterHandle Handle to the adapter. + \param PChannelInfo Pointer to a user-supplied variable into which the function will copy the currently configured channel information. + \return TRUE on success. + + \note this is a device-related function: when you change the channel from an open capture instance, the change will be + immediately reflected on all the other capture instances. +*/ +gboolean AirpcapGetDeviceChannelEx(PAirpcapHandle AdapterHandle, PAirpcapChannelInfo PChannelInfo); + +/*! + \brief Get the list of supported channels for a given device. In case of a 802.11n capable device, information related to supported extension channels is also reported. + + Every control channel is listed multiple times, one for each different supported extension channel. For example channel 6 (2437MHz) is usually listed three times: + - Frequency 2437 Extension +1. Control channel is 6, extension channel is 10. + - Frequency 2437 Extension 0. Control channel is 6, no extension channel is used (20MHz channel and legacy mode). + - Frequency 2437 Extension -1. Control channel is 6, extension channel is 2. + \param AdapterHandle Handle to the adapter. + \param ppChannelInfo Pointer to a user-supplied variable that will point to an array of supported channel. Such list must not be freed by the caller + \param pNumChannelInfo Number of channels returned in the array. + \return TRUE on success. + + \note The supported channels are not listed in any specific order. +*/ +gboolean AirpcapGetDeviceSupportedChannels(PAirpcapHandle AdapterHandle, PAirpcapChannelInfo *ppChannelInfo, guint * pNumChannelInfo); + +/*! + \brief Converts a given frequency to the corresponding channel. + + \param Frequency Frequency of the channel, in MHz. + \param PChannel Pointer to a user-supplied variable that will contain the channel number on success. + \param PBand Pointer to a user-supplied variable that will contain the band (a or b/g) of the given channel. + \return TRUE on success, i.e. the frequency corresponds to a valid a or b/g channel. +*/ +gboolean AirpcapConvertFrequencyToChannel(guint Frequency, guint * PChannel, PAirpcapChannelBand PBand); + +/*! + \brief Converts a given channel to the corresponding frequency. + + \param Channel Channel number to be converted. + \param PFrequency Pointer to a user-supplied variable that will contain the channel frequency in MHz on success. + \return TRUE on success, i.e. the given channel number exists. +*/ +gboolean AirpcapConvertChannelToFrequency(guint Channel, guint * PFrequency); + + +/*@}*/ + +#endif /* __AIRPCAP_DRIVER__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(AIRPCAP_H__EAE405F5_0171_9592_B3C2_C19EC426AD34__INCLUDED_) */ diff --git a/capture/airpcap_loader.c b/capture/airpcap_loader.c new file mode 100644 index 0000000000..4e83953fdb --- /dev/null +++ b/capture/airpcap_loader.c @@ -0,0 +1,1230 @@ +/* airpcap_loader.c + * + * Giorgio Tino + * Copyright (c) CACE Technologies, LLC 2006 + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 2000 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#include +#include + + +/* + * Set to TRUE if the DLL was successfully loaded AND all functions + * are present. + */ +static gboolean AirpcapLoaded = FALSE; + +#ifdef _WIN32 +/* + * We load dynamically the dag library in order link it only when + * it's present on the system + */ +static void * AirpcapLib = NULL; + +static AirpcapGetLastErrorHandler g_PAirpcapGetLastError; +static AirpcapSetKernelBufferHandler g_PAirpcapSetKernelBuffer; +static AirpcapSetFilterHandler g_PAirpcapSetFilter; +static AirpcapGetMacAddressHandler g_PAirpcapGetMacAddress; +static AirpcapSetMinToCopyHandler g_PAirpcapSetMinToCopy; +static AirpcapGetReadEventHandler g_PAirpcapGetReadEvent; +static AirpcapReadHandler g_PAirpcapRead; +static AirpcapGetStatsHandler g_PAirpcapGetStats; +#endif + +static int AirpcapVersion = 3; + +static AirpcapGetDeviceListHandler g_PAirpcapGetDeviceList; +static AirpcapFreeDeviceListHandler g_PAirpcapFreeDeviceList; +static AirpcapOpenHandler g_PAirpcapOpen; +static AirpcapCloseHandler g_PAirpcapClose; +static AirpcapGetLinkTypeHandler g_PAirpcapGetLinkType; +static AirpcapSetLinkTypeHandler g_PAirpcapSetLinkType; +static AirpcapTurnLedOnHandler g_PAirpcapTurnLedOn; +static AirpcapTurnLedOffHandler g_PAirpcapTurnLedOff; +static AirpcapGetDeviceChannelHandler g_PAirpcapGetDeviceChannel; +static AirpcapSetDeviceChannelHandler g_PAirpcapSetDeviceChannel; +static AirpcapGetFcsPresenceHandler g_PAirpcapGetFcsPresence; +static AirpcapSetFcsPresenceHandler g_PAirpcapSetFcsPresence; +static AirpcapGetFcsValidationHandler g_PAirpcapGetFcsValidation; +static AirpcapSetFcsValidationHandler g_PAirpcapSetFcsValidation; +static AirpcapGetDeviceKeysHandler g_PAirpcapGetDeviceKeys; +static AirpcapSetDeviceKeysHandler g_PAirpcapSetDeviceKeys; +static AirpcapGetDriverKeysHandler g_PAirpcapGetDriverKeys; +static AirpcapSetDriverKeysHandler g_PAirpcapSetDriverKeys; +static AirpcapGetDecryptionStateHandler g_PAirpcapGetDecryptionState; +static AirpcapSetDecryptionStateHandler g_PAirpcapSetDecryptionState; +static AirpcapGetDriverDecryptionStateHandler g_PAirpcapGetDriverDecryptionState; +static AirpcapSetDriverDecryptionStateHandler g_PAirpcapSetDriverDecryptionState; +static AirpcapStoreCurConfigAsAdapterDefaultHandler g_PAirpcapStoreCurConfigAsAdapterDefault; +static AirpcapGetVersionHandler g_PAirpcapGetVersion; +static AirpcapSetDeviceChannelExHandler g_PAirpcapSetDeviceChannelEx; +static AirpcapGetDeviceChannelExHandler g_PAirpcapGetDeviceChannelEx; +static AirpcapGetDeviceSupportedChannelsHandler g_PAirpcapGetDeviceSupportedChannels; + +/* Airpcap interface list */ +GList *g_airpcap_if_list = NULL; + +/* Airpcap current selected interface */ +airpcap_if_info_t *airpcap_if_selected = NULL; + +/* Airpcap current active interface */ +airpcap_if_info_t *airpcap_if_active = NULL; + +Dot11Channel *pSupportedChannels; +guint numSupportedChannels; + +static AirpcapChannelInfo LegacyChannels[] = +{ + {2412, 0, {0,0,0}}, + {2417, 0, {0,0,0}}, + {2422, 0, {0,0,0}}, + {2427, 0, {0,0,0}}, + {2432, 0, {0,0,0}}, + {2437, 0, {0,0,0}}, + {2442, 0, {0,0,0}}, + {2447, 0, {0,0,0}}, + {2452, 0, {0,0,0}}, + {2457, 0, {0,0,0}}, + {2462, 0, {0,0,0}}, + {2467, 0, {0,0,0}}, + {2472, 0, {0,0,0}}, + {2484, 0, {0,0,0}}, +}; + +static guint num_legacy_channels = 14; + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_airpcap_interface_list()". + */ +static gchar * +cant_get_airpcap_if_list_error_message(const char *err_str) +{ + return g_strdup_printf("Can't get list of Wireless interfaces: %s", err_str); +} + +/* + * Airpcap wrapper, used to store the current settings for the selected adapter + */ +gboolean +airpcap_if_store_cur_config_as_adapter_default(PAirpcapHandle ah) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapStoreCurConfigAsAdapterDefault(ah); +} + +/* + * Airpcap wrapper, used to open an airpcap adapter + */ +PAirpcapHandle +airpcap_if_open(gchar * name, gchar * err) +{ + if (!AirpcapLoaded) return NULL; + if (name == NULL) return NULL; + return g_PAirpcapOpen(name,err); +} + +/* + * Airpcap wrapper, used to close an airpcap adapter + */ +void +airpcap_if_close(PAirpcapHandle handle) +{ + if (!AirpcapLoaded) return; + g_PAirpcapClose(handle); +} + +/* + * Retrieve the state of the Airpcap DLL + */ +int +airpcap_get_dll_state(void) +{ + return AirpcapVersion; +} + +/* + * Airpcap wrapper, used to turn on the led of an airpcap adapter + */ +gboolean +airpcap_if_turn_led_on(PAirpcapHandle AdapterHandle, guint LedNumber) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapTurnLedOn(AdapterHandle,LedNumber); +} + +/* + * Airpcap wrapper, used to turn off the led of an airpcap adapter + */ +gboolean +airpcap_if_turn_led_off(PAirpcapHandle AdapterHandle, guint LedNumber) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapTurnLedOff(AdapterHandle,LedNumber); +} + +/* + * Airpcap wrapper, used to get the channel of an airpcap adapter + */ +gboolean +airpcap_if_get_device_channel(PAirpcapHandle ah, guint * ch) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapGetDeviceChannel(ah,ch); +} + +/* + * Airpcap wrapper, used to get the supported channels of an airpcap adapter + */ +gboolean +airpcap_if_get_device_supported_channels(PAirpcapHandle ah, AirpcapChannelInfo **cInfo, guint * nInfo) +{ + if (!AirpcapLoaded) return FALSE; + if (airpcap_get_dll_state() == AIRPCAP_DLL_OLD) { + *nInfo = num_legacy_channels; + *cInfo = (AirpcapChannelInfo*)&LegacyChannels; + + return TRUE; + } else if (airpcap_get_dll_state() == AIRPCAP_DLL_OK) { + return g_PAirpcapGetDeviceSupportedChannels(ah, cInfo, nInfo); + } + return FALSE; +} + +/* + * Airpcap wrapper, used to get the supported channels of an airpcap adapter + */ +Dot11Channel* +airpcap_if_get_device_supported_channels_array(PAirpcapHandle ah, guint * pNumSupportedChannels) +{ + AirpcapChannelInfo *chanInfo; + guint numInfo = 0; + + if (!AirpcapLoaded) + return NULL; + if (airpcap_if_get_device_supported_channels(ah, &chanInfo, &numInfo) == FALSE) + return NULL; + numSupportedChannels = 0; + + /* + * allocate a bigger array + */ + if (numInfo == 0) + return NULL; + + pSupportedChannels = (Dot11Channel *)g_malloc(numInfo * (sizeof *pSupportedChannels)); + + for (guint i = 0; i < numInfo; i++) + { + guint supportedChannel = G_MAXUINT; + + /* + * search if we have it already + */ + for (guint j = 0; j < numSupportedChannels; j++) + { + if (pSupportedChannels[j].Frequency == chanInfo[i].Frequency) + { + supportedChannel = j; + break; + } + } + + if (supportedChannel == G_MAXUINT) + { + /* + * not found, create a new item + */ + pSupportedChannels[numSupportedChannels].Frequency = chanInfo[i].Frequency; + + switch(chanInfo[i].ExtChannel) + { + case -1: + pSupportedChannels[numSupportedChannels].Flags = FLAG_CAN_BE_LOW; + break; + case +1: + pSupportedChannels[numSupportedChannels].Flags = FLAG_CAN_BE_HIGH; + break; + case 0: + default: + pSupportedChannels[numSupportedChannels].Flags = 0; + } + + /* + * Gather channel information + */ + + pSupportedChannels[numSupportedChannels].Flags |= + FREQ_IS_BG(pSupportedChannels[numSupportedChannels].Frequency) ? + FLAG_IS_BG_CHANNEL : FLAG_IS_A_CHANNEL; + pSupportedChannels[numSupportedChannels].Channel = + ieee80211_mhz_to_chan(pSupportedChannels[numSupportedChannels].Frequency); + numSupportedChannels++; + } + else + { + /* + * just update the ext channel flags + */ + switch(chanInfo[i].ExtChannel) + { + case -1: + pSupportedChannels[supportedChannel].Flags |= FLAG_CAN_BE_LOW; + break; + case +1: + pSupportedChannels[supportedChannel].Flags |= FLAG_CAN_BE_HIGH; + break; + case 0: + default: + break; + } + } + } + + if (numSupportedChannels < 1) + return NULL; + /* + * Now sort the list by frequency + */ + for (guint i = 0; i < numSupportedChannels - 1; i++) + { + for (guint j = i + 1; j < numSupportedChannels; j++) + { + if (pSupportedChannels[i].Frequency > pSupportedChannels[j].Frequency) + { + Dot11Channel temp = pSupportedChannels[i]; + pSupportedChannels[i] = pSupportedChannels[j]; + pSupportedChannels[j] = temp; + } + } + } + + *pNumSupportedChannels = numSupportedChannels; + return pSupportedChannels; +} + +/* + * Airpcap wrapper, used to set the channel of an airpcap adapter + */ +gboolean +airpcap_if_set_device_channel(PAirpcapHandle ah, guint ch) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapSetDeviceChannel(ah,ch); +} + +/* + * Airpcap wrapper, used to set the frequency of an airpcap adapter + */ +gboolean +airpcap_if_set_device_channel_ex(PAirpcapHandle ah, AirpcapChannelInfo ChannelInfo) +{ + if (!AirpcapLoaded) return FALSE; + if (airpcap_get_dll_state() == AIRPCAP_DLL_OLD){ + gint channel = 0; + channel = ieee80211_mhz_to_chan(ChannelInfo.Frequency); + + if (channel < 0){ + return FALSE; + } else { + return airpcap_if_set_device_channel(ah, channel); + } + } else if (airpcap_get_dll_state() == AIRPCAP_DLL_OK){ + return g_PAirpcapSetDeviceChannelEx (ah, ChannelInfo); + } + + return FALSE; +} + +/* + * Airpcap wrapper, used to get the frequency of an airpcap adapter + */ +gboolean +airpcap_if_get_device_channel_ex(PAirpcapHandle ah, PAirpcapChannelInfo pChannelInfo) +{ + if (!AirpcapLoaded) return FALSE; + + pChannelInfo->Frequency = 0; + pChannelInfo->ExtChannel = 0; + pChannelInfo->Reserved[0] = 0; + pChannelInfo->Reserved[1] = 0; + pChannelInfo->Reserved[2] = 0; + + if (airpcap_get_dll_state() == AIRPCAP_DLL_OLD){ + guint channel = 0; + guint chan_freq = 0; + + if (!airpcap_if_get_device_channel(ah, &channel)) return FALSE; + + chan_freq = ieee80211_chan_to_mhz(channel, TRUE); + if (chan_freq == 0) return FALSE; + pChannelInfo->Frequency = chan_freq; + + return TRUE; + } else if (airpcap_get_dll_state() == AIRPCAP_DLL_OK){ + return g_PAirpcapGetDeviceChannelEx (ah, pChannelInfo); + } + return FALSE; +} + +/* + * Airpcap wrapper, used to get the link type of an airpcap adapter + */ +gboolean +airpcap_if_get_link_type(PAirpcapHandle ah, PAirpcapLinkType lt) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapGetLinkType(ah,lt); +} + +/* + * Airpcap wrapper, used to set the link type of an airpcap adapter + */ +gboolean +airpcap_if_set_link_type(PAirpcapHandle ah, AirpcapLinkType lt) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapSetLinkType(ah,lt); +} + +/* + * Airpcap wrapper, used to get the fcs presence of an airpcap adapter + */ +gboolean +airpcap_if_get_fcs_presence(PAirpcapHandle ah, gboolean * fcs) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapGetFcsPresence(ah,fcs); +} + +/* + * Airpcap wrapper, used to set the fcs presence of an airpcap adapter + */ +gboolean +airpcap_if_set_fcs_presence(PAirpcapHandle ah, gboolean fcs) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapSetFcsPresence(ah,fcs); +} + +/* + * Airpcap wrapper, used to get the decryption enabling of an airpcap adapter + */ +gboolean +airpcap_if_get_decryption_state(PAirpcapHandle ah, PAirpcapDecryptionState PEnable) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapGetDecryptionState(ah,PEnable); +} + +/* + * Airpcap wrapper, used to set the decryption enabling of an airpcap adapter + */ +gboolean +airpcap_if_set_decryption_state(PAirpcapHandle ah, AirpcapDecryptionState Enable) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapSetDecryptionState(ah,Enable); +} + +/* + * Airpcap wrapper, used to get the decryption enabling of an airpcap driver + */ +gboolean +airpcap_if_get_driver_decryption_state(PAirpcapHandle ah, PAirpcapDecryptionState PEnable) +{ + if (!AirpcapLoaded || (g_PAirpcapGetDriverDecryptionState==NULL)) return FALSE; + return g_PAirpcapGetDriverDecryptionState(ah,PEnable); +} + +/* + * Airpcap wrapper, used to set the decryption enabling of an airpcap driver + */ +gboolean +airpcap_if_set_driver_decryption_state(PAirpcapHandle ah, AirpcapDecryptionState Enable) +{ + if (!AirpcapLoaded || (g_PAirpcapSetDriverDecryptionState==NULL)) return FALSE; + return g_PAirpcapSetDriverDecryptionState(ah,Enable); +} + +/* + * Airpcap wrapper, used to get the fcs validation of an airpcap adapter + */ +gboolean +airpcap_if_get_fcs_validation(PAirpcapHandle ah, PAirpcapValidationType val) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapGetFcsValidation(ah,val); +} + +/* + * Airpcap wrapper, used to set the fcs validation of an airpcap adapter + */ +gboolean +airpcap_if_set_fcs_validation(PAirpcapHandle ah, AirpcapValidationType val) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapSetFcsValidation(ah,val); +} + +/* + * Airpcap wrapper, used to save the settings for the selected_if + */ +gboolean +airpcap_if_set_device_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapSetDeviceKeys(AdapterHandle,KeysCollection); +} + +/* + * Airpcap wrapper, used to save the settings for the selected_if + */ +gboolean +airpcap_if_get_device_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize) +{ + if (!AirpcapLoaded) return FALSE; + return g_PAirpcapGetDeviceKeys(AdapterHandle,KeysCollection,PKeysCollectionSize); +} + +/* + * Airpcap wrapper, used to save the driver's set of keys + */ +gboolean +airpcap_if_set_driver_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection) +{ + if (!AirpcapLoaded || (g_PAirpcapSetDriverKeys==NULL)) return FALSE; + return g_PAirpcapSetDriverKeys(AdapterHandle,KeysCollection); +} + +/* + * Airpcap wrapper, used to load the driver's set of keys + */ +gboolean +airpcap_if_get_driver_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize) +{ + if (!AirpcapLoaded || (g_PAirpcapGetDriverKeys==NULL)) return FALSE; + return g_PAirpcapGetDriverKeys(AdapterHandle,KeysCollection,PKeysCollectionSize); +} + +/* + * This function will create a new airpcap_if_info_t using a name and a description + */ +airpcap_if_info_t * +airpcap_if_info_new(char *name, char *description) +{ + PAirpcapHandle ad; + gchar ebuf[AIRPCAP_ERRBUF_SIZE]; + + airpcap_if_info_t *if_info = NULL; + + /* Probably I have to switch on the leds!!! */ + ad = airpcap_if_open(name, ebuf); + if (ad) + { + if_info = g_new0(airpcap_if_info_t, 1); + if_info->name = g_strdup(name); + if (description == NULL){ + if_info->description = NULL; + }else{ + if_info->description = g_strdup(description); + } + + if_info->ip_addr = NULL; + if_info->loopback = FALSE; + airpcap_if_get_fcs_validation(ad,&(if_info->CrcValidationOn)); + airpcap_if_get_fcs_presence(ad,&(if_info->IsFcsPresent)); + airpcap_if_get_link_type(ad,&(if_info->linkType)); + airpcap_if_get_device_channel_ex(ad,&(if_info->channelInfo)); + if_info->pSupportedChannels = airpcap_if_get_device_supported_channels_array(ad, &(if_info->numSupportedChannels)); + airpcap_if_turn_led_on(ad, 0); + airpcap_if_get_decryption_state(ad, &(if_info->DecryptionOn)); + if_info->led = TRUE; + if_info->blinking = FALSE; + if_info->saved = TRUE; /* NO NEED TO BE SAVED */ + + /* get the keys, if everything is ok, close the adapter */ + if (airpcap_if_load_keys(ad,if_info)) + { + airpcap_if_close(ad); + } + } + return if_info; +} + +/* + * This function will create a new fake drivers' interface, to load global keys... + */ +airpcap_if_info_t* +airpcap_driver_fake_if_info_new(void) +{ + PAirpcapHandle ad; + gchar ebuf[AIRPCAP_ERRBUF_SIZE]; + + airpcap_if_info_t *if_info = NULL; + airpcap_if_info_t *fake_if_info = NULL; + + /* Maybe for some reason no airpcap adapter is found */ + if (g_airpcap_if_list == NULL) + return NULL; + + /* + * Retrieve the first AirPcap adapter available. If no interface is found, + * it is not possible to retrieve the driver's settings, so return NULL. + */ + if_info = (airpcap_if_info_t *)g_list_nth_data(g_airpcap_if_list,0); + if (if_info == NULL) + return NULL; + + /* Open the 'fake' adapter */ + ad = airpcap_if_open(if_info->name, ebuf); + if (ad) + { + fake_if_info = g_new0(airpcap_if_info_t, 1); + fake_if_info->name = g_strdup(if_info->name); + fake_if_info->description = g_strdup(if_info->description); + fake_if_info->loopback = FALSE; + fake_if_info->ip_addr = NULL; + airpcap_if_get_driver_decryption_state(ad, &(fake_if_info->DecryptionOn)); + airpcap_if_get_fcs_validation(ad,&(fake_if_info->CrcValidationOn)); + airpcap_if_get_fcs_presence(ad,&(fake_if_info->IsFcsPresent)); + airpcap_if_get_link_type(ad,&(fake_if_info->linkType)); + airpcap_if_get_device_channel_ex(ad,&(fake_if_info->channelInfo)); + airpcap_if_turn_led_on(ad, 0); + fake_if_info->led = TRUE; + fake_if_info->blinking = FALSE; + fake_if_info->saved = TRUE; /* NO NEED TO BE SAVED */ + + /* get the keys, if everything is ok, close the adapter */ + if (airpcap_if_load_driver_keys(ad,fake_if_info)) + { + airpcap_if_close(ad); + } + } + + return fake_if_info; +} + +#ifdef AIRPCAP_DEBUG +/* + * USED FOR DEBUG ONLY... PRINTS AN AirPcap ADAPTER STRUCTURE in a fancy way. + */ +void +airpcap_if_info_print(airpcap_if_info_t* if_info) +{ + guint i; + if (if_info == NULL) + { + g_print("\nWARNING : AirPcap Interface pointer is NULL.\n"); + return; + } + + g_print("\n----------------- AirPcap Interface \n"); + g_print(" NAME: %s\n",if_info->name); + g_print(" DESCRIPTION: %s\n",if_info->description); + g_print(" BLINKING: %s\n",if_info->blinking ? "TRUE" : "FALSE"); + g_print(" channelInfo.Frequency: %u\n",if_info->channelInfo.Frequency); + g_print(" channelInfo.ExtChannel: %d\n",if_info->channelInfo.ExtChannel); + g_print(" CRCVALIDATION: %s\n",if_info->CrcValidationOn ? "ON" : "OFF"); + g_print(" DECRYPTION: %s\n",if_info->DecryptionOn ? "ON" : "OFF"); + g_print(" IP ADDR: %s\n",if_info->ip_addr!=NULL ? "NOT NULL" : "NULL"); + g_print(" FCSPRESENT: %s\n",if_info->IsFcsPresent ? "TRUE" : "FALSE"); + g_print(" KEYSCOLLECTION: %s\n",if_info->keysCollection!=NULL ? "NOT NULL" : "NULL"); + g_print(" KEYSCOLLECTIONSIZE: %u\n",if_info->keysCollectionSize); + g_print(" LED: %s\n",if_info->led ? "ON" : "OFF"); + g_print(" LINKTYPE: %d\n",if_info->linkType); + g_print(" LOOPBACK: %s\n",if_info->loopback ? "YES" : "NO"); + g_print(" (GTK) TAG: %d\n",if_info->tag); + g_print("SUPPORTED CHANNELS POINTER: %p\n",if_info->pSupportedChannels); + g_print(" NUM SUPPORTED CHANNELS: %u\n",if_info->numSupportedChannels); + + for(i=0; i<(if_info->numSupportedChannels); i++){ + g_print("\n SUPPORTED CHANNEL #%u\n",i+1); + g_print(" CHANNEL: %u\n",if_info->pSupportedChannels[i].Channel); + g_print(" FREQUENCY: %u\n",if_info->pSupportedChannels[i].Frequency); + g_print(" FLAGS: %u\n",if_info->pSupportedChannels[i].Flags); + } + g_print("\n\n"); +} +#endif /* AIRPCAP_DEBUG */ + +/* + * Function used to load the WEP keys for a selected interface + */ +gboolean +airpcap_if_load_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info) +{ + if (!if_info) return FALSE; + + if_info->keysCollectionSize = 0; + if_info->keysCollection = NULL; + + if (!airpcap_if_get_device_keys(ad, NULL, &(if_info->keysCollectionSize))) + { + if (if_info->keysCollectionSize == 0) + { + if_info->keysCollection = NULL; + airpcap_if_close(ad); + return FALSE; + } + + if_info->keysCollection = (PAirpcapKeysCollection)g_malloc(if_info->keysCollectionSize); + if (!if_info->keysCollection) + { + if_info->keysCollectionSize = 0; + if_info->keysCollection = NULL; + airpcap_if_close(ad); + return FALSE; + } + + airpcap_if_get_device_keys(ad, if_info->keysCollection, &(if_info->keysCollectionSize)); + return TRUE; + } + + airpcap_if_close(ad); + return FALSE; +} + +/* + * Function used to load the WEP keys for a selected interface + */ +gboolean +airpcap_if_load_driver_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info) +{ + if_info->keysCollectionSize = 0; + if_info->keysCollection = NULL; + + if (!airpcap_if_get_driver_keys(ad, NULL, &(if_info->keysCollectionSize))) + { + if (if_info->keysCollectionSize == 0) + { + if_info->keysCollection = NULL; + airpcap_if_close(ad); + return FALSE; + } + + if_info->keysCollection = (PAirpcapKeysCollection)g_malloc(if_info->keysCollectionSize); + if (!if_info->keysCollection) + { + if_info->keysCollectionSize = 0; + if_info->keysCollection = NULL; + airpcap_if_close(ad); + return FALSE; + } + + airpcap_if_get_driver_keys(ad, if_info->keysCollection, &(if_info->keysCollectionSize)); + return TRUE; + } + + airpcap_if_close(ad); + return FALSE; +} + +/* + * Function used to save the WEP keys for a selected interface + */ +void +airpcap_if_save_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info) +{ + if (!if_info || !AirpcapLoaded) return; + + if (if_info->keysCollection != NULL) + g_PAirpcapSetDeviceKeys(ad,if_info->keysCollection); +} + +/* + * Function used to save the WEP keys for a selected interface + */ +void +airpcap_if_save_driver_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info) +{ + if (if_info->keysCollection != NULL) + airpcap_if_set_driver_keys(ad,if_info->keysCollection); +} + +/* + * Callback used to free an instance of airpcap_if_info_t + */ +static void +free_airpcap_if_cb(gpointer data, gpointer user_data _U_) +{ + airpcap_if_info_t *if_info = (airpcap_if_info_t *)data; + + if (NULL == if_info) + return; + + g_free(if_info->name); + + g_free(if_info->description); + + /* XXX - FREE THE WEP KEY LIST HERE!!!*/ + if (if_info->keysCollection != NULL) + { + g_free(if_info->keysCollection); + if_info->keysCollection = NULL; + } + + g_slist_free(if_info->ip_addr); + + g_free(if_info); +} + +/* + * Function used to free the airpcap interface list + */ +void +free_airpcap_interface_list(GList *if_list) +{ + g_list_foreach(if_list, free_airpcap_if_cb, NULL); + g_list_free(if_list); +} + +/* + * This function will use the airpcap.dll to find all the airpcap devices. + * Will return null if no device is found. + */ +GList* +get_airpcap_interface_list(int *err, char **err_str) +{ + GList *il = NULL; + airpcap_if_info_t *if_info; + int n_adapts; + AirpcapDeviceDescription *devsList, *adListEntry; + char errbuf[AIRPCAP_ERRBUF_SIZE]; + + *err = 0; + + if (!AirpcapLoaded) + { + *err = AIRPCAP_NOT_LOADED; + return il; + } + + if (!g_PAirpcapGetDeviceList(&devsList, errbuf)) + { + /* No interfaces, return il = NULL; */ + *err = CANT_GET_AIRPCAP_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_airpcap_if_list_error_message(errbuf); + return il; + } + + /* + * Count the adapters + */ + adListEntry = devsList; + n_adapts = 0; + while(adListEntry) + { + n_adapts++; + adListEntry = adListEntry->next; + } + + if (n_adapts == 0) + { + /* No interfaces, return il= NULL */ + g_PAirpcapFreeDeviceList(devsList); + *err = NO_AIRPCAP_INTERFACES_FOUND; + if (err_str != NULL) + *err_str = NULL; + return il; + } + + /* + * Insert the adapters in our list + */ + adListEntry = devsList; + while(adListEntry) + { + if_info = airpcap_if_info_new(adListEntry->Name, adListEntry->Description); + if (if_info != NULL){ + il = g_list_append(il, if_info); + } + + adListEntry = adListEntry->next; + } + + g_PAirpcapFreeDeviceList(devsList); + + return il; +} + +/* + * Used to retrieve the interface given the name + * (the name is used in AirpcapOpen) + */ +airpcap_if_info_t* get_airpcap_if_from_name(GList* if_list, const gchar* name) +{ + GList* curr; + airpcap_if_info_t* if_info; + + for (curr = g_list_first(if_list); curr; curr = g_list_next(curr)) { + if_info = (airpcap_if_info_t *)curr->data; + if (if_info && (g_ascii_strcasecmp(if_info->name, name) == 0)) { + return (if_info); + } + /* Try the name without the "\\.\" prefix. */ + if (strlen(if_info->name) > 4 && (g_ascii_strcasecmp(if_info->name + 4, name) == 0)) { + return (if_info); + } + } + return (NULL); +} + +/* + * Clear keys and decryption status for the specified interface + */ +void +airpcap_if_clear_decryption_settings(airpcap_if_info_t* info_if) +{ + if (info_if != NULL) + { + if (info_if->keysCollection != NULL) + { + g_free(info_if->keysCollection); + info_if->keysCollection = NULL; + } + + info_if->keysCollectionSize = 0; + + info_if->DecryptionOn = AIRPCAP_DECRYPTION_OFF; + info_if->saved = FALSE; + } +} + +/* + * Used to retrieve the two chars string from interface + */ +gchar* +airpcap_get_if_string_number(airpcap_if_info_t* if_info) +{ + gchar* number; + guint n; + int a; + + a = sscanf(if_info->name,AIRPCAP_DEVICE_NUMBER_EXTRACT_STRING,&n); + + /* If sscanf() returned 1, it means that has read a number, so interface is not "Any" + * Otherwise, check if it is the "Any" adapter... + */ + if (a == 0) + { + if (g_ascii_strcasecmp(if_info->name,AIRPCAP_DEVICE_ANY_EXTRACT_STRING)!=0) + number = g_strdup("??"); + else + number = g_strdup(AIRPCAP_CHANNEL_ANY_NAME); + } + else + { + number = g_strdup_printf("%.2u",n); + } + + return number; +} + +/* + * Used to retrieve the two chars string from interface + */ +gchar* +airpcap_get_if_string_number_from_description(gchar* description) +{ + gchar* number; + gchar* pointer; + + number = g_new(gchar, 3); + + pointer = g_strrstr(description,"#\0"); + + number[0] = *(pointer+1); + number[1] = *(pointer+2); + number[2] = '\0'; + + return number; +} + +/* + * Load the configuration for the specified interface + */ +void +airpcap_load_selected_if_configuration(airpcap_if_info_t* if_info) +{ + gchar ebuf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle ad; + + if (if_info != NULL) + { + ad = airpcap_if_open(if_info->name, ebuf); + + if (ad) + { + /* Stop blinking (if it was blinking!)*/ + if (if_info->blinking) + { + /* Turn on the light (if it was off) */ + if (!(if_info->led)) airpcap_if_turn_led_on(ad, 0); + } + + /* Apply settings... */ + airpcap_if_get_device_channel_ex(ad,&(if_info->channelInfo)); + airpcap_if_get_fcs_validation(ad,&(if_info->CrcValidationOn)); + airpcap_if_get_fcs_presence(ad,&(if_info->IsFcsPresent)); + airpcap_if_get_link_type(ad,&(if_info->linkType)); + airpcap_if_get_decryption_state(ad, &(if_info->DecryptionOn)); + /* get the keys, if everything is ok, close the adapter */ + if (airpcap_if_load_keys(ad,if_info)) + airpcap_if_close(ad); + + if_info->saved = TRUE; + } +#if 0 + else + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, " Error in opening adapter for %s",if_info->description); + } +#endif + } +} + +/* + * Save the configuration for the specified interface + */ +void +airpcap_save_selected_if_configuration(airpcap_if_info_t* if_info) +{ + gchar ebuf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle ad; + + if (if_info != NULL) + { + ad = airpcap_if_open(if_info->name, ebuf); + + if (ad) + { + /* Stop blinking (if it was blinking!)*/ + if (if_info->blinking) + { + /* Turn on the light (if it was off) */ + if (!(if_info->led)) airpcap_if_turn_led_on(ad, 0); + } + + /* Apply settings... */ + airpcap_if_set_device_channel_ex(ad,if_info->channelInfo); + airpcap_if_set_fcs_validation(ad,if_info->CrcValidationOn); + airpcap_if_set_fcs_presence(ad,if_info->IsFcsPresent); + airpcap_if_set_link_type(ad,if_info->linkType); + airpcap_if_set_decryption_state(ad, if_info->DecryptionOn); + airpcap_if_save_keys(ad,if_info); + + /* ... and save them */ + if (!airpcap_if_store_cur_config_as_adapter_default(ad)) + { +#if 0 + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Cannot save Wireless configuration!!!\nRemember that in order to store the configuration in the registry you have to:\n\n- Close all the airpcap-based applications.\n- Be sure to have administrative privileges."); +#endif + if_info->saved = FALSE; + airpcap_if_close(ad); + return; + } + + if_info->saved = TRUE; + airpcap_if_close(ad); + } +#if 0 + else + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, " Error in opening adapter for %s",if_info->description); + } +#endif + } +} + +/* + * Save the configuration for the specified interface + */ +void +airpcap_save_driver_if_configuration(airpcap_if_info_t* fake_if_info) +{ + gchar ebuf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle ad; + + if (fake_if_info != NULL) + { + ad = airpcap_if_open(fake_if_info->name, ebuf); + + if (ad) + { + /* Apply decryption settings... */ + airpcap_if_set_driver_decryption_state(ad, fake_if_info->DecryptionOn); + airpcap_if_save_driver_keys(ad,fake_if_info); + airpcap_if_close(ad); + } +#if 0 + else + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, " Error in opening adapter for %s",fake_if_info->description); + } +#endif + } + + return; +} + +/* + * Free an instance of airpcap_if_info_t + */ +void +airpcap_if_info_free(airpcap_if_info_t *if_info) +{ + if (if_info != NULL) + { + g_free(if_info->name); + + g_free(if_info->description); + + if (if_info->keysCollection != NULL) + { + g_free(if_info->keysCollection); + if_info->keysCollection = NULL; + } + + if (if_info->ip_addr != NULL) + { + g_slist_free(if_info->ip_addr); + if_info->ip_addr = NULL; + } + + g_free(if_info); + } +} + + +/* DYNAMIC LIBRARY LOADER */ +/* + * Used to dynamically load the airpcap library in order link it only when + * it's present on the system + */ +int load_airpcap(void) +{ +#ifdef _WIN32 + gboolean base_functions = TRUE; + gboolean eleven_n_functions = TRUE; + + if ((AirpcapLib = ws_load_library("airpcap.dll")) == NULL) + { + /* Report the error but go on */ + AirpcapVersion = AIRPCAP_DLL_NOT_FOUND; + return AirpcapVersion; + } + else + { + if ((g_PAirpcapGetLastError = (AirpcapGetLastErrorHandler) GetProcAddress(AirpcapLib, "AirpcapGetLastError")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetDeviceList = (AirpcapGetDeviceListHandler) GetProcAddress(AirpcapLib, "AirpcapGetDeviceList")) == NULL) base_functions = FALSE; + if ((g_PAirpcapFreeDeviceList = (AirpcapFreeDeviceListHandler) GetProcAddress(AirpcapLib, "AirpcapFreeDeviceList")) == NULL) base_functions = FALSE; + if ((g_PAirpcapOpen = (AirpcapOpenHandler) GetProcAddress(AirpcapLib, "AirpcapOpen")) == NULL) base_functions = FALSE; + if ((g_PAirpcapClose = (AirpcapCloseHandler) GetProcAddress(AirpcapLib, "AirpcapClose")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetLinkType = (AirpcapGetLinkTypeHandler) GetProcAddress(AirpcapLib, "AirpcapGetLinkType")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetLinkType = (AirpcapSetLinkTypeHandler) GetProcAddress(AirpcapLib, "AirpcapSetLinkType")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetKernelBuffer = (AirpcapSetKernelBufferHandler) GetProcAddress(AirpcapLib, "AirpcapSetKernelBuffer")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetFilter = (AirpcapSetFilterHandler) GetProcAddress(AirpcapLib, "AirpcapSetFilter")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetMacAddress = (AirpcapGetMacAddressHandler) GetProcAddress(AirpcapLib, "AirpcapGetMacAddress")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetMinToCopy = (AirpcapSetMinToCopyHandler) GetProcAddress(AirpcapLib, "AirpcapSetMinToCopy")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetReadEvent = (AirpcapGetReadEventHandler) GetProcAddress(AirpcapLib, "AirpcapGetReadEvent")) == NULL) base_functions = FALSE; + if ((g_PAirpcapRead = (AirpcapReadHandler) GetProcAddress(AirpcapLib, "AirpcapRead")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetStats = (AirpcapGetStatsHandler) GetProcAddress(AirpcapLib, "AirpcapGetStats")) == NULL) base_functions = FALSE; + if ((g_PAirpcapTurnLedOn = (AirpcapTurnLedOnHandler) GetProcAddress(AirpcapLib, "AirpcapTurnLedOn")) == NULL) base_functions = FALSE; + if ((g_PAirpcapTurnLedOff = (AirpcapTurnLedOffHandler) GetProcAddress(AirpcapLib, "AirpcapTurnLedOff")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetDeviceChannel = (AirpcapGetDeviceChannelHandler) GetProcAddress(AirpcapLib, "AirpcapGetDeviceChannel")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetDeviceChannel = (AirpcapSetDeviceChannelHandler) GetProcAddress(AirpcapLib, "AirpcapSetDeviceChannel")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetFcsPresence = (AirpcapGetFcsPresenceHandler) GetProcAddress(AirpcapLib, "AirpcapGetFcsPresence")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetFcsPresence = (AirpcapSetFcsPresenceHandler) GetProcAddress(AirpcapLib, "AirpcapSetFcsPresence")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetFcsValidation = (AirpcapGetFcsValidationHandler) GetProcAddress(AirpcapLib, "AirpcapGetFcsValidation")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetFcsValidation = (AirpcapSetFcsValidationHandler) GetProcAddress(AirpcapLib, "AirpcapSetFcsValidation")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetDeviceKeys = (AirpcapGetDeviceKeysHandler) GetProcAddress(AirpcapLib, "AirpcapGetDeviceKeys")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetDeviceKeys = (AirpcapSetDeviceKeysHandler) GetProcAddress(AirpcapLib, "AirpcapSetDeviceKeys")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetDecryptionState = (AirpcapGetDecryptionStateHandler) GetProcAddress(AirpcapLib, "AirpcapGetDecryptionState")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetDecryptionState = (AirpcapSetDecryptionStateHandler) GetProcAddress(AirpcapLib, "AirpcapSetDecryptionState")) == NULL) base_functions = FALSE; + if ((g_PAirpcapStoreCurConfigAsAdapterDefault = (AirpcapStoreCurConfigAsAdapterDefaultHandler) GetProcAddress(AirpcapLib, "AirpcapStoreCurConfigAsAdapterDefault")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetVersion = (AirpcapGetVersionHandler) GetProcAddress(AirpcapLib, "AirpcapGetVersion")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetDriverDecryptionState = (AirpcapGetDriverDecryptionStateHandler) GetProcAddress(AirpcapLib, "AirpcapGetDriverDecryptionState")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetDriverDecryptionState = (AirpcapSetDriverDecryptionStateHandler) GetProcAddress(AirpcapLib, "AirpcapSetDriverDecryptionState")) == NULL) base_functions = FALSE; + if ((g_PAirpcapGetDriverKeys = (AirpcapGetDriverKeysHandler) GetProcAddress(AirpcapLib, "AirpcapGetDriverKeys")) == NULL) base_functions = FALSE; + if ((g_PAirpcapSetDriverKeys = (AirpcapSetDriverKeysHandler) GetProcAddress(AirpcapLib, "AirpcapSetDriverKeys")) == NULL) base_functions = FALSE; + + /* TEST IF AIRPCAP SUPPORTS 11N */ + if ((g_PAirpcapSetDeviceChannelEx = (AirpcapSetDeviceChannelExHandler) GetProcAddress(AirpcapLib, "AirpcapSetDeviceChannelEx")) == NULL) eleven_n_functions = FALSE; + if ((g_PAirpcapGetDeviceChannelEx = (AirpcapGetDeviceChannelExHandler) GetProcAddress(AirpcapLib, "AirpcapGetDeviceChannelEx")) == NULL) eleven_n_functions = FALSE; + if ((g_PAirpcapGetDeviceSupportedChannels = (AirpcapGetDeviceSupportedChannelsHandler) GetProcAddress(AirpcapLib, "AirpcapGetDeviceSupportedChannels")) == NULL) eleven_n_functions = FALSE; + + if (base_functions && eleven_n_functions){ + AirpcapLoaded = TRUE; + AirpcapVersion = AIRPCAP_DLL_OK; + } else if (base_functions){ + AirpcapLoaded = TRUE; + AirpcapVersion = AIRPCAP_DLL_OLD; + return AIRPCAP_DLL_OK; + }else{ + AirpcapLoaded = FALSE; + AirpcapVersion = AIRPCAP_DLL_ERROR; + } + } + return AirpcapVersion; +#else /* _WIN32 */ + return AIRPCAP_DLL_NOT_FOUND; +#endif /* _WIN32 */ +} + +/* + * Append the version of AirPcap with which we were compiled to a GString. + */ +void +get_compiled_airpcap_version(GString *str) +{ + g_string_append(str, "with AirPcap"); +} + +/* + * Append the version of AirPcap with which we we're running to a GString. + */ +void +get_runtime_airpcap_version(GString *str) +{ + guint vmaj, vmin, vrev, build; + + /* See if the DLL has been loaded successfully. Bail if it hasn't */ + if (AirpcapLoaded == FALSE) { + g_string_append(str, "without AirPcap"); + return; + } + + g_PAirpcapGetVersion(&vmaj, &vmin, &vrev, &build); + g_string_append_printf(str, "with AirPcap %d.%d.%d build %d", vmaj, vmin, + vrev, build); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/capture/airpcap_loader.h b/capture/airpcap_loader.h new file mode 100644 index 0000000000..c1d612bd9f --- /dev/null +++ b/capture/airpcap_loader.h @@ -0,0 +1,407 @@ +/* airpcap_loader.h + * Declarations of routines for the "About" dialog + * + * Giorgio Tino + * Copyright (c) CACE Technologies, LLC 2006 + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __AIRPCAP_LOADER_H__ +#define __AIRPCAP_LOADER_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Error values from "get_airpcap_interface_list()". */ +#define CANT_GET_AIRPCAP_INTERFACE_LIST 0 /* error getting list */ +#define NO_AIRPCAP_INTERFACES_FOUND 1 /* list is empty */ +#define AIRPCAP_NOT_LOADED 2 /* Airpcap DLL not loaded */ + +#define AIRPCAP_CHANNEL_ANY_NAME "ANY" + +#define AIRPCAP_WEP_KEY_STRING "WEP" +/* + * XXX - WPA_PWD is the passphrase+ssid and WPA-PSK is the hexadecimal key + */ +#define AIRPCAP_WPA_PWD_KEY_STRING "WPA-PWD" +#define AIRPCAP_WPA_BIN_KEY_STRING "WPA-PSK" + +#define AIRPCAP_DLL_OK 0 +#define AIRPCAP_DLL_OLD 1 +#define AIRPCAP_DLL_ERROR 2 +#define AIRPCAP_DLL_NOT_FOUND 3 + +/* #define AIRPCAP_DEBUG 1 */ + +typedef gchar * (*AirpcapGetLastErrorHandler)(PAirpcapHandle AdapterHandle); +typedef gboolean (*AirpcapGetDeviceListHandler)(PAirpcapDeviceDescription *PPAllDevs, gchar * Ebuf); +typedef void (*AirpcapFreeDeviceListHandler)(PAirpcapDeviceDescription PAllDevs); +typedef PAirpcapHandle (*AirpcapOpenHandler)(gchar * DeviceName, gchar * Ebuf); +typedef void (*AirpcapCloseHandler)(PAirpcapHandle AdapterHandle); +typedef gboolean (*AirpcapGetLinkTypeHandler)(PAirpcapHandle AdapterHandle, PAirpcapLinkType PLinkType); +typedef gboolean (*AirpcapSetLinkTypeHandler)(PAirpcapHandle AdapterHandle, AirpcapLinkType NewLinkType); +typedef gboolean (*AirpcapSetKernelBufferHandler)(PAirpcapHandle AdapterHandle, guint BufferSize); +typedef gboolean (*AirpcapSetFilterHandler)(PAirpcapHandle AdapterHandle, void * Instructions, guint Len); +typedef gboolean (*AirpcapGetMacAddressHandler)(PAirpcapHandle AdapterHandle, PAirpcapMacAddress PMacAddress); +typedef gboolean (*AirpcapSetMinToCopyHandler)(PAirpcapHandle AdapterHandle, guint MinToCopy); +typedef gboolean (*AirpcapGetReadEventHandler)(PAirpcapHandle AdapterHandle, void *** PReadEvent); +typedef gboolean (*AirpcapReadHandler)(PAirpcapHandle AdapterHandle, guint8 * Buffer, guint BufSize, guint * PReceievedBytes); +typedef gboolean (*AirpcapGetStatsHandler)(PAirpcapHandle AdapterHandle, PAirpcapStats PStats); +typedef gboolean (*AirpcapTurnLedOnHandler)(PAirpcapHandle AdapterHandle, guint LedNumber); +typedef gboolean (*AirpcapTurnLedOffHandler)(PAirpcapHandle AdapterHandle, guint LedNumber); +typedef gboolean (*AirpcapSetDeviceChannelHandler)(PAirpcapHandle AdapterHandle, guint Channel); +typedef gboolean (*AirpcapGetDeviceChannelHandler)(PAirpcapHandle AdapterHandle, guint * PChannel); +typedef gboolean (*AirpcapSetFcsPresenceHandler)(PAirpcapHandle AdapterHandle, gboolean IsFcsPresent); +typedef gboolean (*AirpcapGetFcsPresenceHandler)(PAirpcapHandle AdapterHandle, gboolean * PIsFcsPresent); +typedef gboolean (*AirpcapSetFcsValidationHandler)(PAirpcapHandle AdapterHandle, AirpcapValidationType ValidationType); +typedef gboolean (*AirpcapGetFcsValidationHandler)(PAirpcapHandle AdapterHandle, PAirpcapValidationType PValidationType); +typedef gboolean (*AirpcapSetDeviceKeysHandler)(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection); +typedef gboolean (*AirpcapGetDeviceKeysHandler)(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize); +typedef gboolean (*AirpcapSetDriverKeysHandler)(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection); +typedef gboolean (*AirpcapGetDriverKeysHandler)(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize); +typedef gboolean (*AirpcapSetDecryptionStateHandler)(PAirpcapHandle AdapterHandle, AirpcapDecryptionState Enable); +typedef gboolean (*AirpcapGetDecryptionStateHandler)(PAirpcapHandle AdapterHandle, PAirpcapDecryptionState PEnable); +typedef gboolean (*AirpcapSetDriverDecryptionStateHandler)(PAirpcapHandle AdapterHandle, AirpcapDecryptionState Enable); +typedef gboolean (*AirpcapGetDriverDecryptionStateHandler)(PAirpcapHandle AdapterHandle, PAirpcapDecryptionState PEnable); +typedef gboolean (*AirpcapStoreCurConfigAsAdapterDefaultHandler)(PAirpcapHandle AdapterHandle); +typedef void (*AirpcapGetVersionHandler)(guint * VersionMajor, guint * VersionMinor, guint * VersionRev, guint * VersionBuild); +typedef gboolean (*AirpcapSetDeviceChannelExHandler)(PAirpcapHandle AdapterHandle, AirpcapChannelInfo ChannelInfo); +typedef gboolean (*AirpcapGetDeviceChannelExHandler)(PAirpcapHandle AdapterHandle, PAirpcapChannelInfo PChannelInfo); +typedef gboolean (*AirpcapGetDeviceSupportedChannelsHandler)(PAirpcapHandle AdapterHandle, AirpcapChannelInfo **ppChannelInfo, guint32 * pNumChannelInfo); + +#define FLAG_CAN_BE_LOW 0x00000001 +#define FLAG_CAN_BE_HIGH 0x00000002 +#define FLAG_IS_BG_CHANNEL 0x00000004 +#define FLAG_IS_A_CHANNEL 0x00000008 + +typedef struct _Dot11Channel +{ + guint Channel; + guint32 Frequency; + guint32 Flags; +} Dot11Channel; + +/* + * The list of interfaces returned by "get_airpcap_interface_list()" is + * a list of these structures. + */ +typedef struct { + char *name; /* e.g. "eth0" */ + char *description; /* from OS, e.g. "Local Area Connection" or NULL */ + GSList *ip_addr; /* containing address values of if_addr_t */ + gboolean loopback; /* TRUE if loopback, FALSE otherwise */ + AirpcapLinkType linkType; /* The link layer type */ + AirpcapChannelInfo channelInfo; /* Channel Information */ + gboolean IsFcsPresent; /* Include 802.11 CRC in frames */ + AirpcapValidationType CrcValidationOn; /* Capture Frames with Wrong CRC */ + AirpcapDecryptionState DecryptionOn; /* TRUE if decryption is on, FALSE otherwise */ + PAirpcapKeysCollection keysCollection; /* WEP Key collection for the adapter */ + guint keysCollectionSize; /* Size of the key collection */ + gboolean blinking; /* TRUE if is blinkng, FALSE otherwise */ + gboolean led; /* TRUE if on, FALSE if off */ + gboolean saved; /* TRUE if current configuration has been saved, FALSE otherwise */ + gint tag; /* int for the gtk blinking callback */ + Dot11Channel *pSupportedChannels; + guint32 numSupportedChannels; +} airpcap_if_info_t; + +/* + * Struct used to store infos to pass to the preferences manager callbacks + */ +typedef struct { + GList *list; + int current_index; + int number_of_keys; +} keys_cb_data_t; + +/* Airpcap interface list */ +extern GList *g_airpcap_if_list; + +/* Airpcap current selected interface */ +extern airpcap_if_info_t *airpcap_if_selected; + +/* Airpcap current active interface */ +extern airpcap_if_info_t *airpcap_if_active; + +#ifdef AIRPCAP_DEBUG +/* + * USED FOR DEBUG ONLY... PRINTS AN AirPcap ADAPTER STRUCTURE in a fancy way. + */ +void +airpcap_if_info_print(airpcap_if_info_t* if_info); +#endif + +/* + * Used to retrieve the two chars string from interface + */ +gchar* +airpcap_get_if_string_number_from_description(gchar* description); + +/* + * Function used to free the airpcap interface list + */ +void +free_airpcap_interface_list(GList *if_list); + +/* + * Used to retrieve the interface given the name + * (the name is used in AirpcapOpen). + */ +airpcap_if_info_t* get_airpcap_if_from_name(GList* if_list, const gchar* name); + +/* + * Airpcap wrapper, used to store the current settings for the selected adapter + */ +gboolean +airpcap_if_store_cur_config_as_adapter_default(PAirpcapHandle ah); + +/* + * Function used to load the WEP keys for a selected interface + */ +gboolean +airpcap_if_load_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info); + +/* + * Function used to load the WEP keys from the global driver list + */ +gboolean +airpcap_if_load_driver_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info); + +/* + * Function used to save the WEP keys for a selected interface + */ +void +airpcap_if_save_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info); + +/* + * Function used to save the WEP keys for a selected interface + */ +void +airpcap_if_save_driver_keys(PAirpcapHandle ad, airpcap_if_info_t *if_info); + +/* + * Airpcap wrapper, used to get the fcs validation of an airpcap adapter + */ +gboolean +airpcap_if_get_fcs_validation(PAirpcapHandle ah, PAirpcapValidationType val); + +/* + * Airpcap wrapper, used to set the fcs validation of an airpcap adapter + */ +gboolean +airpcap_if_set_fcs_validation(PAirpcapHandle ah, AirpcapValidationType val); + +/* Many of these are GTK+ only. */ +/* + * Airpcap wrapper, used to get the decryption enabling of an airpcap adapter + */ +gboolean +airpcap_if_get_decryption_state(PAirpcapHandle ah, PAirpcapDecryptionState val); + +/* + * Airpcap wrapper, used to set the decryption enabling of an airpcap adapter + */ +gboolean +airpcap_if_set_decryption_state(PAirpcapHandle ah, AirpcapDecryptionState val); + +/* + * Airpcap wrapper, used to get the fcs presence of an airpcap adapter + */ +gboolean +airpcap_if_get_fcs_presence(PAirpcapHandle ah, gboolean * ch); + +/* + * Airpcap wrapper, used to set the fcs presence of an airpcap adapter + */ +gboolean +airpcap_if_set_fcs_presence(PAirpcapHandle ah, gboolean ch); + +/* + * Airpcap wrapper, used to get the link type of an airpcap adapter + */ +gboolean +airpcap_if_get_link_type(PAirpcapHandle ah, PAirpcapLinkType lt); + +/* + * Airpcap wrapper, used to set the link type of an airpcap adapter + */ +gboolean +airpcap_if_set_link_type(PAirpcapHandle ah, AirpcapLinkType lt); + +/* + * Airpcap wrapper, used to get the channel of an airpcap adapter + */ +gboolean +airpcap_if_get_device_channel(PAirpcapHandle ah, guint * ch); + +/* + * Airpcap wrapper, get the channels supported by the adapter + */ +gboolean +airpcap_if_get_device_supported_channels(PAirpcapHandle ah, AirpcapChannelInfo **cInfo, guint32 * nInfo); + +/* + * Airpcap wrapper, get supported channels formatted into an array + */ +Dot11Channel* +airpcap_if_get_device_supported_channels_array(PAirpcapHandle ah, guint32 * pNumSupportedChannels); + +/* + * Airpcap wrapper, used to set the channel of an airpcap adapter + */ +gboolean +airpcap_if_set_device_channel(PAirpcapHandle ah, guint ch); + +/* + * Airpcap wrapper, used to get the frequency of an airpcap adapter + */ +gboolean +airpcap_if_get_device_channel_ex(PAirpcapHandle ah, PAirpcapChannelInfo pChannelInfo); + +/* + * Airpcap wrapper, used to set the frequency of an airpcap adapter + */ +gboolean +airpcap_if_set_device_channel_ex(PAirpcapHandle ah, AirpcapChannelInfo ChannelInfo); + +/* + * Airpcap wrapper, used to open an airpcap adapter + */ +PAirpcapHandle airpcap_if_open(gchar * name, gchar * err); + +/* + * Airpcap wrapper, used to close an airpcap adapter + */ +void airpcap_if_close(PAirpcapHandle handle); + +/* + * Retrieve the state of the Airpcap DLL + */ +int +airpcap_get_dll_state(void); + +/* + * Airpcap wrapper, used to turn on the led of an airpcap adapter + */ +gboolean airpcap_if_turn_led_on(PAirpcapHandle AdapterHandle, guint LedNumber); + +/* + * Airpcap wrapper, used to turn off the led of an airpcap adapter + */ +gboolean airpcap_if_turn_led_off(PAirpcapHandle AdapterHandle, guint LedNumber); + +/* + * This function will create a new airpcap_if_info_t using a name and a description + */ +airpcap_if_info_t* airpcap_if_info_new(char *name, char *description); + +/* + * This function will create a new fake drivers' interface, to load global keys... + */ +airpcap_if_info_t* airpcap_driver_fake_if_info_new(void); + +/* + * Used to dinamically load the airpcap library in order link it only when + * it's present on the system. + */ +int load_airpcap(void); + +/* + * This function will use the airpcap.dll to find all the airpcap devices. + * Will return null if no device is found. + */ +GList* get_airpcap_interface_list(int *err, char **err_str); + +/* + * Load the configuration for the specified interface + */ +void +airpcap_load_selected_if_configuration(airpcap_if_info_t* if_info); + +/* + * Save the configuration for the specified interface + */ +void +airpcap_save_selected_if_configuration(airpcap_if_info_t* if_info); + +/* + * Used to retrieve the two chars string from interface description + */ +gchar* +airpcap_get_if_string_number(airpcap_if_info_t* if_info); + +/* + * Airpcap wrapper, used to save the settings for the selected_if + */ +gboolean +airpcap_if_set_device_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection); + +/* + * Airpcap wrapper, used to save the settings for the selected_if + */ +gboolean +airpcap_if_get_device_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize); + +/* + * Airpcap wrapper, used to save the settings for the selected_if + */ +gboolean +airpcap_if_set_driver_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection); + +/* + * Airpcap wrapper, used to save the settings for the selected_if + */ +gboolean +airpcap_if_get_driver_keys(PAirpcapHandle AdapterHandle, PAirpcapKeysCollection KeysCollection, guint * PKeysCollectionSize); + +/* + * Airpcap wrapper, used to get the decryption enabling of an airpcap driver + */ +gboolean +airpcap_if_get_driver_decryption_state(PAirpcapHandle ah, PAirpcapDecryptionState PEnable); +/* + * Airpcap wrapper, used to set the decryption enabling of an airpcap driver + */ +gboolean +airpcap_if_set_driver_decryption_state(PAirpcapHandle ah, AirpcapDecryptionState Enable); + +/* + * Save the configuration for the specified interface + */ +void +airpcap_save_driver_if_configuration(airpcap_if_info_t* fake_if_info); + +/* + * Free an instance of airpcap_if_info_t + */ +void +airpcap_if_info_free(airpcap_if_info_t *if_info); + +/* + * Clear keys and decryption status for the specified interface + */ +void +airpcap_if_clear_decryption_settings(airpcap_if_info_t* info_if); + +/* + * Adds compiled version string to str + */ +void +get_compiled_airpcap_version(GString *str); + +void +get_runtime_airpcap_version(GString *str); + +#ifdef __cplusplus +} +#endif + +#endif /* __AIRPCAP_LOADER_H__ */ diff --git a/capture/capture-pcap-util-int.h b/capture/capture-pcap-util-int.h new file mode 100644 index 0000000000..efeb467409 --- /dev/null +++ b/capture/capture-pcap-util-int.h @@ -0,0 +1,61 @@ +/* capture-pcap-util-int.h + * Definitions of routines internal to the libpcap/WinPcap/Npcap utilities + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __PCAP_UTIL_INT_H__ +#define __PCAP_UTIL_INT_H__ + +extern if_info_t *if_info_new(const char *name, const char *description, + gboolean loopback); +extern void if_info_add_address(if_info_t *if_info, struct sockaddr *addr); +#ifdef HAVE_PCAP_REMOTE +extern GList *get_interface_list_findalldevs_ex(const char *hostname, + const char *port, int auth_type, const char *username, const char *passwd, + int *err, char **err_str); +#endif /* HAVE_PCAP_REMOTE */ +extern GList *get_interface_list_findalldevs(int *err, char **err_str); + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION +/* + * Request that a pcap_t provide high-resolution (nanosecond) time + * stamps; if that request fails, we'll just silently continue to + * use the microsecond-resolution time stamps, and our caller will + * find out, when they call have_high_resolution_timestamp(), that + * we don't have high-resolution time stamps. + */ +extern void request_high_resolution_timestamp(pcap_t *pcap_h); +#endif + +extern if_capabilities_t *get_if_capabilities_local(interface_options *interface_opts, + cap_device_open_err *err, char **err_str); +extern pcap_t *open_capture_device_local(capture_options *capture_opts, + interface_options *interface_opts, int timeout, + cap_device_open_err *open_err, char (*open_err_str)[PCAP_ERRBUF_SIZE]); +#ifdef HAVE_PCAP_CREATE +extern if_capabilities_t *get_if_capabilities_pcap_create(interface_options *interface_opts, + cap_device_open_err *err, char **err_str); +extern pcap_t *open_capture_device_pcap_create(capture_options *capture_opts, + interface_options *interface_opts, int timeout, + cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]); +#endif /* HAVE_PCAP_CREATE */ +extern if_capabilities_t *get_if_capabilities_pcap_open_live(interface_options *interface_opts, + cap_device_open_err *err, char **err_str); +extern pcap_t *open_capture_device_pcap_open_live(interface_options *interface_opts, + int timeout, cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]); + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_interface_list()". This is used to let the error message string + * be platform-dependent. + */ +extern gchar *cant_get_if_list_error_message(const char *err_str); + +#endif /* __PCAP_UTIL_INT_H__ */ diff --git a/capture/capture-pcap-util-unix.c b/capture/capture-pcap-util-unix.c new file mode 100644 index 0000000000..b83888e849 --- /dev/null +++ b/capture/capture-pcap-util-unix.c @@ -0,0 +1,202 @@ +/* capture-pcap-util-unix.c + * UN*X-specific utility routines for packet capture + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#ifdef HAVE_LIBPCAP + +#include "wspcap.h" + +#ifdef HAVE_LIBCAP +# include +#endif + +#include "capture/capture_ifinfo.h" +#include "capture/capture-pcap-util.h" +#include "capture/capture-pcap-util-int.h" + +#ifdef HAVE_PCAP_REMOTE +GList * +get_remote_interface_list(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str) +{ + return get_interface_list_findalldevs_ex(hostname, port, auth_type, + username, passwd, err, err_str); +} +#endif + +GList * +get_interface_list(int *err, char **err_str) +{ + return get_interface_list_findalldevs(err, err_str); +} + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_interface_list()". + */ +gchar * +cant_get_if_list_error_message(const char *err_str) +{ + return g_strdup_printf("Can't get list of interfaces: %s", err_str); +} + +if_capabilities_t * +get_if_capabilities_local(interface_options *interface_opts, + cap_device_open_err *err, char **err_str) +{ +#ifdef HAVE_PCAP_CREATE + return get_if_capabilities_pcap_create(interface_opts, err, err_str); +#else + return get_if_capabilities_pcap_open_live(interface_opts, err, err_str); +#endif +} + +pcap_t * +open_capture_device_local(capture_options *capture_opts +#ifndef HAVE_PCAP_CREATE + _U_ +#endif + , + interface_options *interface_opts, int timeout, + cap_device_open_err *open_err, char (*open_err_str)[PCAP_ERRBUF_SIZE]) +{ + /* + * We're not opening a remote device; use pcap_create() and + * pcap_activate() if we have them, so that we can set various + * options, otherwise use pcap_open_live(). + */ +#ifdef HAVE_PCAP_CREATE + return open_capture_device_pcap_create(capture_opts, + interface_opts, timeout, open_err, open_err_str); +#else + return open_capture_device_pcap_open_live(interface_opts, timeout, + open_err, open_err_str); +#endif +} + +/* + * Get the versions of libpcap, libpcap, and libnl with which we were + * compiled, and append them to a GString. + */ +void +get_compiled_caplibs_version(GString *str) +{ + /* + * NOTE: in *some* flavors of UN*X, the data from a shared + * library might be linked into executable images that are + * linked with that shared library, in which case you could + * look at pcap_version[] to get the version with which + * the program was compiled. + * + * In other flavors of UN*X, that doesn't happen, so + * pcap_version[] gives you the version the program is + * running with, not the version it was built with, and, + * in at least some of them, if the length of a data item + * referred to by the executable - such as the pcap_version[] + * string - isn't the same in the version of the library + * with which the program was built and the version with + * which it was run, the run-time linker will complain, + * which is Not Good. + * + * So, for now, we just give up on reporting the version + * of libpcap with which we were compiled. + */ + g_string_append(str, "with libpcap"); +#ifdef HAVE_PCAP_REMOTE + /* + * We have remote pcap support in libpcap. + */ + g_string_append(str, " (including remote capture support)"); +#endif + + /* + * XXX - these libraries are actually used only by dumpcap, + * but we mention them here so that a user reporting a bug + * can get information about dumpcap's libraries without + * having to run dumpcap. + */ + /* LIBCAP */ + g_string_append(str, ", "); +#ifdef HAVE_LIBCAP + g_string_append(str, "with POSIX capabilities"); +#ifdef _LINUX_CAPABILITY_VERSION + g_string_append(str, " (Linux)"); +#endif /* _LINUX_CAPABILITY_VERSION */ +#else /* HAVE_LIBCAP */ + g_string_append(str, "without POSIX capabilities"); +#endif /* HAVE_LIBCAP */ + +#ifdef __linux__ + /* This is a Linux-specific library. */ + /* LIBNL */ + g_string_append(str, ", "); +#if defined(HAVE_LIBNL1) + g_string_append(str, "with libnl 1"); +#elif defined(HAVE_LIBNL2) + g_string_append(str, "with libnl 2"); +#elif defined(HAVE_LIBNL3) + g_string_append(str, "with libnl 3"); +#else /* no libnl */ + g_string_append(str, "without libnl"); +#endif /* libnl version */ +#endif /* __linux__ */ +} + +/* + * Append the version of libpcap with which we we're running to a GString. + */ +void +get_runtime_caplibs_version(GString *str) +{ + g_string_append_printf(str, "with "); + g_string_append(str, pcap_lib_version()); +} + +#else /* HAVE_LIBPCAP */ + +/* + * Append an indication that we were not compiled with libpcap + * to a GString. Don't even bother mentioning the other + * libraries. + */ +void +get_compiled_caplibs_version(GString *str) +{ + g_string_append(str, "without libpcap"); +} + +/* + * Don't append anything, as we weren't even compiled to use libpcap. + */ +void +get_runtime_caplibs_version(GString *str _U_) +{ +} + +#endif /* HAVE_LIBPCAP */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/capture/capture-pcap-util.c b/capture/capture-pcap-util.c new file mode 100644 index 0000000000..5f8377a688 --- /dev/null +++ b/capture/capture-pcap-util.c @@ -0,0 +1,1626 @@ +/* capture-pcap-util.c + * Utility routines for packet capture + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#ifdef HAVE_LIBPCAP + +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#include "ws_attributes.h" + +/* + * Linux bonding devices mishandle unknown ioctls; they fail + * with ENODEV rather than ENOTSUP, EOPNOTSUPP, or ENOTTY, + * so pcap_can_set_rfmon() returns a "no such device" indication + * if we try to do SIOCGIWMODE on them. + * + * So, on Linux, we check for bonding devices, if we can, before + * trying pcap_can_set_rfmon(), as pcap_can_set_rfmon() will + * end up trying SIOCGIWMODE on the device if that ioctl exists. + */ +#if defined(HAVE_PCAP_CREATE) && defined(__linux__) + +#include + +/* + * If we're building for a Linux version that supports bonding, + * HAVE_BONDING will be defined. + */ + +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif + +#ifdef HAVE_LINUX_IF_BONDING_H +#include +#endif + +#if defined(BOND_INFO_QUERY_OLD) || defined(SIOCBONDINFOQUERY) +#define HAVE_BONDING +#endif + +#endif /* defined(HAVE_PCAP_CREATE) && defined(__linux__) */ + +#include "capture/capture_ifinfo.h" +#include "capture/capture-pcap-util.h" +#include "capture/capture-pcap-util-int.h" + +#include "log.h" + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#ifdef _WIN32 +#include "capture/capture_win_ifnames.h" /* windows friendly interface names */ +#endif + +#if defined(__FreeBSD__) || defined(__OpenBSD__) +/* + * Needed for the code to get a device description. + */ +#include +#include +#include +#endif + +/* + * Given an interface name, find the "friendly name" and interface + * type for the interface. + */ + +#if defined(HAVE_MACOS_FRAMEWORKS) + +#include +#include + +#include + +/* + * On macOS, we get the "friendly name" and interface type for the interface + * from the System Configuration framework. + * + * To find the System Configuration framework information for the + * interface, we get all the interfaces that the System Configuration + * framework knows about and look for the one with a "BSD name" matching + * the interface name. + * + * If we find it, we use its "localized display name", if it has one, as + * the "friendly name". + * + * As for the interface type: + * + * Yes, fetching all the network addresses for an interface gets you an + * AF_LINK address, of type "struct sockaddr_dl", and, yes, that includes + * an SNMP MIB-II ifType value. + * + * However, it's IFT_ETHER, i.e. Ethernet, for AirPort interfaces, + * not IFT_IEEE80211 (which isn't defined in macOS in any case). + * + * Perhaps some other BSD-flavored OSes won't make this mistake; + * however, FreeBSD 7.0 and OpenBSD 4.2, at least, appear to have + * made the same mistake, at least for my Belkin ZyDAS stick. + * + * SCNetworkInterfaceGetInterfaceType() will get the interface + * type. The interface type is a CFString, and: + * + * kSCNetworkInterfaceTypeIEEE80211 means IF_WIRELESS; + * kSCNetworkInterfaceTypeBluetooth means IF_BLUETOOTH; + * kSCNetworkInterfaceTypeModem or + * kSCNetworkInterfaceTypePPP or + * maybe kSCNetworkInterfaceTypeWWAN means IF_DIALUP + */ +static void +add_unix_interface_ifinfo(if_info_t *if_info, const char *name, + const char *description _U_) +{ + CFStringRef name_CFString; + CFArrayRef interfaces; + CFIndex num_interfaces; + CFIndex i; + SCNetworkInterfaceRef interface; + CFStringRef bsdname_CFString; + CFStringRef friendly_name_CFString; + CFStringRef interface_type_CFString; + + interfaces = SCNetworkInterfaceCopyAll(); + if (interfaces == NULL) { + /* + * Couldn't get a list of interfaces. + */ + return; + } + + name_CFString = CFStringCreateWithCString(kCFAllocatorDefault, + name, kCFStringEncodingUTF8); + if (name_CFString == NULL) { + /* + * Couldn't convert the interface name to a CFString. + */ + CFRelease(interfaces); + return; + } + + num_interfaces = CFArrayGetCount(interfaces); + for (i = 0; i < num_interfaces; i++) { + interface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(interfaces, i); + bsdname_CFString = SCNetworkInterfaceGetBSDName(interface); + if (bsdname_CFString == NULL) { + /* + * This interface has no BSD name, so it's not + * a regular network interface. + */ + continue; + } + if (CFStringCompare(name_CFString, bsdname_CFString, 0) == 0) { + /* + * This is the interface. + * First, get the friendly name. + */ + friendly_name_CFString = SCNetworkInterfaceGetLocalizedDisplayName(interface); + if (friendly_name_CFString != NULL) + if_info->friendly_name = CFString_to_C_string(friendly_name_CFString); + + /* + * Now get the interface type. + */ + interface_type_CFString = SCNetworkInterfaceGetInterfaceType(interface); + if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeIEEE80211, 0) == kCFCompareEqualTo) + if_info->type = IF_WIRELESS; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeBluetooth, 0) == kCFCompareEqualTo) + if_info->type = IF_BLUETOOTH; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeModem, 0) == kCFCompareEqualTo) + if_info->type = IF_DIALUP; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypePPP, 0) == kCFCompareEqualTo) + if_info->type = IF_DIALUP; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeWWAN, 0) == kCFCompareEqualTo) + if_info->type = IF_DIALUP; + else + if_info->type = IF_WIRED; + break; + } + } + + CFRelease(interfaces); + CFRelease(name_CFString); +} +#elif defined(__linux__) +/* + * Linux doesn't offer any form of "friendly name", but you can + * determine an interface type to some degree. + */ +static void +add_unix_interface_ifinfo(if_info_t *if_info, const char *name, + const char *description _U_) +{ + char *wireless_path; + ws_statb64 statb; + + /* + * Look for /sys/class/net/{device}/wireless. If it exists, + * it's a wireless interface. + */ + wireless_path = g_strdup_printf("/sys/class/net/%s/wireless", name); + if (wireless_path != NULL) { + if (ws_stat64(wireless_path, &statb) == 0) + if_info->type = IF_WIRELESS; + g_free(wireless_path); + } + if (if_info->type == IF_WIRED) { + /* + * We still don't know what it is. Check for + * Bluetooth and USB devices. + */ + if (strstr(name, "bluetooth") != NULL) { + /* + * XXX - this is for raw Bluetooth capture; what + * about IP-over-Bluetooth devices? + */ + if_info->type = IF_BLUETOOTH; + } else if (strstr(name, "usbmon") != NULL) + if_info->type = IF_USB; + } +} +#else +/* + * On other UN*Xes, if there is a description, it's a friendly + * name, and there is no vendor description. ("Other UN*Xes" + * currently means "FreeBSD and OpenBSD".) + */ +static void +add_unix_interface_ifinfo(if_info_t *if_info, const char *name _U_, + const char *description) +{ + if_info->friendly_name = g_strdup(description); +} +#endif + +if_info_t * +if_info_get(const char *name) +{ + char *description = NULL; + if_info_t *if_info; +#ifdef SIOCGIFDESCR + /* + * Try to fetch the description of this interface. + * XXX - this is only here because libpcap has no API to + * get the description of a *single* interface; it really + * needs both an API to get pcapng-IDB-style attributes + * for a single interface and to get a list of interfaces + * with pcapng-IDB-style attributes for each interface. + */ + int s; + struct ifreq ifrdesc; +#ifndef IFDESCRSIZE + size_t descrlen = 64; +#else + size_t descrlen = IFDESCRSIZE; +#endif /* IFDESCRSIZE */ + + /* + * Get the description for the interface. + */ + memset(&ifrdesc, 0, sizeof ifrdesc); + g_strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name); + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s >= 0) { +#ifdef __FreeBSD__ + /* + * On FreeBSD, if the buffer isn't big enough for the + * description, the ioctl succeeds, but the description + * isn't copied, ifr_buffer.length is set to the description + * length, and ifr_buffer.buffer is set to NULL. + */ + for (;;) { + g_free(description); + if ((description = (char*)g_malloc(descrlen)) != NULL) { + ifrdesc.ifr_buffer.buffer = description; + ifrdesc.ifr_buffer.length = descrlen; + if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0) { + if (ifrdesc.ifr_buffer.buffer == + description) + break; + else + descrlen = ifrdesc.ifr_buffer.length; + } else { + /* + * Failed to get interface description. + */ + g_free(description); + description = NULL; + break; + } + } else + break; + } +#else /* __FreeBSD__ */ + /* + * The only other OS that currently supports + * SIOCGIFDESCR is OpenBSD, and it has no way + * to get the description length - it's clamped + * to a maximum of IFDESCRSIZE. + */ + if ((description = (char*)g_malloc(descrlen)) != NULL) { + ifrdesc.ifr_data = (caddr_t)description; + if (ioctl(s, SIOCGIFDESCR, &ifrdesc) != 0) { + /* + * Failed to get interface description. + */ + g_free(description); + description = NULL; + } + } +#endif /* __FreeBSD__ */ + close(s); + if (description != NULL && strlen(description) == 0) { + /* + * Description is empty, so discard it. + */ + g_free(description); + description = NULL; + } + } + +#ifdef __FreeBSD__ + /* + * For FreeBSD, if we didn't get a description, and this is + * a device with a name of the form usbusN, label it as a USB + * bus. + */ + if (description == NULL) { + if (strncmp(name, "usbus", 5) == 0) { + /* + * OK, it begins with "usbus". + */ + long busnum; + char *p; + + errno = 0; + busnum = strtol(name + 5, &p, 10); + if (errno == 0 && p != name + 5 && *p == '\0' && + busnum >= 0 && busnum <= INT_MAX) { + /* + * OK, it's a valid number that's not + * bigger than INT_MAX. Construct + * a description from it. + */ + static const char descr_prefix[] = "USB bus number "; + size_t descr_size; + + /* + * Allow enough room for a 32-bit bus number. + * sizeof (descr_prefix) includes the + * terminating NUL. + */ + descr_size = sizeof (descr_prefix) + 10; + description = g_malloc(descr_size); + if (description != NULL) { + g_snprintf(description, descr_size, + "%s%ld", descr_prefix, busnum); + } + } + } + } +#endif /* __FreeBSD__ */ +#endif /* SIOCGIFDESCR */ + if_info = if_info_new(name, description, FALSE); + g_free(description); + return if_info; +} + +void +if_info_free(if_info_t *if_info) +{ + g_free(if_info->name); + g_free(if_info->friendly_name); + g_free(if_info->vendor_description); + g_free(if_info->extcap); + g_slist_free_full(if_info->addrs, g_free); + g_free(if_info); +} + +if_info_t * +if_info_new(const char *name, const char *description, gboolean loopback) +{ + if_info_t *if_info; +#ifdef _WIN32 + const char *guid_text; + GUID guid; +#endif + + if_info = g_new(if_info_t, 1); + if_info->name = g_strdup(name); + if_info->friendly_name = NULL; /* default - unknown */ + if_info->vendor_description = NULL; + if_info->type = IF_WIRED; /* default */ + if_info->extcap = g_strdup(""); +#ifdef _WIN32 + /* + * Get the interface type. + * + * Much digging failed to reveal any obvious way to get something + * such as the SNMP MIB-II ifType value for an interface: + * + * https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib + * + * by making some NDIS request. And even if there were such + * a way, there's no guarantee that the ifType reflects an + * interface type that a user would view as correct (for + * example, some systems report Wi-Fi interfaces as + * Ethernet interfaces). + * + * So we look for keywords in the vendor's interface + * description. + */ + if (description && (strstr(description, "generic dialup") != NULL || + strstr(description, "PPP/SLIP") != NULL)) { + if_info->type = IF_DIALUP; + } else if (description && (strstr(description, "Wireless") != NULL || + strstr(description,"802.11") != NULL)) { + if_info->type = IF_WIRELESS; + } else if (description && strstr(description, "AirPcap") != NULL || + strstr(name, "airpcap") != NULL) { + if_info->type = IF_AIRPCAP; + } else if (description && strstr(description, "Bluetooth") != NULL ) { + if_info->type = IF_BLUETOOTH; + } else if (description && strstr(description, "VMware") != NULL) { + /* + * Bridge, NAT, or host-only interface on a VMware host. + * + * XXX - what about guest interfaces? + */ + if_info->type = IF_VIRTUAL; + } + + /* + * On Windows, the "description" is a vendor description, + * and the friendly name isn't returned by Npcap/WinPcap. + * Fetch it ourselves. + */ + + /* + * Skip over the "\Device\NPF_" prefix in the device name, + * if present. + */ + if (strncmp("\\Device\\NPF_", name, 12) == 0) + guid_text = name + 12; + else + guid_text = name; + + /* Now try to parse what remains as a GUID. */ + if (parse_as_guid(guid_text, &guid)) { + /* + * Success. Try to get a friendly name using the GUID. + * As this is a regular interface, the description is a + * vendor description. + */ + if_info->friendly_name = get_interface_friendly_name_from_device_guid(&guid); + if_info->vendor_description = g_strdup(description); + } else { + /* + * This is probably not a regular interface; we only + * support NT 5 (W2K) and later, so all regular interfaces + * should have GUIDs at the end of the name. Therefore, + * the description, if supplied, is a friendly name + * provided by WinPcap, and there is no vendor + * description. + */ + if_info->friendly_name = g_strdup(description); + if_info->vendor_description = NULL; + } +#else + /* + * On UN*X, if there is a description, it's a friendly + * name, and there is no vendor description. + * + * Try the platform's way of getting a friendly name and + * interface type first. + * + * If that fails, then, for a loopback interface, give it the + * friendly name "Loopback" and, for VMware interfaces, + * give them the type IF_VIRTUAL. + */ + add_unix_interface_ifinfo(if_info, name, description); + if (if_info->type == IF_WIRED) { + /* + * This is the default interface type. + * + * Bridge, NAT, or host-only interfaces on VMWare hosts + * have the name vmnet[0-9]+. Guests might use a native + * (LANCE or E1000) driver or the vmxnet driver. Check + * the name. + */ + if (g_ascii_strncasecmp(name, "vmnet", 5) == 0) + if_info->type = IF_VIRTUAL; + else if (g_ascii_strncasecmp(name, "vmxnet", 6) == 0) + if_info->type = IF_VIRTUAL; + } + if (if_info->friendly_name == NULL) { + /* + * We couldn't get interface information using platform- + * dependent calls. + * + * If this is a loopback interface, give it a + * "friendly name" of "Loopback". + */ + if (loopback) + if_info->friendly_name = g_strdup("Loopback"); + } + if_info->vendor_description = NULL; +#endif + if_info->loopback = loopback; + if_info->addrs = NULL; + return if_info; +} + +void +if_info_add_address(if_info_t *if_info, struct sockaddr *addr) +{ + if_addr_t *if_addr; + struct sockaddr_in *ai; + struct sockaddr_in6 *ai6; + + switch (addr->sa_family) { + + case AF_INET: + ai = (struct sockaddr_in *)(void *)addr; + if_addr = (if_addr_t *)g_malloc(sizeof(*if_addr)); + if_addr->ifat_type = IF_AT_IPv4; + if_addr->addr.ip4_addr = + *((guint32 *)&(ai->sin_addr.s_addr)); + if_info->addrs = g_slist_prepend(if_info->addrs, if_addr); + break; + + case AF_INET6: + ai6 = (struct sockaddr_in6 *)(void *)addr; + if_addr = (if_addr_t *)g_malloc(sizeof(*if_addr)); + if_addr->ifat_type = IF_AT_IPv6; + memcpy((void *)&if_addr->addr.ip6_addr, + (void *)&ai6->sin6_addr.s6_addr, + sizeof if_addr->addr.ip6_addr); + if_info->addrs = g_slist_prepend(if_info->addrs, if_addr); + break; + } +} + +/* + * Get all IP address information for the given interface. + */ +static void +if_info_ip(if_info_t *if_info, pcap_if_t *d) +{ + pcap_addr_t *a; + + /* All addresses */ + for (a = d->addresses; a != NULL; a = a->next) { + if (a->addr != NULL) + if_info_add_address(if_info, a->addr); + } + + if(if_info->addrs){ + if_info->addrs = g_slist_reverse(if_info->addrs); + } +} + +#ifdef HAVE_PCAP_REMOTE +GList * +get_interface_list_findalldevs_ex(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str) +{ + char source[PCAP_BUF_SIZE]; + struct pcap_rmtauth auth; + GList *il = NULL; + pcap_if_t *alldevs, *dev; + if_info_t *if_info; + /* + * WinPcap can overflow PCAP_ERRBUF_SIZE if the host is unreachable. + * Fudge a larger size. + */ + char errbuf[PCAP_ERRBUF_SIZE*4]; + + if (pcap_createsrcstr(source, PCAP_SRC_IFREMOTE, hostname, port, + NULL, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + return NULL; + } + + auth.type = auth_type; + auth.username = g_strdup(username); + auth.password = g_strdup(passwd); + + if (pcap_findalldevs_ex(source, &auth, &alldevs, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + g_free(auth.username); + g_free(auth.password); + return NULL; + } + + if (alldevs == NULL) { + /* + * No interfaces found. + */ + *err = 0; + if (err_str != NULL) + *err_str = NULL; + g_free(auth.username); + g_free(auth.password); + return NULL; + } + + for (dev = alldevs; dev != NULL; dev = dev->next) { + if_info = if_info_new(dev->name, dev->description, + (dev->flags & PCAP_IF_LOOPBACK) ? TRUE : FALSE); + il = g_list_append(il, if_info); + if_info_ip(if_info, dev); + } + pcap_freealldevs(alldevs); + g_free(auth.username); + g_free(auth.password); + + return il; +} +#endif + +GList * +get_interface_list_findalldevs(int *err, char **err_str) +{ + GList *il = NULL; + pcap_if_t *alldevs, *dev; + if_info_t *if_info; + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&alldevs, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + return NULL; + } + + if (alldevs == NULL) { + /* + * No interfaces found. + */ + *err = 0; + if (err_str != NULL) + *err_str = NULL; + return NULL; + } + + for (dev = alldevs; dev != NULL; dev = dev->next) { + if_info = if_info_new(dev->name, dev->description, + (dev->flags & PCAP_IF_LOOPBACK) ? TRUE : FALSE); + il = g_list_append(il, if_info); + if_info_ip(if_info, dev); + } + pcap_freealldevs(alldevs); + + return il; +} + +static void +free_if_cb(gpointer data, gpointer user_data _U_) +{ + if_info_free((if_info_t *)data); +} + +void +free_interface_list(GList *if_list) +{ + g_list_foreach(if_list, free_if_cb, NULL); + g_list_free(if_list); +} + +static void +free_linktype_cb(gpointer data, gpointer user_data _U_) +{ + data_link_info_t *linktype_info = (data_link_info_t *)data; + + g_free(linktype_info->name); + g_free(linktype_info->description); + g_free(linktype_info); +} + +static void +free_timestamp_cb(gpointer data, gpointer user_data _U_) +{ + timestamp_info_t *timestamp_info = (timestamp_info_t *)data; + + g_free(timestamp_info->name); + g_free(timestamp_info->description); + g_free(data); +} + +void +free_if_capabilities(if_capabilities_t *caps) +{ + g_list_foreach(caps->data_link_types, free_linktype_cb, NULL); + g_list_free(caps->data_link_types); + + g_list_foreach(caps->timestamp_types, free_timestamp_cb, NULL); + g_list_free(caps->timestamp_types); + + g_free(caps); +} + +const char * +linktype_val_to_name(int dlt) +{ + return pcap_datalink_val_to_name(dlt); +} + +int +linktype_name_to_val(const char *linktype) +{ + return pcap_datalink_name_to_val(linktype); +} + +/* + * Get the data-link type for a libpcap device. + * This works around AIX 5.x's non-standard and incompatible-with-the- + * rest-of-the-universe libpcap. + */ +int +get_pcap_datalink(pcap_t *pch, +#ifdef _AIX + const char* devicename +#else + const char* devicename _U_ +#endif + ) +{ + int datalink; +#ifdef _AIX + const char *ifacename; +#endif + + datalink = pcap_datalink(pch); +#ifdef _AIX + + /* + * The libpcap that comes with AIX 5.x uses RFC 1573 ifType values + * rather than DLT_ values for link-layer types; the ifType values + * for LAN devices are: + * + * Ethernet 6 + * 802.3 7 + * Token Ring 9 + * FDDI 15 + * + * and the ifType value for a loopback device is 24. + * + * The AIX names for LAN devices begin with: + * + * Ethernet en + * 802.3 et + * Token Ring tr + * FDDI fi + * + * and the AIX names for loopback devices begin with "lo". + * + * (The difference between "Ethernet" and "802.3" is presumably + * whether packets have an Ethernet header, with a packet type, + * or an 802.3 header, with a packet length, followed by an 802.2 + * header and possibly a SNAP header.) + * + * If the device name matches "datalink" interpreted as an ifType + * value, rather than as a DLT_ value, we will assume this is AIX's + * non-standard, incompatible libpcap, rather than a standard libpcap, + * and will map the link-layer type to the standard DLT_ value for + * that link-layer type, as that's what the rest of Wireshark expects. + * + * (This means the capture files won't be readable by a tcpdump + * linked with AIX's non-standard libpcap, but so it goes. They + * *will* be readable by standard versions of tcpdump, Wireshark, + * and so on.) + * + * XXX - if we conclude we're using AIX libpcap, should we also + * set a flag to cause us to assume the time stamps are in + * seconds-and-nanoseconds form, and to convert them to + * seconds-and-microseconds form before processing them and + * writing them out? + */ + + /* + * Find the last component of the device name, which is the + * interface name. + */ + ifacename = strchr(devicename, '/'); + if (ifacename == NULL) + ifacename = devicename; + + /* See if it matches any of the LAN device names. */ + if (strncmp(ifacename, "en", 2) == 0) { + if (datalink == 6) { + /* + * That's the RFC 1573 value for Ethernet; + * map it to DLT_EN10MB. + */ + datalink = 1; + } + } else if (strncmp(ifacename, "et", 2) == 0) { + if (datalink == 7) { + /* + * That's the RFC 1573 value for 802.3; + * map it to DLT_EN10MB. + * + * (libpcap, tcpdump, Wireshark, etc. don't + * care if it's Ethernet or 802.3.) + */ + datalink = 1; + } + } else if (strncmp(ifacename, "tr", 2) == 0) { + if (datalink == 9) { + /* + * That's the RFC 1573 value for 802.5 (Token Ring); + * map it to DLT_IEEE802, which is what's used for + * Token Ring. + */ + datalink = 6; + } + } else if (strncmp(ifacename, "fi", 2) == 0) { + if (datalink == 15) { + /* + * That's the RFC 1573 value for FDDI; + * map it to DLT_FDDI. + */ + datalink = 10; + } + } else if (strncmp(ifacename, "lo", 2) == 0) { + if (datalink == 24) { + /* + * That's the RFC 1573 value for "software loopback" + * devices; map it to DLT_NULL, which is what's used + * for loopback devices on BSD. + */ + datalink = 0; + } + } +#endif + + return datalink; +} + +/* Set the data link type on a pcap. */ +gboolean +set_pcap_datalink(pcap_t *pcap_h, int datalink, char *name, + char *errmsg, size_t errmsg_len, + char *secondary_errmsg, size_t secondary_errmsg_len) +{ + char *set_datalink_err_str; + + if (datalink == -1) + return TRUE; /* just use the default */ + if (pcap_set_datalink(pcap_h, datalink) == 0) + return TRUE; /* no error */ + set_datalink_err_str = pcap_geterr(pcap_h); + g_snprintf(errmsg, (gulong) errmsg_len, "Unable to set data link type on interface '%s' (%s).", + name, set_datalink_err_str); + /* + * If the error isn't "XXX is not one of the DLTs supported by this device", + * tell the user to tell the Wireshark developers about it. + */ + if (strstr(set_datalink_err_str, "is not one of the DLTs supported by this device") == NULL) + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, + "%s", please_report_bug()); + else + secondary_errmsg[0] = '\0'; + return FALSE; +} + +static data_link_info_t * +create_data_link_info(int dlt) +{ + data_link_info_t *data_link_info; + const char *text; + + data_link_info = g_new(data_link_info_t, 1); + data_link_info->dlt = dlt; + text = pcap_datalink_val_to_name(dlt); + if (text != NULL) + data_link_info->name = g_strdup(text); + else + data_link_info->name = g_strdup_printf("DLT %d", dlt); + text = pcap_datalink_val_to_description(dlt); + data_link_info->description = g_strdup(text); + return data_link_info; +} + +static GList * +get_data_link_types(pcap_t *pch, interface_options *interface_opts, + cap_device_open_err *err, char **err_str) +{ + GList *data_link_types; + int deflt; + int *linktypes; + int i, nlt; + data_link_info_t *data_link_info; + + deflt = get_pcap_datalink(pch, interface_opts->name); + nlt = pcap_list_datalinks(pch, &linktypes); + if (nlt < 0) { + /* + * A negative return is an error. + */ +#ifdef HAVE_PCAP_CREATE + /* + * If we have pcap_create(), we have + * pcap_statustostr(), and we can get back errors + * other than PCAP_ERROR (-1), such as + * PCAP_ERROR_NOT_ACTIVATED. and we should report + * them properly. + */ + if (nlt == PCAP_ERROR) { + *err = CAP_DEVICE_OPEN_ERR_GENERIC; + *err_str = g_strdup_printf("pcap_list_datalinks() failed: %s", + pcap_geterr(pch)); + } else { + if (nlt == PCAP_ERROR_PERM_DENIED) + *err = CAP_DEVICE_OPEN_ERR_PERMISSIONS; + else + *err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *err_str = g_strdup(pcap_statustostr(nlt)); + } +#else /* HAVE_PCAP_CREATE */ + *err = CAP_DEVICE_OPEN_ERR_GENERIC; + *err_str = g_strdup_printf("pcap_list_datalinks() failed: %s", + pcap_geterr(pch)); +#endif /* HAVE_PCAP_CREATE */ + return NULL; + } + data_link_types = NULL; + for (i = 0; i < nlt; i++) { + data_link_info = create_data_link_info(linktypes[i]); + + /* + * XXX - for 802.11, make the most detailed 802.11 + * version the default, rather than the one the + * device has as the default? + */ + if (linktypes[i] == deflt) + data_link_types = g_list_prepend(data_link_types, + data_link_info); + else + data_link_types = g_list_append(data_link_types, + data_link_info); + } +#ifdef HAVE_PCAP_FREE_DATALINKS + pcap_free_datalinks(linktypes); +#else + /* + * In Windows, there's no guarantee that if you have a library + * built with one version of the MSVC++ run-time library, and + * it returns a pointer to allocated data, you can free that + * data from a program linked with another version of the + * MSVC++ run-time library. + * + * This is not an issue on UN*X. + * + * See the mail threads starting at + * + * https://www.winpcap.org/pipermail/winpcap-users/2006-September/001421.html + * + * and + * + * https://www.winpcap.org/pipermail/winpcap-users/2008-May/002498.html + */ +#ifndef _WIN32 +#define xx_free free /* hack so checkAPIs doesn't complain */ + xx_free(linktypes); +#endif /* _WIN32 */ +#endif /* HAVE_PCAP_FREE_DATALINKS */ + + *err_str = NULL; + return data_link_types; +} + +/* Get supported timestamp types for a libpcap device. */ +static GList* +get_pcap_timestamp_types(pcap_t *pch _U_, char **err_str _U_) +{ + GList *list = NULL; +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + int *types; + int ntypes = pcap_list_tstamp_types(pch, &types); + + if (err_str) + *err_str = ntypes < 0 ? pcap_geterr(pch) : NULL; + + if (ntypes <= 0) + return NULL; + + while (ntypes--) { + timestamp_info_t *info = (timestamp_info_t *)g_malloc(sizeof *info); + info->name = g_strdup(pcap_tstamp_type_val_to_name(types[ntypes])); + info->description = g_strdup(pcap_tstamp_type_val_to_description(types[ntypes])); + list = g_list_prepend(list, info); + } + + pcap_free_tstamp_types(types); +#endif + return list; +} + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION +/* + * Request high-resolution time stamps. + * + * We don't check for errors - if this fails, we just live with boring old + * microsecond-resolution time stamps. The only errors pcap_set_tstamp_precision() + * is documenting as returning are PCAP_ERROR_TSTAMP_PRECISION_NOTSUP, which just + * means we can't do nanosecond precision on this adapter, in which case we + * just live with whatever resolution we get by default, and + * PCAP_ERROR_ACTIVATED, which shouldn't happen as we shouldn't call this + * after we've activated the pcap_t. + */ +void +request_high_resolution_timestamp(pcap_t *pcap_h) +{ +#ifdef __APPLE__ + /* + * On macOS, if you build with a newer SDK, pcap_set_tstamp_precision() + * is available, so the code will be built with it. + * + * However, if you then try to run on an older release that + * doesn't have pcap_set_tstamp_precision(), the dynamic linker + * will fail, as it won't find pcap_set_tstamp_precision(). + * + * libpcap doesn't use macOS "weak linking" for new routines, + * so we can't just check whether a pointer to + * pcap_set_tstamp_precision() is null and, if it is, not + * call it. We have to, instead, use dlopen() to load + * libpcap, and dlsym() to find a pointer to pcap_set_tstamp_precision(), + * and if we find the pointer, call it. + */ + static gboolean initialized = FALSE; + static int (*p_pcap_set_tstamp_precision)(pcap_t *, int); + + if (!initialized) { + p_pcap_set_tstamp_precision = + (int (*)(pcap_t *, int)) + dlsym(RTLD_NEXT, "pcap_set_tstamp_precision"); + initialized = TRUE; + } + if (p_pcap_set_tstamp_precision != NULL) + (*p_pcap_set_tstamp_precision)(pcap_h, PCAP_TSTAMP_PRECISION_NANO); +#else /* __APPLE__ */ + /* + * On other UN*Xes we require that we be run on an OS version + * with a libpcap equal to or later than the version with which + * we were built. + */ + pcap_set_tstamp_precision(pcap_h, PCAP_TSTAMP_PRECISION_NANO); +#endif /* __APPLE__ */ +} + +/* + * Return TRUE if the pcap_t in question is set up for high-precision + * time stamps, FALSE otherwise. + */ +gboolean +have_high_resolution_timestamp(pcap_t *pcap_h) +{ +#ifdef __APPLE__ + /* + * See above. + */ + static gboolean initialized = FALSE; + static int (*p_pcap_get_tstamp_precision)(pcap_t *); + + if (!initialized) { + p_pcap_get_tstamp_precision = + (int (*)(pcap_t *)) + dlsym(RTLD_NEXT, "pcap_get_tstamp_precision"); + initialized = TRUE; + } + if (p_pcap_get_tstamp_precision != NULL) + return (*p_pcap_get_tstamp_precision)(pcap_h) == PCAP_TSTAMP_PRECISION_NANO; + else + return FALSE; /* Can't get implies couldn't set */ +#else /* __APPLE__ */ + /* + * On other UN*Xes we require that we be run on an OS version + * with a libpcap equal to or later than the version with which + * we were built. + */ + return pcap_get_tstamp_precision(pcap_h) == PCAP_TSTAMP_PRECISION_NANO; +#endif /* __APPLE__ */ +} + +#endif /* HAVE_PCAP_SET_TSTAMP_PRECISION */ + +#ifdef HAVE_PCAP_CREATE +#ifdef HAVE_BONDING +static gboolean +is_linux_bonding_device(const char *ifname) +{ + int fd; + struct ifreq ifr; + ifbond ifb; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd == -1) + return FALSE; + + memset(&ifr, 0, sizeof ifr); + g_strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); + memset(&ifb, 0, sizeof ifb); + ifr.ifr_data = (caddr_t)&ifb; +#if defined(SIOCBONDINFOQUERY) + if (ioctl(fd, SIOCBONDINFOQUERY, &ifr) == 0) { + close(fd); + return TRUE; + } +#else + if (ioctl(fd, BOND_INFO_QUERY_OLD, &ifr) == 0) { + close(fd); + return TRUE; + } +#endif + + close(fd); + return FALSE; +} +#else +static gboolean +is_linux_bonding_device(const char *ifname _U_) +{ + return FALSE; +} +#endif + +if_capabilities_t * +get_if_capabilities_pcap_create(interface_options *interface_opts, + cap_device_open_err *err, char **err_str) +{ + if_capabilities_t *caps; + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pch; + int status; + + pch = pcap_create(interface_opts->name, errbuf); + if (pch == NULL) { + *err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *err_str = g_strdup(errbuf); + return NULL; + } + + if (is_linux_bonding_device(interface_opts->name)) { + /* + * Linux bonding device; not Wi-Fi, so no monitor mode, and + * calling pcap_can_set_rfmon() might get a "no such device" + * error. + */ + status = 0; + } else { + /* + * Not a Linux bonding device, so go ahead. + */ + status = pcap_can_set_rfmon(pch); + } + if (status < 0) { + /* Error. */ + if (status == PCAP_ERROR) { + *err = CAP_DEVICE_OPEN_ERR_GENERIC; + *err_str = g_strdup_printf("pcap_can_set_rfmon() failed: %s", + pcap_geterr(pch)); + } else { + if (status == PCAP_ERROR_PERM_DENIED) + *err = CAP_DEVICE_OPEN_ERR_PERMISSIONS; + else + *err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *err_str = g_strdup(pcap_statustostr(status)); + } + pcap_close(pch); + return NULL; + } + caps = (if_capabilities_t *)g_malloc(sizeof *caps); + if (status == 0) + caps->can_set_rfmon = FALSE; + else if (status == 1) { + caps->can_set_rfmon = TRUE; + if (interface_opts->monitor_mode) + pcap_set_rfmon(pch, 1); + } else { + *err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *err_str = g_strdup_printf("pcap_can_set_rfmon() returned %d", + status); + pcap_close(pch); + g_free(caps); + return NULL; + } + + status = pcap_activate(pch); + if (status < 0) { + /* Error. We ignore warnings (status > 0). */ + if (status == PCAP_ERROR) { + *err = CAP_DEVICE_OPEN_ERR_GENERIC; + *err_str = g_strdup_printf("pcap_activate() failed: %s", + pcap_geterr(pch)); + } else { + if (status == PCAP_ERROR_PERM_DENIED) + *err = CAP_DEVICE_OPEN_ERR_PERMISSIONS; + else + *err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *err_str = g_strdup(pcap_statustostr(status)); + } + pcap_close(pch); + g_free(caps); + return NULL; + } + + caps->data_link_types = get_data_link_types(pch, interface_opts, + err, err_str); + if (caps->data_link_types == NULL) { + pcap_close(pch); + g_free(caps); + return NULL; + } + + caps->timestamp_types = get_pcap_timestamp_types(pch, NULL); + + pcap_close(pch); + + if (err_str != NULL) + *err_str = NULL; + return caps; +} + +pcap_t * +open_capture_device_pcap_create( +#if defined(HAVE_PCAP_SET_TSTAMP_PRECISION) + capture_options* capture_opts, +#else + capture_options* capture_opts _U_, +#endif + interface_options *interface_opts, int timeout, + cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]) +{ + pcap_t *pcap_h; + int status; + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_create() using %s.", interface_opts->name); + pcap_h = pcap_create(interface_opts->name, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_create() returned %p.", (void *)pcap_h); + if (pcap_h == NULL) { + *open_err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + return NULL; + } + if (interface_opts->has_snaplen) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_set_snaplen() with snaplen %d.", + interface_opts->snaplen); + pcap_set_snaplen(pcap_h, interface_opts->snaplen); + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_set_promisc() with promisc_mode %d.", + interface_opts->promisc_mode); + pcap_set_promisc(pcap_h, interface_opts->promisc_mode); + pcap_set_timeout(pcap_h, timeout); + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + /* + * If we're writing pcapng files, try to enable + * nanosecond-resolution capture; any code that + * can read pcapng files must be able to handle + * nanosecond-resolution time stamps. We don't + * care whether it succeeds or fails - if it fails, + * we just use the microsecond-precision time stamps + * we get. + * + * If we're writing pcap files, don't try to enable + * nanosecond-resolution capture, as not all code + * that reads pcap files recognizes the nanosecond- + * resolution pcap file magic number. + * We don't care whether this succeeds or fails; if it + * fails (because we don't have pcap_set_tstamp_precision(), + * or because we do but the OS or device doesn't support + * nanosecond resolution timing), we just use microsecond- + * resolution time stamps. + */ + if (capture_opts->use_pcapng) + request_high_resolution_timestamp(pcap_h); +#endif /* HAVE_PCAP_SET_TSTAMP_PRECISION */ + +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + if (interface_opts->timestamp_type) { + status = pcap_set_tstamp_type(pcap_h, interface_opts->timestamp_type_id); + /* + * XXX - what if it fails because that time stamp type + * isn't supported? + */ + if (status == PCAP_ERROR) { + *open_err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + g_strlcpy(*open_err_str, pcap_geterr(pcap_h), + sizeof *open_err_str); + pcap_close(pcap_h); + return NULL; + } + } +#endif /* HAVE_PCAP_SET_TSTAMP_PRECISION */ + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "buffersize %d.", interface_opts->buffer_size); + if (interface_opts->buffer_size != 0) + pcap_set_buffer_size(pcap_h, + interface_opts->buffer_size * 1024 * 1024); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "monitor_mode %d.", interface_opts->monitor_mode); + if (interface_opts->monitor_mode) + pcap_set_rfmon(pcap_h, 1); + status = pcap_activate(pcap_h); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_activate() returned %d.", status); + if (status < 0) { + /* Failed to activate, set to NULL */ + if (status == PCAP_ERROR) { + *open_err = CAP_DEVICE_OPEN_ERR_GENERIC; + g_strlcpy(*open_err_str, pcap_geterr(pcap_h), + sizeof *open_err_str); + } else { + if (status == PCAP_ERROR_PERM_DENIED) + *open_err = CAP_DEVICE_OPEN_ERR_PERMISSIONS; + else + *open_err = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + g_strlcpy(*open_err_str, pcap_statustostr(status), + sizeof *open_err_str); + } + pcap_close(pcap_h); + return NULL; + } + return pcap_h; +} +#endif /* HAVE_PCAP_CREATE */ + +if_capabilities_t * +get_if_capabilities_pcap_open_live(interface_options *interface_opts, + cap_device_open_err *err, char **err_str) +{ + if_capabilities_t *caps; + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pch; + + pch = pcap_open_live(interface_opts->name, MIN_PACKET_SIZE, 0, 0, + errbuf); + if (pch == NULL) { + *err = CAP_DEVICE_OPEN_ERR_GENERIC; + *err_str = g_strdup(errbuf[0] == '\0' ? "Unknown error (pcap bug; actual error cause not reported)" : errbuf); + return NULL; + } + + caps = (if_capabilities_t *)g_malloc(sizeof *caps); + caps->can_set_rfmon = FALSE; + caps->data_link_types = get_data_link_types(pch, interface_opts, + err, err_str); + if (caps->data_link_types == NULL) { + pcap_close(pch); + g_free(caps); + return NULL; + } + + caps->timestamp_types = get_pcap_timestamp_types(pch, NULL); + + pcap_close(pch); + + *err_str = NULL; + return caps; +} + +pcap_t * +open_capture_device_pcap_open_live(interface_options *interface_opts, + int timeout, cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]) +{ + pcap_t *pcap_h; + int snaplen; + + if (interface_opts->has_snaplen) + snaplen = interface_opts->snaplen; + else { + /* + * Default - use the non-D-Bus maximum snapshot length of + * 256KB, which should be big enough (libpcap didn't get + * D-Bus support until after it goet pcap_create() and + * pcap_activate(), so we don't have D-Bus support and + * don't have to worry about really huge packets). + */ + snaplen = 256*1024; + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open_live() calling using name %s, snaplen %d, promisc_mode %d.", + interface_opts->name, snaplen, interface_opts->promisc_mode); + pcap_h = pcap_open_live(interface_opts->name, snaplen, + interface_opts->promisc_mode, timeout, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open_live() returned %p.", (void *)pcap_h); + if (pcap_h == NULL) { + *open_err = CAP_DEVICE_OPEN_ERR_GENERIC; + return NULL; + } + +#ifdef _WIN32 + /* Try to set the capture buffer size. */ + if (interface_opts->buffer_size > 1) { + /* + * We have no mechanism to report a warning if this + * fails; we just keep capturing with the smaller buffer, + * as is the case on systems with BPF and pcap_create() + * and pcap_set_buffer_size(), where pcap_activate() just + * silently clamps the buffer size to the maximum. + */ + pcap_setbuff(pcap_h, interface_opts->buffer_size * 1024 * 1024); + } +#endif + + return pcap_h; +} + +/* + * Get the capabilities of a network device. + */ +if_capabilities_t * +get_if_capabilities(interface_options *interface_opts, + cap_device_open_err *err, char **err_str) +{ +#if defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) + if_capabilities_t *caps; + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pch; + int deflt; + data_link_info_t *data_link_info; + + if (strncmp (interface_opts->name, "rpcap://", 8) == 0) { + struct pcap_rmtauth auth; + + auth.type = interface_opts->auth_type == CAPTURE_AUTH_PWD ? + RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL; + auth.username = interface_opts->auth_username; + auth.password = interface_opts->auth_password; + + /* + * WinPcap 4.1.2, and possibly earlier versions, have a bug + * wherein, when an open with an rpcap: URL fails, the error + * message for the error is not copied to errbuf and whatever + * on-the-stack junk is in errbuf is treated as the error + * message. + * + * To work around that (and any other bugs of that sort), we + * initialize errbuf to an empty string. If we get an error + * and the string is empty, we report it as an unknown error. + * (If we *don't* get an error, and the string is *non*-empty, + * that could be a warning returned, such as "can't turn + * promiscuous mode on"; we currently don't do so.) + */ + errbuf[0] = '\0'; + pch = pcap_open(interface_opts->name, MIN_PACKET_SIZE, 0, 0, &auth, + errbuf); + if (pch == NULL) { + /* + * We don't know whether it's a permission error or not. + * (If it is, maybe we can give ourselves permission or + * maybe we just have to ask politely for permission.) + */ + *err = CAP_DEVICE_OPEN_ERR_GENERIC; + *err_str = g_strdup(errbuf[0] == '\0' ? "Unknown error (pcap bug; actual error cause not reported)" : errbuf); + return NULL; + } + + caps = (if_capabilities_t *)g_malloc(sizeof *caps); + caps->can_set_rfmon = FALSE; + caps->data_link_types = NULL; + deflt = get_pcap_datalink(pch, interface_opts->name); + data_link_info = create_data_link_info(deflt); + caps->data_link_types = g_list_append(caps->data_link_types, data_link_info); + caps->timestamp_types = get_pcap_timestamp_types(pch, NULL); + pcap_close(pch); + + *err_str = NULL; + return caps; + } +#endif /* defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) */ + + /* + * Local interface. + */ + return get_if_capabilities_local(interface_opts, err, err_str); +} + +pcap_t * +open_capture_device(capture_options *capture_opts, + interface_options *interface_opts, int timeout, + cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]) +{ + pcap_t *pcap_h; +#if defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) + struct pcap_rmtauth auth; +#endif + + /* Open the network interface to capture from it. + Some versions of libpcap may put warnings into the error buffer + if they succeed; to tell if that's happened, we have to clear + the error buffer, and check if it's still a null string. */ + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Entering open_capture_device()."); + *open_err = CAP_DEVICE_OPEN_NO_ERR; + (*open_err_str)[0] = '\0'; +#if defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) + /* + * If we're opening a remote device, use pcap_open(); that's currently + * the only open routine that supports remote devices. + */ + if (strncmp (interface_opts->name, "rpcap://", 8) == 0) { + int snaplen; + + auth.type = interface_opts->auth_type == CAPTURE_AUTH_PWD ? + RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL; + auth.username = interface_opts->auth_username; + auth.password = interface_opts->auth_password; + + if (interface_opts->has_snaplen) + snaplen = interface_opts->snaplen; + else { + /* + * Default - use the non-D-Bus maximum snapshot length, + * which should be big enough, except for D-Bus. + */ + snaplen = 256*1024; + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_open() using name %s, snaplen %d, promisc_mode %d, datatx_udp %d, nocap_rpcap %d.", + interface_opts->name, snaplen, + interface_opts->promisc_mode, interface_opts->datatx_udp, + interface_opts->nocap_rpcap); + pcap_h = pcap_open(interface_opts->name, snaplen, + /* flags */ + (interface_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) | + (interface_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) | + (interface_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0), + timeout, &auth, *open_err_str); + if (pcap_h == NULL) { + /* + * Error. + * + * We don't know whether it's a permission error + * or not. + * (If it is, maybe we can give ourselves permission + * or maybe we just have to ask politely for + * permission.) + */ + *open_err = CAP_DEVICE_OPEN_ERR_GENERIC; + /* Did pcap actually supply an error message? */ + if ((*open_err_str)[0] == '\0') { + /* + * Work around known WinPcap bug wherein + * no error message is filled in on a + * failure to open an rpcap: URL. + */ + g_strlcpy(*open_err_str, + "Unknown error (pcap bug; actual error cause not reported)", + sizeof *open_err_str); + } + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open() returned %p.", (void *)pcap_h); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "open_capture_device %s : %s", pcap_h ? "SUCCESS" : "FAILURE", interface_opts->name); + return pcap_h; + } +#endif + + pcap_h = open_capture_device_local(capture_opts, interface_opts, + timeout, open_err, open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "open_capture_device %s : %s", pcap_h ? "SUCCESS" : "FAILURE", interface_opts->name); + return pcap_h; +} + +#endif /* HAVE_LIBPCAP */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/capture/capture-pcap-util.h b/capture/capture-pcap-util.h new file mode 100644 index 0000000000..33c87c2542 --- /dev/null +++ b/capture/capture-pcap-util.h @@ -0,0 +1,93 @@ +/* capture-pcap-util.h + * Utility definitions for packet capture + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __CAPTURE_PCAP_UTIL_H__ +#define __CAPTURE_PCAP_UTIL_H__ + +#ifdef HAVE_LIBPCAP + +#include "wspcap.h" + +#include "capture_opts.h" + +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef HAVE_LIBPCAP +/* + * A snapshot length of 0 is useless - and libpcap/WinPcap/Npcap don't guarantee + * that a snapshot length of 0 will work, and, on some platforms, it won't + * (with BPF, for example, the kernel is told the snapshot length via the + * return value of the BPF program, and a return value of 0 means "drop + * the packet"), so the minimum packet size is 1 byte. + */ +#define MIN_PACKET_SIZE 1 /* minimum amount of packet data we can read */ + +GList *get_interface_list(int *err, char **err_str); +#ifdef HAVE_PCAP_REMOTE +GList *get_remote_interface_list(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str); +#endif /* HAVE_PCAP_REMOTE */ + +const char *linktype_val_to_name(int dlt); +int linktype_name_to_val(const char *linktype); + +int get_pcap_datalink(pcap_t *pch, const char *devicename); + +gboolean set_pcap_datalink(pcap_t *pcap_h, int datalink, char *name, + char *errmsg, size_t errmsg_len, + char *secondary_errmsg, size_t secondary_errmsg_len); + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION +/* + * Return TRUE if the pcap_t in question is set up for high-precision + * time stamps, FALSE otherwise. + */ +gboolean have_high_resolution_timestamp(pcap_t *pcap_h); +#endif /* HAVE_PCAP_SET_TSTAMP_PRECISION */ + +/* + * Error values. + */ +typedef enum { + CAP_DEVICE_OPEN_NO_ERR, /* No error */ + CAP_DEVICE_OPEN_ERR_PERMISSIONS, /* Error is known to be a permissions error */ + CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS, /* Error is known not to be a permissions error */ + CAP_DEVICE_OPEN_ERR_GENERIC /* Error is not known to be one or the other */ +} cap_device_open_err; +extern if_capabilities_t *get_if_capabilities(interface_options *interface_opts, + cap_device_open_err *err, char **err_str); +extern pcap_t *open_capture_device(capture_options *capture_opts, + interface_options *interface_opts, + int timeout, cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]); + +#endif /* HAVE_LIBPCAP */ + +extern void get_compiled_caplibs_version(GString *str); + +/* + * Append to a GString an indication of the version of capture libraries + * with which we're running, or an indication that we're not running + * with capture libraries, if we were compiled with WinPcap or Npcap but + * WinPcap/Npcap wasn't loaded, or nothing, if we weren't compiled with + * libpcap/WinPcap/Npcap. + */ +extern void get_runtime_caplibs_version(GString *str); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPTURE_PCAP_UTIL_H__ */ diff --git a/capture/capture-wpcap.c b/capture/capture-wpcap.c new file mode 100644 index 0000000000..c3d8fdfea4 --- /dev/null +++ b/capture/capture-wpcap.c @@ -0,0 +1,915 @@ +/* capture-wpcap.c + * WinPcap/Npcap-specific interfaces for capturing. We load WinPcap/Npcap + * at run time, so that we only need one Wireshark binary and one TShark + * binary for Windows, regardless of whether WinPcap/Npcap is installed + * or not. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 2001 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "capture/capture-wpcap.h" + +gboolean has_wpcap = FALSE; + +#ifdef HAVE_LIBPCAP + +#include + +#include + +#include "capture/capture_ifinfo.h" +#include "capture/capture-pcap-util.h" +#include "capture/capture-pcap-util-int.h" + +#include + +/* XXX - yes, I know, I should move cppmagic.h to a generic location. */ +#include "tools/lemon/cppmagic.h" + +#define MAX_WIN_IF_NAME_LEN 511 + +/* + * XXX - should we require at least WinPcap 3.1 both for building an + * for using Wireshark? + */ + +static void (*p_pcap_close) (pcap_t *); +static int (*p_pcap_stats) (pcap_t *, struct pcap_stat *); +static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, guchar *); +static int (*p_pcap_snapshot) (pcap_t *); +static int (*p_pcap_datalink) (pcap_t *); +static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); +static char* (*p_pcap_geterr) (pcap_t *); +static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, const char *, int, + bpf_u_int32); +static int (*p_pcap_compile_nopcap) (int, int, struct bpf_program *, const char *, int, + bpf_u_int32); +static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, + char *); +static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); +static int (*p_pcap_loop) (pcap_t *, int, pcap_handler, guchar *); +static pcap_t* (*p_pcap_open_dead) (int, int); +static void (*p_pcap_freecode) (struct bpf_program *); +static int (*p_pcap_findalldevs) (pcap_if_t **, char *); +static void (*p_pcap_freealldevs) (pcap_if_t *); +static int (*p_pcap_datalink_name_to_val) (const char *); +static const char *(*p_pcap_datalink_val_to_name) (int); +static const char *(*p_pcap_datalink_val_to_description) (int); +static void (*p_pcap_breakloop) (pcap_t *); +static const char *(*p_pcap_lib_version) (void); +static int (*p_pcap_setbuff) (pcap_t *, int dim); +static int (*p_pcap_next_ex) (pcap_t *, struct pcap_pkthdr **pkt_header, const u_char **pkt_data); +#ifdef HAVE_PCAP_REMOTE +static pcap_t* (*p_pcap_open) (const char *, int, int, int, + struct pcap_rmtauth *, char *); +static int (*p_pcap_findalldevs_ex) (const char *, struct pcap_rmtauth *, + pcap_if_t **, char *); +static int (*p_pcap_createsrcstr) (char *, int, const char *, const char *, + const char *, char *); +#endif +#ifdef HAVE_PCAP_SETSAMPLING +static struct pcap_samp* (*p_pcap_setsampling)(pcap_t *); +#endif + +static int (*p_pcap_list_datalinks)(pcap_t *, int **); +static int (*p_pcap_set_datalink)(pcap_t *, int); + +#ifdef HAVE_PCAP_FREE_DATALINKS +static int (*p_pcap_free_datalinks)(int *); +#endif + +static char *(*p_bpf_image)(const struct bpf_insn *, int); + +#ifdef HAVE_PCAP_CREATE +static pcap_t *(*p_pcap_create)(const char *, char *); +static int (*p_pcap_set_snaplen)(pcap_t *, int); +static int (*p_pcap_set_promisc)(pcap_t *, int); +static int (*p_pcap_can_set_rfmon)(pcap_t *); +static int (*p_pcap_set_rfmon)(pcap_t *, int); +static int (*p_pcap_set_timeout)(pcap_t *, int); +static int (*p_pcap_set_buffer_size)(pcap_t *, int); +static int (*p_pcap_activate)(pcap_t *); +static const char *(*p_pcap_statustostr)(int); +#endif + +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +static int (*p_pcap_set_tstamp_type)(pcap_t *, int); +static int (*p_pcap_set_tstamp_precision)(pcap_t *, int); +static int (*p_pcap_get_tstamp_precision)(pcap_t *); +static int (*p_pcap_list_tstamp_types)(pcap_t *, int **); +static void (*p_pcap_free_tstamp_types)(int *); +static int (*p_pcap_tstamp_type_name_to_val)(const char *); +static const char * (*p_pcap_tstamp_type_val_to_name)(int); +static const char * (*p_pcap_tstamp_type_val_to_description)(int); +#endif + +typedef struct { + const char *name; + gpointer *ptr; + gboolean optional; +} symbol_table_t; + +#define SYM(x, y) { G_STRINGIFY(x) , (gpointer) &CONCAT(p_,x), y } + +void +load_wpcap(void) +{ + + /* These are the symbols I need or want from Wpcap */ + static const symbol_table_t symbols[] = { + SYM(pcap_close, FALSE), + SYM(pcap_stats, FALSE), + SYM(pcap_dispatch, FALSE), + SYM(pcap_snapshot, FALSE), + SYM(pcap_datalink, FALSE), + SYM(pcap_setfilter, FALSE), + SYM(pcap_geterr, FALSE), + SYM(pcap_compile, FALSE), + SYM(pcap_compile_nopcap, FALSE), + SYM(pcap_lookupnet, FALSE), +#ifdef HAVE_PCAP_REMOTE + SYM(pcap_open, FALSE), + SYM(pcap_findalldevs_ex, FALSE), + SYM(pcap_createsrcstr, FALSE), +#endif + SYM(pcap_open_live, FALSE), + SYM(pcap_open_dead, FALSE), +#ifdef HAVE_PCAP_SETSAMPLING + SYM(pcap_setsampling, TRUE), +#endif + SYM(pcap_loop, FALSE), + SYM(pcap_freecode, FALSE), + SYM(pcap_findalldevs, FALSE), + SYM(pcap_freealldevs, FALSE), + SYM(pcap_datalink_name_to_val, FALSE), + SYM(pcap_datalink_val_to_name, FALSE), + SYM(pcap_datalink_val_to_description, FALSE), + SYM(pcap_breakloop, FALSE), + SYM(pcap_lib_version, FALSE), + SYM(pcap_setbuff, TRUE), + SYM(pcap_next_ex, TRUE), + SYM(pcap_list_datalinks, FALSE), + SYM(pcap_set_datalink, FALSE), +#ifdef HAVE_PCAP_FREE_DATALINKS + SYM(pcap_free_datalinks, TRUE), +#endif + SYM(bpf_image, FALSE), +#ifdef HAVE_PCAP_CREATE + SYM(pcap_create, TRUE), + SYM(pcap_set_snaplen, TRUE), + SYM(pcap_set_promisc, TRUE), + SYM(pcap_can_set_rfmon, TRUE), + SYM(pcap_set_rfmon, TRUE), + SYM(pcap_set_timeout, FALSE), + SYM(pcap_set_buffer_size, FALSE), + SYM(pcap_activate, TRUE), + SYM(pcap_statustostr, TRUE), +#endif +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + SYM(pcap_set_tstamp_type, TRUE), + SYM(pcap_set_tstamp_precision, TRUE), + SYM(pcap_get_tstamp_precision, TRUE), + SYM(pcap_list_tstamp_types, TRUE), + SYM(pcap_free_tstamp_types, TRUE), + SYM(pcap_tstamp_type_name_to_val, TRUE), + SYM(pcap_tstamp_type_val_to_name, TRUE), + SYM(pcap_tstamp_type_val_to_description, TRUE), +#endif + { NULL, NULL, FALSE } + }; + + GModule *wh; /* wpcap handle */ + const symbol_table_t *sym; + + wh = ws_module_open("wpcap.dll", 0); + + if (!wh) { + return; + } + + sym = symbols; + while (sym->name) { + if (!g_module_symbol(wh, sym->name, sym->ptr)) { + if (sym->optional) { + /* + * We don't care if it's missing; we just + * don't use it. + */ + *sym->ptr = NULL; + } else { + /* + * We require this symbol. + */ + return; + } + } + sym++; + } + + + has_wpcap = TRUE; +} + +static char * +local_code_page_str_to_utf8(char *str) +{ + ULONG utf16_len; + wchar_t *utf16_str; + char *utf8_str; + + if (str == NULL) { + return NULL; + } + + utf16_len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + utf16_str = g_malloc_n(utf16_len, sizeof(wchar_t)); + MultiByteToWideChar(CP_ACP, 0, str, -1, utf16_str, utf16_len); + + utf8_str = g_utf16_to_utf8(utf16_str, -1, NULL, NULL, NULL); + + g_free(utf16_str); + return utf8_str; +} + +static void +prepare_errbuf(char *errbuf) +{ + g_assert(errbuf); + errbuf[0] = '\0'; +} + +static void +convert_errbuf_to_utf8(char *errbuf) +{ + gchar *utf8_err; + if (errbuf[0] == '\0') { + return; + } + errbuf[PCAP_ERRBUF_SIZE - 1] = '\0'; + utf8_err = local_code_page_str_to_utf8(errbuf); + g_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s", utf8_err); + g_free(utf8_err); +} + +static char * +cant_load_winpcap_err(const char *app_name) +{ + return g_strdup_printf( +"Unable to load Npcap or WinPcap (wpcap.dll); %s will not be able to\n" +"capture packets.\n" +"\n" +"In order to capture packets Npcap or WinPcap must be installed. See\n" +"\n" +" https://nmap.org/npcap/\n" +"\n" +"for a downloadable version of Npcap and for instructions on how to\n" +"install it.", + app_name); +} + +void +pcap_close(pcap_t *a) +{ + g_assert(has_wpcap); + p_pcap_close(a); +} + +int +pcap_stats(pcap_t *a, struct pcap_stat *b) +{ + g_assert(has_wpcap); + return p_pcap_stats(a, b); +} + +int +pcap_dispatch(pcap_t *a, int b, pcap_handler c, guchar *d) +{ + g_assert(has_wpcap); + return p_pcap_dispatch(a, b, c, d); +} + +int +pcap_snapshot(pcap_t *a) +{ + g_assert(has_wpcap); + return p_pcap_snapshot(a); +} + +int +pcap_datalink(pcap_t *a) +{ + g_assert(has_wpcap); + return p_pcap_datalink(a); +} + +int +pcap_set_datalink(pcap_t *p, int dlt) +{ + g_assert(has_wpcap); + return p_pcap_set_datalink(p, dlt); +} + +int +pcap_setfilter(pcap_t *a, struct bpf_program *b) +{ + g_assert(has_wpcap); + return p_pcap_setfilter(a, b); +} + +char* +pcap_geterr(pcap_t *a) +{ + char *errbuf; + g_assert(has_wpcap); + errbuf = p_pcap_geterr(a); + convert_errbuf_to_utf8(errbuf); + return errbuf; +} + +int +pcap_compile(pcap_t *a, struct bpf_program *b, const char *c, int d, + bpf_u_int32 e) +{ + g_assert(has_wpcap); + return p_pcap_compile(a, b, c, d, e); +} + +int +pcap_compile_nopcap(int a, int b, struct bpf_program *c, const char *d, int e, + bpf_u_int32 f) +{ + g_assert(has_wpcap); + return p_pcap_compile_nopcap(a, b, c, d, e, f); +} + +int +pcap_lookupnet(const char *a, bpf_u_int32 *b, bpf_u_int32 *c, char *errbuf) +{ + int ret; + g_assert(has_wpcap); + ret = p_pcap_lookupnet(a, b, c, errbuf); + if (ret == -1) + convert_errbuf_to_utf8(errbuf); + return ret; +} + +pcap_t* +pcap_open_live(const char *a, int b, int c, int d, char *errbuf) +{ + pcap_t *p; + if (!has_wpcap) { + g_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "unable to load Npcap or WinPcap (wpcap.dll); can't open %s to capture", + a); + return NULL; + } + prepare_errbuf(errbuf); + p = p_pcap_open_live(a, b, c, d, errbuf); + convert_errbuf_to_utf8(errbuf); + return p; +} + +pcap_t* +pcap_open_dead(int a, int b) +{ + if (!has_wpcap) { + return NULL; + } + return p_pcap_open_dead(a, b); +} + +char * +bpf_image(const struct bpf_insn *a, int b) +{ + if (!has_wpcap) { + return NULL; + } + return p_bpf_image(a, b); +} + +#ifdef HAVE_PCAP_REMOTE +pcap_t* +pcap_open(const char *a, int b, int c, int d, struct pcap_rmtauth *e, char *errbuf) +{ + pcap_t *ret; + if (!has_wpcap) { + g_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "unable to load Npcap or WinPcap (wpcap.dll); can't open %s to capture", + a); + return NULL; + } + prepare_errbuf(errbuf); + ret = p_pcap_open(a, b, c, d, e, errbuf); + convert_errbuf_to_utf8(errbuf); + return ret; +} + +int +pcap_findalldevs_ex(const char *a, struct pcap_rmtauth *b, pcap_if_t **c, char *errbuf) +{ + int ret; + g_assert(has_wpcap); + ret = p_pcap_findalldevs_ex(a, b, c, errbuf); + if (ret == -1) + convert_errbuf_to_utf8(errbuf); + return ret; +} + +int +pcap_createsrcstr(char *a, int b, const char *c, const char *d, const char *e, + char *errbuf) +{ + int ret; + g_assert(has_wpcap); + ret = p_pcap_createsrcstr(a, b, c, d, e, errbuf); + if (ret == -1) + convert_errbuf_to_utf8(errbuf); + return ret; +} +#endif + +#ifdef HAVE_PCAP_SETSAMPLING +struct pcap_samp * +pcap_setsampling(pcap_t *a) +{ + g_assert(has_wpcap); + if (p_pcap_setsampling != NULL) { + return p_pcap_setsampling(a); + } + return NULL; +} +#endif + +int +pcap_loop(pcap_t *a, int b, pcap_handler c, guchar *d) +{ + g_assert(has_wpcap); + return p_pcap_loop(a, b, c, d); +} + +void +pcap_freecode(struct bpf_program *a) +{ + g_assert(has_wpcap); + p_pcap_freecode(a); +} + +int +pcap_findalldevs(pcap_if_t **a, char *errbuf) +{ + int ret; + g_assert(has_wpcap); + ret = p_pcap_findalldevs(a, errbuf); + if (ret == -1) + convert_errbuf_to_utf8(errbuf); + return ret; +} + +void +pcap_freealldevs(pcap_if_t *a) +{ + g_assert(has_wpcap); + p_pcap_freealldevs(a); +} + +#ifdef HAVE_PCAP_CREATE +pcap_t * +pcap_create(const char *a, char *errbuf) +{ + pcap_t *p; + g_assert(has_wpcap && p_pcap_create != NULL); + p = p_pcap_create(a, errbuf); + if (p == NULL) + convert_errbuf_to_utf8(errbuf); + return p; +} + +int +pcap_set_snaplen(pcap_t *a, int b) +{ + g_assert(has_wpcap && p_pcap_set_snaplen != NULL); + return p_pcap_set_snaplen(a, b); +} + +int +pcap_set_promisc(pcap_t *a, int b) +{ + g_assert(has_wpcap && p_pcap_set_promisc != NULL); + return p_pcap_set_promisc(a, b); +} + +int +pcap_can_set_rfmon(pcap_t *a) +{ + g_assert(has_wpcap); + if (p_pcap_can_set_rfmon != NULL) { + return p_pcap_can_set_rfmon(a); + } + return 0; +} + +int +pcap_set_rfmon(pcap_t *a, int b) +{ + g_assert(has_wpcap && p_pcap_set_rfmon != NULL); + return p_pcap_set_rfmon(a, b); +} + +int +pcap_set_timeout(pcap_t *a, int b) +{ + g_assert(has_wpcap && p_pcap_set_timeout != NULL); + return p_pcap_set_timeout(a, b); +} +int +pcap_set_buffer_size(pcap_t *a, int b) +{ + g_assert(has_wpcap && p_pcap_set_buffer_size != NULL); + return p_pcap_set_buffer_size(a, b); +} + +int +pcap_activate(pcap_t *a) +{ + g_assert(has_wpcap && p_pcap_activate != NULL); + return p_pcap_activate(a); + +} + +const char * +pcap_statustostr(int a) +{ + static char ebuf[15 + 10 + 1]; + + g_assert(has_wpcap); + if (p_pcap_statustostr != NULL) { + return p_pcap_statustostr(a); + } + + /* XXX copy routine from pcap.c ??? */ + (void)g_snprintf(ebuf, sizeof ebuf, "Don't have pcap_statustostr(), can't translate error: %d", a); + return(ebuf); + +} +#endif + +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +int +pcap_set_tstamp_type(pcap_t *a, int b) { + g_assert(has_wpcap); + if (p_pcap_set_tstamp_type != NULL) { + return p_pcap_set_tstamp_type(a, b); + } + return PCAP_ERROR_CANTSET_TSTAMP_TYPE; +} + +int +pcap_set_tstamp_precision(pcap_t *a, int b) { + g_assert(has_wpcap); + if (p_pcap_set_tstamp_precision != NULL) { + return p_pcap_set_tstamp_precision(a, b); + } + // No error code defined so return NOTSUP. + return PCAP_ERROR_TSTAMP_PRECISION_NOTSUP; +} + +int +pcap_get_tstamp_precision(pcap_t *a) { + g_assert(has_wpcap); + if (p_pcap_get_tstamp_precision != NULL) { + return p_pcap_get_tstamp_precision(a); + } + // No error code defined so return MICRO. + return PCAP_TSTAMP_PRECISION_MICRO; +} + +int +pcap_list_tstamp_types(pcap_t *a, int **b) { + g_assert(has_wpcap); + if (p_pcap_list_tstamp_types != NULL) { + return p_pcap_list_tstamp_types(a, b); + } + return PCAP_ERROR; +} + +void +pcap_free_tstamp_types(int *a) { + g_assert(has_wpcap); + if (p_pcap_free_tstamp_types != NULL) { + p_pcap_free_tstamp_types(a); + } +} + +int +pcap_tstamp_type_name_to_val(const char *a) { + g_assert(has_wpcap); + if (p_pcap_tstamp_type_name_to_val != NULL) { + return p_pcap_tstamp_type_name_to_val(a); + } + return PCAP_ERROR; +} + +const char * +pcap_tstamp_type_val_to_name(int a) { + g_assert(has_wpcap); + if (p_pcap_tstamp_type_val_to_name != NULL) { + return p_pcap_tstamp_type_val_to_name(a); + } + return NULL; +} + +const char * +pcap_tstamp_type_val_to_description(int a) { + g_assert(has_wpcap); + if (p_pcap_tstamp_type_val_to_description != NULL) { + return p_pcap_tstamp_type_val_to_description(a); + } + return NULL; +} +#endif + +int +pcap_datalink_name_to_val(const char *name) +{ + if (has_wpcap) + return p_pcap_datalink_name_to_val(name); + else + return -1; +} + +int +pcap_list_datalinks(pcap_t *p, int **ddlt) +{ + if (has_wpcap) + return p_pcap_list_datalinks(p, ddlt); + else + return -1; +} + +#ifdef HAVE_PCAP_FREE_DATALINKS +void +pcap_free_datalinks(int *ddlt) +{ + g_assert(has_wpcap); + + /* + * If we don't have pcap_free_datalinks() in WinPcap, + * we don't free the memory - we can't use free(), as + * we might not have been built with the same version + * of the C runtime library as WinPcap was, and, if we're + * not, free() isn't guaranteed to work on something + * allocated by WinPcap. + */ + if (p_pcap_free_datalinks != NULL) + p_pcap_free_datalinks(ddlt); +} +#endif + +const char * +pcap_datalink_val_to_name(int dlt) +{ + if (has_wpcap) + return p_pcap_datalink_val_to_name(dlt); + else + return NULL; +} + +const char * +pcap_datalink_val_to_description(int dlt) +{ + if (has_wpcap) + return p_pcap_datalink_val_to_description(dlt); + return NULL; +} + +void pcap_breakloop(pcap_t *a) +{ + p_pcap_breakloop(a); +} + +/* setbuff is win32 specific! */ +int pcap_setbuff(pcap_t *a, int b) +{ + g_assert(has_wpcap); + return p_pcap_setbuff(a, b); +} + +/* pcap_next_ex is available since libpcap 0.8 / WinPcap 3.0! */ +/* (if you get a declaration warning here, try to update to at least WinPcap 3.1b4 develpack) */ +int pcap_next_ex(pcap_t *a, struct pcap_pkthdr **b, const u_char **c) +{ + g_assert(has_wpcap); + return p_pcap_next_ex(a, b, c); +} + +#ifdef HAVE_PCAP_REMOTE +GList * +get_remote_interface_list(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str) +{ + if (!has_wpcap) { + /* + * We don't have Npcap or WinPcap, so we can't get a list of + * interfaces. + */ + *err = DONT_HAVE_PCAP; + if (err_str != NULL) + *err_str = cant_load_winpcap_err("you"); + return NULL; + } + + return get_interface_list_findalldevs_ex(hostname, port, auth_type, + username, passwd, err, err_str); +} +#endif + +GList * +get_interface_list(int *err, char **err_str) +{ + if (!has_wpcap) { + /* + * We don't have Npcap or WinPcap, so we can't get a list of + * interfaces. + */ + *err = DONT_HAVE_PCAP; + if (err_str != NULL) + *err_str = cant_load_winpcap_err("you"); + return NULL; + } + + return get_interface_list_findalldevs(err, err_str); +} + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_interface_list()". + */ +gchar * +cant_get_if_list_error_message(const char *err_str) +{ + /* + * If the error message includes "Not enough storage is available + * to process this command" or "The operation completed successfully", + * suggest that they install a WinPcap version later than 3.0. + */ + if (strstr(err_str, "Not enough storage is available to process this command") != NULL || + strstr(err_str, "The operation completed successfully") != NULL) { + return g_strdup_printf("Can't get list of interfaces: %s\n" +"This might be a problem with WinPcap 3.0. You should try updating to\n" +"Npcap. See https://nmap.org/npcap/ for more information.", + err_str); + } + return g_strdup_printf("Can't get list of interfaces: %s", err_str); +} + +if_capabilities_t * +get_if_capabilities_local(interface_options *interface_opts, + cap_device_open_err *err, char **err_str) +{ + /* + * We're not getting capaibilities for a remote device; use + * pcap_create() and pcap_activate() if we have them, so that + * we can set various options, otherwise use pcap_open_live(). + */ +#ifdef HAVE_PCAP_CREATE + if (p_pcap_create != NULL) + return get_if_capabilities_pcap_create(interface_opts, err, err_str); +#endif + return get_if_capabilities_pcap_open_live(interface_opts, err, err_str); +} + +pcap_t * +open_capture_device_local(capture_options *capture_opts, + interface_options *interface_opts, int timeout, + cap_device_open_err *open_err, + char (*open_err_str)[PCAP_ERRBUF_SIZE]) +{ + /* + * We're not opening a remote device; use pcap_create() and + * pcap_activate() if we have them, so that we can set various + * options, otherwise use pcap_open_live(). + */ +#ifdef HAVE_PCAP_CREATE + if (p_pcap_create != NULL) + return open_capture_device_pcap_create(capture_opts, + interface_opts, timeout, open_err, open_err_str); +#endif + return open_capture_device_pcap_open_live(interface_opts, timeout, + open_err, open_err_str); +} + +/* + * Append the WinPcap or Npcap SDK version with which we were compiled to a GString. + */ +void +get_compiled_caplibs_version(GString *str) +{ + g_string_append(str, "with libpcap"); +} + +/* + * Append the version of Npcap with which we we're running to a GString. + */ +void +get_runtime_caplibs_version(GString *str) +{ + /* + * On Windows, we might have been compiled with WinPcap/Npcap but + * might not have it loaded; indicate whether we have it or + * not and, if we have it, what version we have. + */ + if (has_wpcap) { + g_string_append_printf(str, "with "); + g_string_append_printf(str, p_pcap_lib_version()); + } else + g_string_append(str, "without Npcap or WinPcap"); +} + +/* + * If npf.sys is running, return TRUE. + */ +gboolean +npf_sys_is_running(void) +{ + SC_HANDLE h_scm, h_serv; + SERVICE_STATUS ss; + + h_scm = OpenSCManager(NULL, NULL, 0); + if (!h_scm) + return FALSE; + + h_serv = OpenService(h_scm, _T("npcap"), SC_MANAGER_CONNECT|SERVICE_QUERY_STATUS); + if (!h_serv) { + h_serv = OpenService(h_scm, _T("npf"), SC_MANAGER_CONNECT|SERVICE_QUERY_STATUS); + if (!h_serv) { + CloseServiceHandle(h_scm); + return FALSE; + } + } + + if (QueryServiceStatus(h_serv, &ss)) { + if (ss.dwCurrentState & SERVICE_RUNNING) { + CloseServiceHandle(h_serv); + CloseServiceHandle(h_scm); + return TRUE; + } + } + CloseServiceHandle(h_serv); + CloseServiceHandle(h_scm); + return FALSE; +} + +#else /* HAVE_LIBPCAP */ + +void +load_wpcap(void) +{ + return; +} + +/* + * Append an indication that we were not compiled with WinPcap + * to a GString. + */ +void +get_compiled_caplibs_version(GString *str) +{ + g_string_append(str, "without Npcap or WinPcap"); +} + +/* + * Don't append anything, as we weren't even compiled to use WinPcap/Npcap. + */ +void +get_runtime_caplibs_version(GString *str _U_) +{ +} + +#endif /* HAVE_LIBPCAP */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/capture/capture-wpcap.h b/capture/capture-wpcap.h new file mode 100644 index 0000000000..3e18a79de4 --- /dev/null +++ b/capture/capture-wpcap.h @@ -0,0 +1,32 @@ +/* capture-wpcap.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 2001 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_WPCAP_H +#define CAPTURE_WPCAP_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern gboolean has_wpcap; + +extern void load_wpcap(void); + +/** + * Check to see if npf.sys is running. + * @return TRUE if npf.sys is running, FALSE if it's not or if there was + * an error checking its status. + */ +gboolean npf_sys_is_running(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAPTURE_WPCAP_H */ diff --git a/capture/capture_ifinfo.c b/capture/capture_ifinfo.c new file mode 100644 index 0000000000..d8f2d33bc9 --- /dev/null +++ b/capture/capture_ifinfo.c @@ -0,0 +1,373 @@ +/* capture_ifinfo.c + * Routines for getting interface information from dumpcap + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#ifdef HAVE_LIBPCAP + +#include +#include +#include + +#include + +#include "capture_opts.h" + +#include "capture/capture_session.h" +#include "capture/capture_sync.h" +#include "extcap.h" +#include "log.h" + +#include +#include + +#ifdef HAVE_PCAP_REMOTE +static GList *remote_interface_list = NULL; + +static GList * append_remote_list(GList *iflist) +{ + GSList *list; + GList *rlist; + if_addr_t *if_addr, *temp_addr; + if_info_t *if_info, *temp; + + for (rlist = g_list_nth(remote_interface_list, 0); rlist != NULL; rlist = g_list_next(rlist)) { + if_info = (if_info_t *)rlist->data; + temp = g_new0(if_info_t, 1); + temp->name = g_strdup(if_info->name); + temp->friendly_name = g_strdup(if_info->friendly_name); + temp->vendor_description = g_strdup(if_info->vendor_description); + for (list = g_slist_nth(if_info->addrs, 0); list != NULL; list = g_slist_next(list)) { + temp_addr = g_new0(if_addr_t, 1); + if_addr = (if_addr_t *)list->data; + if (if_addr) { + temp_addr->ifat_type = if_addr->ifat_type; + if (temp_addr->ifat_type == IF_AT_IPv4) { + temp_addr->addr.ip4_addr = if_addr->addr.ip4_addr; + } else { + memcpy(temp_addr->addr.ip6_addr, if_addr->addr.ip6_addr, sizeof(if_addr->addr)); + } + } else { + g_free(temp_addr); + temp_addr = NULL; + } + if (temp_addr) { + temp->addrs = g_slist_append(temp->addrs, temp_addr); + } + } + temp->loopback = if_info->loopback; + iflist = g_list_append(iflist, temp); + } + return iflist; +} +#endif + +/** + * Fetch the interface list from a child process (dumpcap). + * + * @return A GList containing if_info_t structs if successful, NULL (with err and possibly err_str set) otherwise. + * + */ + +/* XXX - We parse simple text output to get our interface list. Should + * we use "real" data serialization instead, e.g. via XML? */ +GList * +capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) +{ + int ret; + GList *if_list = NULL; + int i, j; + gchar *data, *primary_msg, *secondary_msg; + gchar **raw_list, **if_parts, **addr_parts; + gchar *name; + if_info_t *if_info; + if_addr_t *if_addr; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ..."); + + *err = 0; + if (err_str) { + *err_str = NULL; + } + + /* Try to get our interface list */ + ret = sync_interface_list_open(&data, &primary_msg, &secondary_msg, update_cb); + if (ret != 0) { + /* Add the extcap interfaces that can exist, even if no native interfaces have been found */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Loading External Capture Interface List ..."); + if_list = append_extcap_interface_list(if_list, err_str); + /* err_str is ignored, as the error for the interface loading list will take precedence */ + if ( g_list_length(if_list) == 0 ) { + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed. Error %d, %s (%s)", + *err, primary_msg ? primary_msg : "no message", + secondary_msg ? secondary_msg : "no secondary message"); + if (err_str) { + *err_str = primary_msg; + } else { + g_free(primary_msg); + } + g_free(secondary_msg); + *err = CANT_GET_INTERFACE_LIST; + + } + return if_list; + } + + /* Split our lines */ +#ifdef _WIN32 + raw_list = g_strsplit(data, "\r\n", 0); +#else + raw_list = g_strsplit(data, "\n", 0); +#endif + g_free(data); + + for (i = 0; raw_list[i] != NULL; i++) { + if_parts = g_strsplit(raw_list[i], "\t", 7); + if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL || + if_parts[3] == NULL || if_parts[4] == NULL || if_parts[5] == NULL || + if_parts[6] == NULL) { + g_strfreev(if_parts); + continue; + } + + /* Number followed by the name, e.g "1. eth0" */ + name = strchr(if_parts[0], ' '); + if (name) { + name++; + } else { + g_strfreev(if_parts); + continue; + } + + if_info = g_new0(if_info_t,1); + if_info->name = g_strdup(name); + if (strlen(if_parts[1]) > 0) + if_info->vendor_description = g_strdup(if_parts[1]); + if (strlen(if_parts[2]) > 0) + if_info->friendly_name = g_strdup(if_parts[2]); + if_info->type = (interface_type)(int)strtol(if_parts[3], NULL, 10); + addr_parts = g_strsplit(if_parts[4], ",", 0); + for (j = 0; addr_parts[j] != NULL; j++) { + if_addr = g_new0(if_addr_t,1); + if (ws_inet_pton4(addr_parts[j], &if_addr->addr.ip4_addr)) { + if_addr->ifat_type = IF_AT_IPv4; + } else if (ws_inet_pton6(addr_parts[j], (ws_in6_addr *)&if_addr->addr.ip6_addr)) { + if_addr->ifat_type = IF_AT_IPv6; + } else { + g_free(if_addr); + if_addr = NULL; + } + if (if_addr) { + if_info->addrs = g_slist_append(if_info->addrs, if_addr); + } + } + if (strcmp(if_parts[5], "loopback") == 0) + if_info->loopback = TRUE; + if_info->extcap = g_strdup(if_parts[6]); + g_strfreev(if_parts); + g_strfreev(addr_parts); + if_list = g_list_append(if_list, if_info); + } + g_strfreev(raw_list); + +#ifdef HAVE_PCAP_REMOTE + if (remote_interface_list && g_list_length(remote_interface_list) > 0) { + if_list = append_remote_list(if_list); + } +#endif + + /* Add the extcap interfaces after the native and remote interfaces */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Loading External Capture Interface List ..."); + if_list = append_extcap_interface_list(if_list, err_str); + + return if_list; +} + +/* XXX - We parse simple text output to get our interface list. Should + * we use "real" data serialization instead, e.g. via XML? */ +if_capabilities_t * +capture_get_if_capabilities(const gchar *ifname, gboolean monitor_mode, + const gchar *auth_string, + char **err_primary_msg, char **err_secondary_msg, + void (*update_cb)(void)) +{ + if_capabilities_t *caps; + GList *linktype_list = NULL, *timestamp_list = NULL; + int err, i; + gchar *data, *primary_msg, *secondary_msg; + gchar **raw_list; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities ..."); + + /* see if the interface is from extcap */ + caps = extcap_get_if_dlts(ifname, err_primary_msg); + if (caps != NULL) + return caps; + + /* return if the extcap interface generated an error */ + if (err_primary_msg != NULL && *err_primary_msg != NULL) + return NULL; + + /* Try to get our interface list */ + err = sync_if_capabilities_open(ifname, monitor_mode, auth_string, &data, + &primary_msg, &secondary_msg, update_cb); + if (err != 0) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities failed. Error %d, %s", + err, primary_msg ? primary_msg : "no message"); + if (err_primary_msg) + *err_primary_msg = primary_msg; + else + g_free(primary_msg); + if (err_secondary_msg) + *err_secondary_msg = secondary_msg; + else + g_free(secondary_msg); + return NULL; + } + + /* Split our lines */ +#ifdef _WIN32 + raw_list = g_strsplit(data, "\r\n", 0); +#else + raw_list = g_strsplit(data, "\n", 0); +#endif + g_free(data); + + /* + * First line is 0 if monitor mode isn't supported, 1 if it is. + */ + if (raw_list[0] == NULL || *raw_list[0] == '\0') { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities returned no information."); + if (err_primary_msg) { + *err_primary_msg = g_strdup("Dumpcap returned no interface capability information"); + } + g_strfreev(raw_list); + return NULL; + } + + /* + * Allocate the interface capabilities structure. + */ + caps = (if_capabilities_t *)g_malloc(sizeof *caps); + switch (*raw_list[0]) { + + case '0': + caps->can_set_rfmon = FALSE; + break; + + case '1': + caps->can_set_rfmon = TRUE; + break; + + default: + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities returned bad information."); + if (err_primary_msg) { + *err_primary_msg = g_strdup_printf("Dumpcap returned \"%s\" for monitor-mode capability", + raw_list[0]); + } + g_free(caps); + g_strfreev(raw_list); + return NULL; + } + + /* + * The following are link-layer types. + */ + for (i = 1; raw_list[i] != NULL && *raw_list[i] != '\0'; i++) { + data_link_info_t *data_link_info; + /* ...and what if the interface name has a tab in it, Mr. Clever Programmer? */ + char **lt_parts = g_strsplit(raw_list[i], "\t", 3); + if (lt_parts[0] == NULL || lt_parts[1] == NULL || lt_parts[2] == NULL) { + g_strfreev(lt_parts); + continue; + } + + data_link_info = g_new(data_link_info_t,1); + data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10); + data_link_info->name = g_strdup(lt_parts[1]); + if (strcmp(lt_parts[2], "(not supported)") != 0) + data_link_info->description = g_strdup(lt_parts[2]); + else + data_link_info->description = NULL; + g_strfreev(lt_parts); + + linktype_list = g_list_append(linktype_list, data_link_info); + } + + if (raw_list[i]) { /* Oh, timestamp types! */ + for (i++; raw_list[i] != NULL && *raw_list[i] != '\0'; i++) { + timestamp_info_t *timestamp_info; + char **tt_parts = g_strsplit(raw_list[i], "\t", 2); + if (tt_parts[0] == NULL || tt_parts[1] == NULL) { + g_strfreev(tt_parts); + continue; + } + + timestamp_info = g_new(timestamp_info_t,1); + timestamp_info->name = g_strdup(tt_parts[0]); + timestamp_info->description = g_strdup(tt_parts[1]); + g_strfreev(tt_parts); + + timestamp_list = g_list_append(timestamp_list, timestamp_info); + } + } + + g_strfreev(raw_list); + + /* Check to see if we built a list */ + if (linktype_list == NULL) { + /* No. */ + if (err_primary_msg) + *err_primary_msg = g_strdup("Dumpcap returned no link-layer types"); + g_free(caps); + return NULL; + } + + caps->data_link_types = linktype_list; + /* Might be NULL. Not all systems report timestamp types */ + caps->timestamp_types = timestamp_list; + + return caps; +} + +#ifdef HAVE_PCAP_REMOTE +void add_interface_to_remote_list(if_info_t *if_info) +{ + GSList *list; + if_addr_t *if_addr, *temp_addr; + + if_info_t *temp = g_new0(if_info_t, 1); + temp->name = g_strdup(if_info->name); + temp->friendly_name = g_strdup(if_info->friendly_name); + temp->vendor_description = g_strdup(if_info->vendor_description); + for (list = g_slist_nth(if_info->addrs, 0); list != NULL; list = g_slist_next(list)) { + temp_addr = g_new0(if_addr_t, 1); + if_addr = (if_addr_t *)list->data; + if (if_addr) { + temp_addr->ifat_type = if_addr->ifat_type; + if (temp_addr->ifat_type == IF_AT_IPv4) { + temp_addr->addr.ip4_addr = if_addr->addr.ip4_addr; + } else { + memcpy(temp_addr->addr.ip6_addr, if_addr->addr.ip6_addr, sizeof(if_addr->addr)); + } + } else { + g_free(temp_addr); + temp_addr = NULL; + } + if (temp_addr) { + temp->addrs = g_slist_append(temp->addrs, temp_addr); + } + } + temp->loopback = if_info->loopback; + remote_interface_list = g_list_append(remote_interface_list, temp); +} +#endif +#endif /* HAVE_LIBPCAP */ diff --git a/capture/capture_ifinfo.h b/capture/capture_ifinfo.h new file mode 100644 index 0000000000..5034ddb720 --- /dev/null +++ b/capture/capture_ifinfo.h @@ -0,0 +1,138 @@ +/* capture_ifinfo.h + * Definitions for routines to get information about capture interfaces + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __CAPTURE_IFINFO_H__ +#define __CAPTURE_IFINFO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Explicitly set the interface_type enum values as these values are exposed + * in the preferences gui.interfaces_hidden_types string. + */ +typedef enum { + IF_WIRED = 0, + IF_AIRPCAP = 1, + IF_PIPE = 2, + IF_STDIN = 3, + IF_BLUETOOTH = 4, + IF_WIRELESS = 5, + IF_DIALUP = 6, + IF_USB = 7, + IF_EXTCAP = 8, + IF_VIRTUAL = 9 +} interface_type; + +/* + * The list of interfaces returned by "get_interface_list()" is + * a list of these structures. + */ +typedef struct { + char *name; /* e.g. "eth0" */ + char *friendly_name; /* from OS, e.g. "Local Area Connection", or + NULL if not available */ + char *vendor_description; + /* vendor description from pcap_findalldevs(), + e.g. "Realtek PCIe GBE Family Controller", + or NULL if not available */ + GSList *addrs; /* containing address values of if_addr_t */ + interface_type type; /* type of interface */ + gboolean loopback; /* TRUE if loopback, FALSE otherwise */ + char *extcap; /* extcap arguments, which present the data to call the extcap interface */ +} if_info_t; + +/* + * An address in the "addrs" list. + */ +typedef enum { + IF_AT_IPv4, + IF_AT_IPv6 +} if_address_type; + +typedef struct { + if_address_type ifat_type; + union { + guint32 ip4_addr; /* 4 byte IP V4 address, or */ + guint8 ip6_addr[16];/* 16 byte IP V6 address */ + } addr; +} if_addr_t; + +/** + * Fetch the interface list from a child process. + */ +extern GList *capture_interface_list(int *err, char **err_str, void (*update_cb)(void)); + +/* Error values from "get_interface_list()/capture_interface_list()". */ +#define CANT_GET_INTERFACE_LIST 1 /* error getting list */ +#define DONT_HAVE_PCAP 2 /* couldn't load WinPcap/Npcap */ + +void free_interface_list(GList *if_list); + +/** + * Get an if_info_t for a particular interface. + * (May require privilege, so should only be used by dumpcap.) + */ +extern if_info_t *if_info_get(const char *name); + +/** + * Free an if_info_t. + */ +void if_info_free(if_info_t *if_info); + +/* + * "get_if_capabilities()" and "capture_if_capabilities()" return a pointer + * to an allocated instance of this structure. "free_if_capabilities()" + * frees the returned instance. + */ +typedef struct { + gboolean can_set_rfmon; /* TRUE if can be put into monitor mode */ + GList *data_link_types; /* GList of data_link_info_t's */ + GList *timestamp_types; /* GList of timestamp_info_t's */ +} if_capabilities_t; + +/* + * Information about data link types. + */ +typedef struct { + int dlt; /* e.g. DLT_EN10MB (which is 1) */ + char *name; /* e.g. "EN10MB" or "DLT 1" */ + char *description; /* descriptive name from wiretap e.g. "Ethernet", NULL if unknown */ +} data_link_info_t; + +/* + * Information about timestamp types. + */ +typedef struct { + char *name; /* e.g. "adapter_unsynced" */ + char *description; /* description from libpcap e.g. "Adapter, not synced with system time" */ +} timestamp_info_t; + +/** + * Fetch the linktype list for the specified interface from a child process. + */ +extern if_capabilities_t * +capture_get_if_capabilities(const gchar *devname, gboolean monitor_mode, + const gchar *auth_string, + char **err_primary_msg, char **err_secondary_msg, + void (*update_cb)(void)); + +void free_if_capabilities(if_capabilities_t *caps); + +void add_interface_to_remote_list(if_info_t *if_info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPTURE_IFINFO_H__ */ diff --git a/capture/capture_session.h b/capture/capture_session.h new file mode 100644 index 0000000000..36773a5daf --- /dev/null +++ b/capture/capture_session.h @@ -0,0 +1,135 @@ +/* capture_session.h + * State of a capture session + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __CAPCHILD_CAPTURE_SESSION_H__ +#define __CAPCHILD_CAPTURE_SESSION_H__ + +#ifndef _WIN32 +#include +#include +#endif + +#include "capture_opts.h" + +#include + +#include "cfile.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef HAVE_LIBPCAP +/* Current state of capture engine. XXX - differentiate states */ +typedef enum { + CAPTURE_STOPPED, /**< stopped */ + CAPTURE_PREPARING, /**< preparing, but still no response from capture child */ + CAPTURE_RUNNING /**< capture child signalled ok, capture is running now */ +} capture_state; + +struct _info_data; + +/* + * State of a capture session. + */ +typedef struct _capture_session capture_session; + +/* + * Types of callbacks. + */ + +/** + * Capture child told us we have a new (or the first) capture file. + */ +typedef gboolean (*new_file_fn)(capture_session *cap_session, gchar *new_file); + +/** + * Capture child told us we have new packets to read. + */ +typedef void (*new_packets_fn)(capture_session *cap_session, int to_read); + +/** + * Capture child told us how many dropped packets it counted. + */ +typedef void (*drops_fn)(capture_session *cap_session, guint32 dropped, + const char *interface_name); + +/** + * Capture child told us that an error has occurred while starting + * the capture. + */ +typedef void (*error_fn)(capture_session *cap_session, char *error_msg, + char *secondary_error_msg); + +/** + * Capture child told us that an error has occurred while parsing a + * capture filter when starting/running the capture. + */ +typedef void (*cfilter_error_fn)(capture_session *cap_session, guint i, + const char *error_message); + +/** + * Capture child closed its side of the pipe, report any error and + * do the required cleanup. + */ +typedef void (*closed_fn)(capture_session *cap_session, gchar *msg); + +/* + * The structure for the session. + */ +struct _capture_session { + ws_process_id fork_child; /**< If not WS_INVALID_PID, in parent, process ID of child */ + int fork_child_status; /**< Child exit status */ +#ifdef _WIN32 + int signal_pipe_write_fd; /**< the pipe to signal the child */ +#endif + capture_state state; /**< current state of the capture engine */ +#ifndef _WIN32 + uid_t owner; /**< owner of the cfile */ + gid_t group; /**< group of the cfile */ +#endif + gboolean session_will_restart; /**< Set when session will restart */ + guint32 count; /**< Total number of frames captured */ + capture_options *capture_opts; /**< options for this capture */ + capture_file *cf; /**< handle to cfile */ + wtap_rec rec; /**< record we're reading packet metadata into */ + Buffer buf; /**< Buffer we're reading packet data into */ + struct wtap *wtap; /**< current wtap file */ + struct _info_data *cap_data_info; /**< stats for this capture */ + + /* + * Routines supplied by our caller; we call them back to notify them + * of various events. + */ + new_file_fn new_file; + new_packets_fn new_packets; + drops_fn drops; + error_fn error; + cfilter_error_fn cfilter_error; + closed_fn closed; +}; + +extern void +capture_session_init(capture_session *cap_session, capture_file *cf, + new_file_fn new_file, new_packets_fn new_packets, + drops_fn drops, error_fn error, + cfilter_error_fn cfilter_error, closed_fn closed); +#else + +/* dummy is needed because clang throws the error: empty struct has size 0 in C, size 1 in C++ */ +typedef struct _capture_session {int dummy;} capture_session; + +#endif /* HAVE_LIBPCAP */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPCHILD_CAPTURE_SESSION_H__ */ diff --git a/capture/capture_sync.c b/capture/capture_sync.c new file mode 100644 index 0000000000..afd9f47778 --- /dev/null +++ b/capture/capture_sync.c @@ -0,0 +1,2100 @@ +/* capture_sync.c + * Synchronisation between Wireshark capture parent and child instances + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#ifdef HAVE_LIBPCAP + +#include +#include +#include +#include + +#include + +#include + +#ifdef _WIN32 +#include +#include +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +# include +#endif + +#include "capture/capture-pcap-util.h" + +#ifndef _WIN32 +/* + * Define various POSIX macros (and, in the case of WCOREDUMP, non-POSIX + * macros) on UNIX systems that don't have them. + */ +#ifndef WIFEXITED +# define WIFEXITED(status) (((status) & 0177) == 0) +#endif +#ifndef WIFSTOPPED +# define WIFSTOPPED(status) (((status) & 0177) == 0177) +#endif +#ifndef WIFSIGNALED +# define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(status) ((status) >> 8) +#endif +#ifndef WTERMSIG +# define WTERMSIG(status) ((status) & 0177) +#endif +#ifndef WCOREDUMP +# define WCOREDUMP(status) ((status) & 0200) +#endif +#ifndef WSTOPSIG +# define WSTOPSIG(status) ((status) >> 8) +#endif +#endif /* _WIN32 */ + +#include +#include + +#include "file.h" + +#include "ui/capture.h" +#include + +#include "sync_pipe.h" + +#ifdef _WIN32 +#include "capture/capture-wpcap.h" +#endif + +#include "ui/ws_ui_util.h" + +#include +#include +#include +#include "extcap.h" +#include "log.h" + +#ifdef _WIN32 +#include /* For spawning child process */ +#endif + +#include + +#ifdef _WIN32 +static void create_dummy_signal_pipe(); +static HANDLE dummy_signal_pipe; /* Dummy named pipe which lets the child check for a dropped connection */ +static gchar *dummy_control_id; +#else +static const char *sync_pipe_signame(int); +#endif + + +static gboolean sync_pipe_input_cb(gint source, gpointer user_data); +static int sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp); +static void pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len); +static ssize_t pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, + char **err_msg); + +static void (*fetch_dumpcap_pid)(ws_process_id) = NULL; + +static void free_argv(char** argv, int argc) +{ + int i; + for (i = 0; i < argc; i++) + g_free(argv[i]); + g_free(argv); +} + +void +capture_session_init(capture_session *cap_session, capture_file *cf, + new_file_fn new_file, new_packets_fn new_packets, + drops_fn drops, error_fn error, + cfilter_error_fn cfilter_error, closed_fn closed) +{ + cap_session->cf = cf; + cap_session->fork_child = WS_INVALID_PID; /* invalid process handle */ +#ifdef _WIN32 + cap_session->signal_pipe_write_fd = -1; +#endif + cap_session->state = CAPTURE_STOPPED; +#ifndef _WIN32 + cap_session->owner = getuid(); + cap_session->group = getgid(); +#endif + cap_session->count = 0; + cap_session->session_will_restart = FALSE; + + cap_session->new_file = new_file; + cap_session->new_packets = new_packets; + cap_session->drops = drops; + cap_session->error = error; + cap_session->cfilter_error = cfilter_error; + cap_session->closed = closed; +} + +/* Append an arg (realloc) to an argc/argv array */ +/* (add a string pointer to a NULL-terminated array of string pointers) */ +static char ** +sync_pipe_add_arg(char **args, int *argc, const char *arg) +{ + /* Grow the array; "*argc" currently contains the number of string + pointers, *not* counting the NULL pointer at the end, so we have + to add 2 in order to get the new size of the array, including the + new pointer and the terminating NULL pointer. */ + args = (char **)g_realloc( (gpointer) args, (*argc + 2) * sizeof (char *)); + + /* Stuff the pointer into the penultimate element of the array, which + is the one at the index specified by "*argc". */ + args[*argc] = g_strdup(arg); + /* Now bump the count. */ + (*argc)++; + + /* We overwrite the NULL pointer; put it back right after the + element we added. */ + args[*argc] = NULL; + + return args; +} + +/* Initialize an argument list and add dumpcap to it. */ +static char ** +init_pipe_args(int *argc) { + char **argv; + const char *progfile_dir; + char *exename; + + progfile_dir = get_progfile_dir(); + if (progfile_dir == NULL) { + return NULL; + } + + /* Allocate the string pointer array with enough space for the + terminating NULL pointer. */ + *argc = 0; + argv = (char **)g_malloc(sizeof (char *)); + *argv = NULL; + + /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */ +#ifdef _WIN32 + exename = g_strdup_printf("%s\\dumpcap.exe", progfile_dir); +#else + exename = g_strdup_printf("%s/dumpcap", progfile_dir); +#endif + + /* Make that the first argument in the argument list (argv[0]). */ + argv = sync_pipe_add_arg(argv, argc, exename); + + /* sync_pipe_add_arg strdupes exename, so we should free our copy */ + g_free(exename); + + return argv; +} + +#define ARGV_NUMBER_LEN 24 +/* a new capture run: start a new dumpcap task and hand over parameters through command line */ +gboolean +sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, info_data_t* cap_data, void (*update_cb)(void)) +{ +#ifdef _WIN32 + HANDLE sync_pipe_read; /* pipe used to send messages from child to parent */ + HANDLE sync_pipe_write; /* pipe used to send messages from child to parent */ + int signal_pipe_write_fd; + HANDLE signal_pipe; /* named pipe used to send messages from parent to child (currently only stop) */ + GString *args = g_string_sized_new(200); + gchar *quoted_arg; + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; + PROCESS_INFORMATION pi; + char control_id[ARGV_NUMBER_LEN]; + gchar *signal_pipe_name; +#else + char errmsg[1024+1]; + int sync_pipe[2]; /* pipe used to send messages from child to parent */ + enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ +#endif + int sync_pipe_read_fd; + int argc; + char **argv; + int i; + guint j; + interface_options *interface_opts; + + if (capture_opts->ifaces->len > 1) + capture_opts->use_pcapng = TRUE; + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_start"); + capture_opts_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, capture_opts); + + cap_session->fork_child = WS_INVALID_PID; + + if (!extcap_init_interfaces(capture_opts)) { + report_failure("Unable to init extcaps. (tmp fifo already exists?)"); + return FALSE; + } + + argv = init_pipe_args(&argc); + if (!argv) { + /* We don't know where to find dumpcap. */ + report_failure("We don't know where to find dumpcap."); + return FALSE; + } + + if (capture_opts->ifaces->len > 1) + argv = sync_pipe_add_arg(argv, &argc, "-t"); + + if (capture_opts->use_pcapng) + argv = sync_pipe_add_arg(argv, &argc, "-n"); + else + argv = sync_pipe_add_arg(argv, &argc, "-P"); + + if (capture_opts->capture_comment) { + argv = sync_pipe_add_arg(argv, &argc, "--capture-comment"); + argv = sync_pipe_add_arg(argv, &argc, capture_opts->capture_comment); + } + + if (capture_opts->multi_files_on) { + if (capture_opts->has_autostop_filesize) { + char sfilesize[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_snprintf(sfilesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); + argv = sync_pipe_add_arg(argv, &argc, sfilesize); + } + + if (capture_opts->has_file_duration) { + char sfile_duration[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_snprintf(sfile_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->file_duration); + argv = sync_pipe_add_arg(argv, &argc, sfile_duration); + } + + if (capture_opts->has_file_interval) { + char sfile_interval[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_snprintf(sfile_interval, ARGV_NUMBER_LEN, "interval:%d",capture_opts->file_interval); + argv = sync_pipe_add_arg(argv, &argc, sfile_interval); + } + + if (capture_opts->has_file_packets) { + char sfile_packets[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_snprintf(sfile_packets, ARGV_NUMBER_LEN, "packets:%d",capture_opts->file_packets); + argv = sync_pipe_add_arg(argv, &argc, sfile_packets); + } + + if (capture_opts->has_ring_num_files) { + char sring_num_files[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_snprintf(sring_num_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->ring_num_files); + argv = sync_pipe_add_arg(argv, &argc, sring_num_files); + } + + if (capture_opts->has_autostop_files) { + char sautostop_files[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-a"); + g_snprintf(sautostop_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->autostop_files); + argv = sync_pipe_add_arg(argv, &argc, sautostop_files); + } + } else { + if (capture_opts->has_autostop_filesize) { + char sautostop_filesize[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-a"); + g_snprintf(sautostop_filesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); + argv = sync_pipe_add_arg(argv, &argc, sautostop_filesize); + } + } + + if (capture_opts->has_autostop_packets) { + char scount[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-c"); + g_snprintf(scount, ARGV_NUMBER_LEN, "%d",capture_opts->autostop_packets); + argv = sync_pipe_add_arg(argv, &argc, scount); + } + + if (capture_opts->has_autostop_duration) { + char sautostop_duration[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-a"); + g_snprintf(sautostop_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->autostop_duration); + argv = sync_pipe_add_arg(argv, &argc, sautostop_duration); + } + + if (capture_opts->group_read_access) { + argv = sync_pipe_add_arg(argv, &argc, "-g"); + } + + for (j = 0; j < capture_opts->ifaces->len; j++) { + interface_opts = &g_array_index(capture_opts->ifaces, interface_options, j); + + argv = sync_pipe_add_arg(argv, &argc, "-i"); + if (interface_opts->extcap_fifo != NULL) + { +#ifdef _WIN32 + char *pipe = g_strdup_printf("%s%" G_GUINTPTR_FORMAT, EXTCAP_PIPE_PREFIX, interface_opts->extcap_pipe_h); + argv = sync_pipe_add_arg(argv, &argc, pipe); + g_free(pipe); +#else + argv = sync_pipe_add_arg(argv, &argc, interface_opts->extcap_fifo); +#endif + /* Add a name for the interface, to put into an IDB. */ + argv = sync_pipe_add_arg(argv, &argc, "--ifname"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts->name); + if (interface_opts->descr != NULL) + { + /* Add a description for the interface, to put into an IDB. */ + argv = sync_pipe_add_arg(argv, &argc, "--ifdescr"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts->descr); + } + } + else + argv = sync_pipe_add_arg(argv, &argc, interface_opts->name); + + if (interface_opts->cfilter != NULL && strlen(interface_opts->cfilter) != 0) { + argv = sync_pipe_add_arg(argv, &argc, "-f"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts->cfilter); + } + if (interface_opts->has_snaplen) { + char ssnap[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-s"); + g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d", interface_opts->snaplen); + argv = sync_pipe_add_arg(argv, &argc, ssnap); + } + + if (interface_opts->linktype != -1) { + const char *linktype = linktype_val_to_name(interface_opts->linktype); + if ( linktype != NULL ) + { + argv = sync_pipe_add_arg(argv, &argc, "-y"); + argv = sync_pipe_add_arg(argv, &argc, linktype); + } + } + + if (!interface_opts->promisc_mode) { + argv = sync_pipe_add_arg(argv, &argc, "-p"); + } + +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + if (interface_opts->buffer_size != DEFAULT_CAPTURE_BUFFER_SIZE) { + char buffer_size[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-B"); + if(interface_opts->buffer_size == 0x00) + interface_opts->buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE; + g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts->buffer_size); + argv = sync_pipe_add_arg(argv, &argc, buffer_size); + } +#endif + +#ifdef HAVE_PCAP_CREATE + if (interface_opts->monitor_mode) { + argv = sync_pipe_add_arg(argv, &argc, "-I"); + } +#endif + +#ifdef HAVE_PCAP_REMOTE + if (interface_opts->datatx_udp) + argv = sync_pipe_add_arg(argv, &argc, "-u"); + + if (!interface_opts->nocap_rpcap) + argv = sync_pipe_add_arg(argv, &argc, "-r"); + + if (interface_opts->auth_type == CAPTURE_AUTH_PWD) { + char sauth[256]; + argv = sync_pipe_add_arg(argv, &argc, "-A"); + g_snprintf(sauth, sizeof(sauth), "%s:%s", + interface_opts->auth_username, + interface_opts->auth_password); + argv = sync_pipe_add_arg(argv, &argc, sauth); + } +#endif + +#ifdef HAVE_PCAP_SETSAMPLING + if (interface_opts->sampling_method != CAPTURE_SAMP_NONE) { + char ssampling[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-m"); + g_snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d", + interface_opts->sampling_method == CAPTURE_SAMP_BY_COUNT ? "count" : + interface_opts->sampling_method == CAPTURE_SAMP_BY_TIMER ? "timer" : + "undef", + interface_opts->sampling_param); + argv = sync_pipe_add_arg(argv, &argc, ssampling); + } +#endif + if (interface_opts->timestamp_type) { + argv = sync_pipe_add_arg(argv, &argc, "--time-stamp-type"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts->timestamp_type); + } + } + + /* dumpcap should be running in capture child mode (hidden feature) */ +#ifndef DEBUG_CHILD + argv = sync_pipe_add_arg(argv, &argc, "-Z"); +#ifdef _WIN32 + g_snprintf(control_id, ARGV_NUMBER_LEN, "%d", GetCurrentProcessId()); + argv = sync_pipe_add_arg(argv, &argc, control_id); +#else + argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); +#endif +#endif + + if (capture_opts->save_file) { + argv = sync_pipe_add_arg(argv, &argc, "-w"); + argv = sync_pipe_add_arg(argv, &argc, capture_opts->save_file); + } + for (i = 0; i < argc; i++) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[%d]: %s", i, argv[i]); + } + if (capture_opts->compress_type) { + argv = sync_pipe_add_arg(argv, &argc, "--compress-type"); + argv = sync_pipe_add_arg(argv, &argc, capture_opts->compress_type); + } + +#ifdef _WIN32 + /* init SECURITY_ATTRIBUTES */ + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + /* Create a pipe for the child process */ + /* (increase this value if you have trouble while fast capture file switches) */ + if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) { + /* Couldn't create the pipe between parent and child. */ + report_failure("Couldn't create sync pipe: %s", + win32strerror(GetLastError())); + free_argv(argv, argc); + return FALSE; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the message pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + sync_pipe_read_fd = _open_osfhandle( (intptr_t) sync_pipe_read, _O_BINARY); + if (sync_pipe_read_fd == -1) { + /* Couldn't create the pipe between parent and child. */ + report_failure("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); + CloseHandle(sync_pipe_read); + CloseHandle(sync_pipe_write); + free_argv(argv, argc); + return FALSE; + } + + /* Create the signal pipe */ + signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, control_id); + signal_pipe = CreateNamedPipe(utf_8to16(signal_pipe_name), + PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL); + g_free(signal_pipe_name); + + if (signal_pipe == INVALID_HANDLE_VALUE) { + /* Couldn't create the signal pipe between parent and child. */ + report_failure("Couldn't create signal pipe: %s", + win32strerror(GetLastError())); + ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ + CloseHandle(sync_pipe_write); + free_argv(argv, argc); + return FALSE; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the message pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + signal_pipe_write_fd = _open_osfhandle( (intptr_t) signal_pipe, _O_BINARY); + if (sync_pipe_read_fd == -1) { + /* Couldn't create the pipe between parent and child. */ + report_failure("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); + ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ + CloseHandle(sync_pipe_write); + CloseHandle(signal_pipe); + free_argv(argv, argc); + return FALSE; + } + + /* init STARTUPINFO & PROCESS_INFORMATION */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); +#ifdef DEBUG_CHILD + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; +#else + si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; /* this hides the console window */ + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = sync_pipe_write; + /*si.hStdError = (HANDLE) _get_osfhandle(2);*/ +#endif + + /* convert args array into a single string */ + /* XXX - could change sync_pipe_add_arg() instead */ + /* there is a drawback here: the length is internally limited to 1024 bytes */ + for(i=0; argv[i] != 0; i++) { + if(i != 0) g_string_append_c(args, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(argv[i]); + g_string_append(args, quoted_arg); + g_free(quoted_arg); + } + + /* call dumpcap */ + if(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE, + CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { + report_failure("Couldn't run %s in child process: %s", + args->str, win32strerror(GetLastError())); + ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ + CloseHandle(sync_pipe_write); + CloseHandle(signal_pipe); + free_argv(argv, argc); + g_string_free(args, TRUE); + return FALSE; + } + cap_session->fork_child = pi.hProcess; + /* We may need to store this and close it later */ + CloseHandle(pi.hThread); + g_string_free(args, TRUE); + + cap_session->signal_pipe_write_fd = signal_pipe_write_fd; + +#else /* _WIN32 */ + if (pipe(sync_pipe) < 0) { + /* Couldn't create the pipe between parent and child. */ + report_failure("Couldn't create sync pipe: %s", g_strerror(errno)); + free_argv(argv, argc); + return FALSE; + } + + if ((cap_session->fork_child = fork()) == 0) { + /* + * Child process - run dumpcap with the right arguments to make + * it just capture with the specified capture parameters + */ + dup2(sync_pipe[PIPE_WRITE], 2); + ws_close(sync_pipe[PIPE_READ]); + execv(argv[0], argv); + g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s", + argv[0], g_strerror(errno)); + sync_pipe_errmsg_to_parent(2, errmsg, ""); + + /* Exit with "_exit()", so that we don't close the connection + to the X server (and cause stuff buffered up by our parent but + not yet sent to be sent, as that stuff should only be sent by + our parent). We've sent an error message to the parent, so + we exit with an exit status of 1 (any exit status other than + 0 or 1 will cause an additional message to report that exit + status, over and above the error message we sent to the parent). */ + _exit(1); + } + + if (fetch_dumpcap_pid && cap_session->fork_child > 0) + fetch_dumpcap_pid(cap_session->fork_child); + + sync_pipe_read_fd = sync_pipe[PIPE_READ]; +#endif + + /* Parent process - read messages from the child process over the + sync pipe. */ + free_argv(argv, argc); + + /* Close the write side of the pipe, so that only the child has it + open, and thus it completely closes, and thus returns to us + an EOF indication, if the child closes it (either deliberately + or by exiting abnormally). */ +#ifdef _WIN32 + CloseHandle(sync_pipe_write); +#else + ws_close(sync_pipe[PIPE_WRITE]); +#endif + + if (cap_session->fork_child == WS_INVALID_PID) { + /* We couldn't even create the child process. */ + report_failure("Couldn't create child process: %s", g_strerror(errno)); + ws_close(sync_pipe_read_fd); +#ifdef _WIN32 + ws_close(cap_session->signal_pipe_write_fd); +#endif + return FALSE; + } + + cap_session->fork_child_status = 0; + cap_session->capture_opts = capture_opts; + cap_session->cap_data_info = cap_data; + + /* we might wait for a moment till child is ready, so update screen now */ + if (update_cb) update_cb(); + + /* We were able to set up to read the capture file; + arrange that our callback be called whenever it's possible + to read from the sync pipe, so that it's called when + the child process wants to tell us something. */ + + /* we have a running capture, now wait for the real capture filename */ + pipe_input_set_handler(sync_pipe_read_fd, (gpointer) cap_session, + &cap_session->fork_child, sync_pipe_input_cb); + + return TRUE; +} + +/* + * Open two pipes to dumpcap with the supplied arguments, one for its + * standard output and one for its standard error. + * + * On success, *msg is unchanged and 0 is returned; data_read_fd, + * message_read_fd, and fork_child point to the standard output pipe's + * file descriptor, the standard error pipe's file descriptor, and + * the child's PID/handle, respectively. + * + * On failure, *msg points to an error message for the failure, and -1 is + * returned, in which case *msg must be freed with g_free(). + */ +/* XXX - This duplicates a lot of code in sync_pipe_start() */ +/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ +#define PIPE_BUF_SIZE 5120 +static int +sync_pipe_open_command(char* const argv[], int *data_read_fd, + int *message_read_fd, ws_process_id *fork_child, gchar **msg, void(*update_cb)(void)) +{ + enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ +#ifdef _WIN32 + HANDLE sync_pipe[2]; /* pipe used to send messages from child to parent */ + HANDLE data_pipe[2]; /* pipe used to send data from child to parent */ + GString *args = g_string_sized_new(200); + gchar *quoted_arg; + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; + PROCESS_INFORMATION pi; + int i; +#else + char errmsg[1024+1]; + int sync_pipe[2]; /* pipe used to send messages from child to parent */ + int data_pipe[2]; /* pipe used to send data from child to parent */ +#endif + *fork_child = WS_INVALID_PID; + *data_read_fd = -1; + *message_read_fd = -1; + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_open_command"); + + if (!msg) { + /* We can't return anything */ +#ifdef _WIN32 + g_string_free(args, TRUE); +#endif + return -1; + } + +#ifdef _WIN32 + /* init SECURITY_ATTRIBUTES */ + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + /* Create a pipe for the child process to send us messages */ + /* (increase this value if you have trouble while fast capture file switches) */ + if (! CreatePipe(&sync_pipe[PIPE_READ], &sync_pipe[PIPE_WRITE], &sa, 5120)) { + /* Couldn't create the message pipe between parent and child. */ + *msg = g_strdup_printf("Couldn't create sync pipe: %s", + win32strerror(GetLastError())); + return -1; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the message pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + *message_read_fd = _open_osfhandle( (intptr_t) sync_pipe[PIPE_READ], _O_BINARY); + if (*message_read_fd == -1) { + *msg = g_strdup_printf("Couldn't get C file handle for message read pipe: %s", g_strerror(errno)); + CloseHandle(sync_pipe[PIPE_READ]); + CloseHandle(sync_pipe[PIPE_WRITE]); + return -1; + } + + /* Create a pipe for the child process to send us data */ + /* (increase this value if you have trouble while fast capture file switches) */ + if (! CreatePipe(&data_pipe[PIPE_READ], &data_pipe[PIPE_WRITE], &sa, 5120)) { + /* Couldn't create the message pipe between parent and child. */ + *msg = g_strdup_printf("Couldn't create data pipe: %s", + win32strerror(GetLastError())); + ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ + CloseHandle(sync_pipe[PIPE_WRITE]); + return -1; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the data pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + *data_read_fd = _open_osfhandle( (intptr_t) data_pipe[PIPE_READ], _O_BINARY); + if (*data_read_fd == -1) { + *msg = g_strdup_printf("Couldn't get C file handle for data read pipe: %s", g_strerror(errno)); + CloseHandle(data_pipe[PIPE_READ]); + CloseHandle(data_pipe[PIPE_WRITE]); + ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ + CloseHandle(sync_pipe[PIPE_WRITE]); + return -1; + } + + /* init STARTUPINFO & PROCESS_INFORMATION */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); +#ifdef DEBUG_CHILD + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; +#else + si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; /* this hides the console window */ + si.hStdInput = NULL; /* handle for named pipe*/ + + si.hStdOutput = data_pipe[PIPE_WRITE]; + si.hStdError = sync_pipe[PIPE_WRITE]; +#endif + + /* convert args array into a single string */ + /* XXX - could change sync_pipe_add_arg() instead */ + /* there is a drawback here: the length is internally limited to 1024 bytes */ + for(i=0; argv[i] != 0; i++) { + if(i != 0) g_string_append_c(args, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(argv[i]); + g_string_append(args, quoted_arg); + g_free(quoted_arg); + } + + /* call dumpcap */ + if(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE, + CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { + *msg = g_strdup_printf("Couldn't run %s in child process: %s", + args->str, win32strerror(GetLastError())); + ws_close(*data_read_fd); /* Should close data_pipe[PIPE_READ] */ + CloseHandle(data_pipe[PIPE_WRITE]); + ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ + CloseHandle(sync_pipe[PIPE_WRITE]); + g_string_free(args, TRUE); + return -1; + } + *fork_child = pi.hProcess; + /* We may need to store this and close it later */ + CloseHandle(pi.hThread); + g_string_free(args, TRUE); +#else /* _WIN32 */ + /* Create a pipe for the child process to send us messages */ + if (pipe(sync_pipe) < 0) { + /* Couldn't create the message pipe between parent and child. */ + *msg = g_strdup_printf("Couldn't create sync pipe: %s", g_strerror(errno)); + return -1; + } + + /* Create a pipe for the child process to send us data */ + if (pipe(data_pipe) < 0) { + /* Couldn't create the data pipe between parent and child. */ + *msg = g_strdup_printf("Couldn't create data pipe: %s", g_strerror(errno)); + ws_close(sync_pipe[PIPE_READ]); + ws_close(sync_pipe[PIPE_WRITE]); + return -1; + } + + if ((*fork_child = fork()) == 0) { + /* + * Child process - run dumpcap with the right arguments to make + * it just capture with the specified capture parameters + */ + dup2(data_pipe[PIPE_WRITE], 1); + ws_close(data_pipe[PIPE_READ]); + ws_close(data_pipe[PIPE_WRITE]); + dup2(sync_pipe[PIPE_WRITE], 2); + ws_close(sync_pipe[PIPE_READ]); + ws_close(sync_pipe[PIPE_WRITE]); + execv(argv[0], argv); + g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s", + argv[0], g_strerror(errno)); + sync_pipe_errmsg_to_parent(2, errmsg, ""); + + /* Exit with "_exit()", so that we don't close the connection + to the X server (and cause stuff buffered up by our parent but + not yet sent to be sent, as that stuff should only be sent by + our parent). We've sent an error message to the parent, so + we exit with an exit status of 1 (any exit status other than + 0 or 1 will cause an additional message to report that exit + status, over and above the error message we sent to the parent). */ + _exit(1); + } + + if (fetch_dumpcap_pid && *fork_child > 0) + fetch_dumpcap_pid(*fork_child); + + *data_read_fd = data_pipe[PIPE_READ]; + *message_read_fd = sync_pipe[PIPE_READ]; +#endif + + /* Parent process - read messages from the child process over the + sync pipe. */ + + /* Close the write sides of the pipes, so that only the child has them + open, and thus they completely close, and thus return to us + an EOF indication, if the child closes them (either deliberately + or by exiting abnormally). */ +#ifdef _WIN32 + CloseHandle(data_pipe[PIPE_WRITE]); + CloseHandle(sync_pipe[PIPE_WRITE]); +#else + ws_close(data_pipe[PIPE_WRITE]); + ws_close(sync_pipe[PIPE_WRITE]); +#endif + + if (*fork_child == WS_INVALID_PID) { + /* We couldn't even create the child process. */ + *msg = g_strdup_printf("Couldn't create child process: %s", g_strerror(errno)); + ws_close(*data_read_fd); + ws_close(*message_read_fd); + return -1; + } + + /* we might wait for a moment till child is ready, so update screen now */ + if (update_cb) update_cb(); + return 0; +} + +/* + * Close the pipes we're using to read from dumpcap, and wait for it + * to exit. On success, *msgp is unchanged, and the exit status of + * dumpcap is returned. On failure (which includes "dumpcap exited + * due to being killed by a signal or an exception"), *msgp points + * to an error message for the failure, and -1 is returned. In the + * latter case, *msgp must be freed with g_free(). + */ +static int +sync_pipe_close_command(int *data_read_fd, int *message_read_fd, + ws_process_id *fork_child, gchar **msgp) +{ + ws_close(*data_read_fd); + if (message_read_fd != NULL) + ws_close(*message_read_fd); + +#ifdef _WIN32 + /* XXX - Should we signal the child somehow? */ + sync_pipe_kill(*fork_child); +#endif + + return sync_pipe_wait_for_child(*fork_child, msgp); +} + +/* + * Run dumpcap with the supplied arguments. + * + * On success, *data points to a buffer containing the dumpcap output, + * *primary_msg and *secondary_message are NULL, and 0 is returned; *data + * must be freed with g_free(). + * + * On failure, *data is NULL, *primary_msg points to an error message, + * *secondary_msg either points to an additional error message or is + * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL, + * must be freed with g_free(). + */ +/* XXX - This duplicates a lot of code in sync_pipe_start() */ +/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ +#define PIPE_BUF_SIZE 5120 +static int +sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_msg, + gchar **secondary_msg, void(*update_cb)(void)) +{ + gchar *msg; + int data_pipe_read_fd, sync_pipe_read_fd, ret; + ws_process_id fork_child; + char *wait_msg; + gchar buffer[PIPE_BUF_SIZE+1] = {0}; + ssize_t nread; + char indicator; + int primary_msg_len; + char *primary_msg_text; + int secondary_msg_len; + char *secondary_msg_text; + char *combined_msg; + GString *data_buf = NULL; + ssize_t count; + + ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd, + &fork_child, &msg, update_cb); + if (ret == -1) { + *primary_msg = msg; + *secondary_msg = NULL; + *data = NULL; + return -1; + } + + /* + * We were able to set up to read dumpcap's output. Do so. + * + * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. + */ + nread = pipe_read_block(sync_pipe_read_fd, &indicator, SP_MAX_MSG_LEN, + buffer, primary_msg); + if(nread <= 0) { + /* We got a read error from the sync pipe, or we got no data at + all from the sync pipe, so we're not going to be getting any + data or error message from the child process. Pick up its + exit status, and complain. + + We don't have to worry about killing the child, if the sync pipe + returned an error. Usually this error is caused as the child killed + itself while going down. Even in the rare cases that this isn't the + case, the child will get an error when writing to the broken pipe + the next time, cleaning itself up then. */ + ret = sync_pipe_wait_for_child(fork_child, &wait_msg); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that it exited + before giving us any data to read. If ret is -1, we report + that as a bad exit (e.g., exiting due to a signal); otherwise, + we report it as a premature exit. */ + if (ret == -1) + *primary_msg = wait_msg; + else + *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely"); + } else { + /* We got an error from the sync pipe. If ret is -1, report + both the sync pipe I/O error and the wait error. */ + if (ret == -1) { + combined_msg = g_strdup_printf("%s\n\n%s", *primary_msg, wait_msg); + g_free(*primary_msg); + g_free(wait_msg); + *primary_msg = combined_msg; + } + } + *secondary_msg = NULL; + *data = NULL; + + return -1; + } + + /* we got a valid message block from the child, process it */ + switch(indicator) { + + case SP_ERROR_MSG: + /* + * Error from dumpcap; there will be a primary message and a + * secondary message. + */ + + /* convert primary message */ + pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((guchar*)primary_msg_text + primary_msg_len, 4, &indicator, + &secondary_msg_len); + secondary_msg_text = primary_msg_text + primary_msg_len + 4; + /* the capture child will close the sync_pipe, nothing to do */ + + /* + * Pick up the child status. + */ + ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + } else { + /* + * Child process failed, but returned the expected exit status. + * Return the messages it gave us, and indicate failure. + */ + *primary_msg = g_strdup(primary_msg_text); + *secondary_msg = g_strdup(secondary_msg_text); + ret = -1; + } + *data = NULL; + break; + + case SP_SUCCESS: + /* read the output from the command */ + data_buf = g_string_new(""); + while ((count = ws_read(data_pipe_read_fd, buffer, PIPE_BUF_SIZE)) > 0) { + buffer[count] = '\0'; + g_string_append(data_buf, buffer); + } + + /* + * Pick up the child status. + */ + ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + g_string_free(data_buf, TRUE); + *data = NULL; + } else { + /* + * Child process succeeded. + */ + *primary_msg = NULL; + *secondary_msg = NULL; + *data = g_string_free(data_buf, FALSE); + } + break; + + default: + /* + * Pick up the child status. + */ + ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + } else { + /* + * Child process returned an unknown status. + */ + *primary_msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + *secondary_msg = NULL; + ret = -1; + } + *data = NULL; + break; + } + return ret; +} + +/* centralised logging and timing for sync_pipe_run_command_actual(), +* redirects to sync_pipe_run_command_actual() +*/ +static int +sync_pipe_run_command(char* const argv[], gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)) +{ + int ret, i; + gint64 start_time; + double elapsed; + int logging_enabled; + + /* check if logging is actually enabled, otherwise don't expend the CPU generating logging */ + logging_enabled=( (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO) & G_LOG_LEVEL_MASK & prefs.console_log_level); + if(logging_enabled){ + start_time = g_get_monotonic_time(); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "sync_pipe_run_command() starts"); + for(i=0; argv[i] != 0; i++) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " argv[%d]: %s", i, argv[i]); + } + } + /* do the actual sync pipe run command */ + ret=sync_pipe_run_command_actual(argv, data, primary_msg, secondary_msg, update_cb); + + if(logging_enabled){ + elapsed = (g_get_monotonic_time() - start_time) / 1e6; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "sync_pipe_run_command() ends, taking %.3fs, result=%d", elapsed, ret); + + } + return ret; +} + + +int +sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type, + const gchar *center_freq1, const gchar *center_freq2, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)) +{ + int argc, ret; + char **argv; + gchar *opt; + + argv = init_pipe_args(&argc); + + if (!argv) { + *primary_msg = g_strdup("We don't know where to find dumpcap."); + *secondary_msg = NULL; + *data = NULL; + return -1; + } + + argv = sync_pipe_add_arg(argv, &argc, "-i"); + argv = sync_pipe_add_arg(argv, &argc, iface); + + if (center_freq2) + opt = g_strdup_printf("%s,%s,%s,%s", freq, type, center_freq1, center_freq2); + else if (center_freq1) + opt = g_strdup_printf("%s,%s,%s", freq, type, center_freq1); + else if (type) + opt = g_strdup_printf("%s,%s", freq, type); + else + opt = g_strdup(freq); + + if (!opt) { + *primary_msg = g_strdup("Out of mem."); + *secondary_msg = NULL; + *data = NULL; + free_argv(argv, argc); + return -1; + } + + argv = sync_pipe_add_arg(argv, &argc, "-k"); + argv = sync_pipe_add_arg(argv, &argc, opt); + +#ifndef DEBUG_CHILD + /* Run dumpcap in capture child mode */ + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); +#endif + + ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); + g_free(opt); + free_argv(argv, argc); + return ret; +} + +/* + * Get the list of interfaces using dumpcap. + * + * On success, *data points to a buffer containing the dumpcap output, + * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data + * must be freed with g_free(). + * + * On failure, *data is NULL, *primary_msg points to an error message, + * *secondary_msg either points to an additional error message or is + * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL, + * must be freed with g_free(). + */ +int +sync_interface_list_open(gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)) +{ + int argc; + char **argv; + int ret; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open"); + + argv = init_pipe_args(&argc); + + if (!argv) { + *primary_msg = g_strdup("We don't know where to find dumpcap.."); + *secondary_msg = NULL; + *data = NULL; + return -1; + } + + /* Ask for the interface list */ + argv = sync_pipe_add_arg(argv, &argc, "-D"); + +#ifndef DEBUG_CHILD + /* Run dumpcap in capture child mode */ + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); +#endif + ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); + free_argv(argv, argc); + return ret; +} + +/* + * Get the capabilities of an interface using dumpcap. + * + * On success, *data points to a buffer containing the dumpcap output, + * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data + * must be freed with g_free(). + * + * On failure, *data is NULL, *primary_msg points to an error message, + * *secondary_msg either points to an additional error message or is + * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL, + * must be freed with g_free(). + */ +int +sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, const gchar* auth, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)) +{ + int argc; + char **argv; + int ret; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_if_capabilities_open"); + + argv = init_pipe_args(&argc); + + if (!argv) { + *primary_msg = g_strdup("We don't know where to find dumpcap."); + *secondary_msg = NULL; + *data = NULL; + return -1; + } + + /* Ask for the interface capabilities */ + argv = sync_pipe_add_arg(argv, &argc, "-i"); + argv = sync_pipe_add_arg(argv, &argc, ifname); + argv = sync_pipe_add_arg(argv, &argc, "-L"); + argv = sync_pipe_add_arg(argv, &argc, "--list-time-stamp-types"); + if (monitor_mode) + argv = sync_pipe_add_arg(argv, &argc, "-I"); + if (auth) { + argv = sync_pipe_add_arg(argv, &argc, "-A"); + argv = sync_pipe_add_arg(argv, &argc, auth); + } + +#ifndef DEBUG_CHILD + /* Run dumpcap in capture child mode */ + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); +#endif + ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); + free_argv(argv, argc); + return ret; +} + +/* + * Start getting interface statistics using dumpcap. On success, read_fd + * contains the file descriptor for the pipe's stdout, *msg is unchanged, + * and zero is returned. On failure, *msg will point to an error message + * that must be g_free()d, and -1 will be returned. + */ +int +sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, gchar **msg, void (*update_cb)(void)) +{ + int argc; + char **argv; + int message_read_fd, ret; + char *wait_msg; + gchar buffer[PIPE_BUF_SIZE+1] = {0}; + ssize_t nread; + char indicator; + int primary_msg_len; + char *primary_msg_text; + int secondary_msg_len; + /*char *secondary_msg_text;*/ + char *combined_msg; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_stats_open"); + + argv = init_pipe_args(&argc); + + if (!argv) { + *msg = g_strdup("We don't know where to find dumpcap."); + return -1; + } + + /* Ask for the interface statistics */ + argv = sync_pipe_add_arg(argv, &argc, "-S"); + +#ifndef DEBUG_CHILD + argv = sync_pipe_add_arg(argv, &argc, "-Z"); +#ifdef _WIN32 + create_dummy_signal_pipe(); + argv = sync_pipe_add_arg(argv, &argc, dummy_control_id); +#else + argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); +#endif +#endif + ret = sync_pipe_open_command(argv, data_read_fd, &message_read_fd, + fork_child, msg, update_cb); + free_argv(argv, argc); + if (ret == -1) { + return -1; + } + + /* + * We were able to set up to read dumpcap's output. Do so. + * + * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. + */ + nread = pipe_read_block(message_read_fd, &indicator, SP_MAX_MSG_LEN, + buffer, msg); + if(nread <= 0) { + /* We got a read error from the sync pipe, or we got no data at + all from the sync pipe, so we're not going to be getting any + data or error message from the child process. Pick up its + exit status, and complain. + + We don't have to worry about killing the child, if the sync pipe + returned an error. Usually this error is caused as the child killed + itself while going down. Even in the rare cases that this isn't the + case, the child will get an error when writing to the broken pipe + the next time, cleaning itself up then. */ + ret = sync_pipe_wait_for_child(*fork_child, &wait_msg); + ws_close(message_read_fd); + ws_close(*data_read_fd); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that it exited + before giving us any data to read. If ret is -1, we report + that as a bad exit (e.g., exiting due to a signal); otherwise, + we report it as a premature exit. */ + if (ret == -1) + *msg = wait_msg; + else + *msg = g_strdup("Child dumpcap closed sync pipe prematurely"); + } else { + /* We got an error from the sync pipe. If ret is -1, report + both the sync pipe I/O error and the wait error. */ + if (ret == -1) { + combined_msg = g_strdup_printf("%s\n\n%s", *msg, wait_msg); + g_free(*msg); + g_free(wait_msg); + *msg = combined_msg; + } + } + return -1; + } + + /* we got a valid message block from the child, process it */ + switch(indicator) { + + case SP_ERROR_MSG: + /* + * Error from dumpcap; there will be a primary message and a + * secondary message. + */ + + /* convert primary message */ + pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((guchar*)primary_msg_text + primary_msg_len, 4, &indicator, + &secondary_msg_len); + /*secondary_msg_text = primary_msg_text + primary_msg_len + 4;*/ + /* the capture child will close the sync_pipe, nothing to do */ + + /* + * Pick up the child status. + */ + ret = sync_pipe_close_command(data_read_fd, &message_read_fd, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else { + /* + * Child process failed, but returned the expected exit status. + * Return the messages it gave us, and indicate failure. + */ + *msg = g_strdup(primary_msg_text); + ret = -1; + } + break; + + case SP_SUCCESS: + /* Close the message pipe. */ + ws_close(message_read_fd); + break; + + default: + /* + * Pick up the child status. + */ + ret = sync_pipe_close_command(data_read_fd, &message_read_fd, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else { + /* + * Child process returned an unknown status. + */ + *msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + ret = -1; + } + break; + } + return ret; +} + +/* Close down the stats process */ +int +sync_interface_stats_close(int *read_fd, ws_process_id *fork_child, gchar **msg) +{ +#ifndef _WIN32 + /* + * Don't bother waiting for the child. sync_pipe_close_command + * does this for us on Windows. + */ + sync_pipe_kill(*fork_child); +#endif + return sync_pipe_close_command(read_fd, NULL, fork_child, msg); +} + +/* read a number of bytes from a pipe */ +/* (blocks until enough bytes read or an error occurs) */ +static ssize_t +pipe_read_bytes(int pipe_fd, char *bytes, int required, char **msg) +{ + ssize_t newly; + ssize_t offset = 0; + int error; + + while(required) { + newly = ws_read(pipe_fd, &bytes[offset], required); + if (newly == 0) { + /* EOF */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: EOF (capture closed?)", pipe_fd); + *msg = 0; + return offset; + } + if (newly < 0) { + /* error */ + error = errno; + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: error(%u): %s", pipe_fd, error, + g_strerror(error)); + *msg = g_strdup_printf("Error reading from sync pipe: %s", + g_strerror(error)); + return newly; + } + + required -= (int)newly; + offset += newly; + } + + *msg = NULL; + return offset; +} + +/* + * Read a line from a pipe; similar to fgets, but doesn't block. + * + * XXX - just stops reading if there's nothing to be read right now; + * that could conceivably mean that you don't get a complete line. + */ +int +sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max) { + ssize_t newly; + int offset = -1; + + while(offset < max - 1) { + offset++; + if (! ws_pipe_data_available(pipe_fd)) + break; + newly = ws_read(pipe_fd, &bytes[offset], 1); + if (newly == 0) { + /* EOF - not necessarily an error */ + break; + } else if (newly == -1) { + /* error */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); + return -1; + } else if (bytes[offset] == '\n') { + break; + } + } + + if (offset >= 0) + bytes[offset] = '\0'; + + return offset; +} + + +/* convert header values (indicator and 3-byte length) */ +static void +pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) { + + g_assert(header_len == 4); + + /* convert header values */ + *indicator = header[0]; + *block_len = (header[1]&0xFF)<<16 | (header[2]&0xFF)<<8 | (header[3]&0xFF); +} + +/* read a message from the sending pipe in the standard format + (1-byte message indicator, 3-byte message length (excluding length + and indicator field), and the rest is the message) */ +static ssize_t +pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, + char **err_msg) +{ + int required; + ssize_t newly; + gchar header[4]; + + /* read header (indicator and 3-byte length) */ + newly = pipe_read_bytes(pipe_fd, header, 4, err_msg); + if(newly != 4) { + if (newly == 0) { + /* + * Immediate EOF; if the capture child exits normally, this + * is an "I'm done" indication, so don't report it as an + * error. + */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d got an EOF", pipe_fd); + return 0; + } + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d failed to read header: %lu", pipe_fd, (long)newly); + if (newly != -1) { + /* + * Short read, but not an immediate EOF. + */ + *err_msg = g_strdup_printf("Premature EOF reading from sync pipe: got only %ld bytes", + (long)newly); + } + return -1; + } + + /* convert header values */ + pipe_convert_header((guchar*)header, 4, indicator, &required); + + /* only indicator with no value? */ + if(required == 0) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d indicator: %c empty value", pipe_fd, *indicator); + return 4; + } + + /* does the data fit into the given buffer? */ + if(required > len) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d length error, required %d > len %d, header: 0x%02x 0x%02x 0x%02x 0x%02x", + pipe_fd, required, len, + header[0], header[1], header[2], header[3]); + + /* we have a problem here, try to read some more bytes from the pipe to debug where the problem really is */ + memcpy(msg, header, sizeof(header)); + newly = ws_read(pipe_fd, &msg[sizeof(header)], len-sizeof(header)); + if (newly < 0) { /* error */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); + } + *err_msg = g_strdup_printf("Unknown message from dumpcap reading header, try to show it as a string: %s", + msg); + return -1; + } + len = required; + + /* read the actual block data */ + newly = pipe_read_bytes(pipe_fd, msg, required, err_msg); + if(newly != required) { + if (newly != -1) { + *err_msg = g_strdup_printf("Unknown message from dumpcap reading data, try to show it as a string: %s", + msg); + } + return -1; + } + + /* XXX If message is "2part", the msg probably won't be sent to debug log correctly */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d ok indicator: %c len: %u msg: %s", pipe_fd, *indicator, + len, msg); + *err_msg = NULL; + return newly + 4; +} + + +/* There's stuff to read from the sync pipe, meaning the child has sent + us a message, or the sync pipe has closed, meaning the child has + closed it (perhaps because it exited). */ +static gboolean +sync_pipe_input_cb(gint source, gpointer user_data) +{ + capture_session *cap_session = (capture_session *)user_data; + int ret; + char buffer[SP_MAX_MSG_LEN+1] = {0}; + ssize_t nread; + char indicator; + int primary_len; + char *primary_msg; + int secondary_len; + char *secondary_msg; + char *wait_msg, *combined_msg; + guint32 npackets = 0; + + nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer, + &primary_msg); + if(nread <= 0) { + /* We got a read error, or a bad message, or an EOF, from the sync pipe. + + If we got a read error or a bad message, nread is -1 and + primary_msg is set to point to an error message. We don't + have to worry about killing the child; usually this error + is caused as the child killed itself while going down. + Even in the rare cases that this isn't the case, the child + will get an error when writing to the broken pipe the next time, + cleaning itself up then. + + If we got an EOF, nread is 0 and primary_msg isn't set. This + is an indication that the capture is finished. */ + ret = sync_pipe_wait_for_child(cap_session->fork_child, &wait_msg); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that the capture + child exited, and not in the middle of a message; we treat + that as an indication that it's done, and only report an + error if ret is -1, in which case wait_msg is the error + message. */ + if (ret == -1) + primary_msg = wait_msg; + } else { + /* We got an error from the sync pipe. If ret is -1, report + both the sync pipe I/O error and the wait error. */ + if (ret == -1) { + combined_msg = g_strdup_printf("%s\n\n%s", primary_msg, wait_msg); + g_free(primary_msg); + g_free(wait_msg); + primary_msg = combined_msg; + } + } + + /* No more child process. */ + cap_session->fork_child = WS_INVALID_PID; + cap_session->fork_child_status = ret; + +#ifdef _WIN32 + ws_close(cap_session->signal_pipe_write_fd); +#endif + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: cleaning extcap pipe"); + extcap_if_cleanup(cap_session->capture_opts, &primary_msg); + cap_session->closed(cap_session, primary_msg); + g_free(primary_msg); + return FALSE; + } + + /* we got a valid message block from the child, process it */ + switch(indicator) { + case SP_FILE: + if(!cap_session->new_file(cap_session, buffer)) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: file failed, closing capture"); + + /* We weren't able to open the new capture file; user has been + alerted. Close the sync pipe. */ + ws_close(source); + + /* The child has sent us a filename which we couldn't open. + + This could mean that the child is creating and deleting files + (ring buffer mode) faster than we can handle it. + + That should only be the case for very fast file switches; + We can't do much more than telling the child to stop. + (This is the "emergency brake" if the user e.g. wants to + switch files every second). + + This can also happen if the user specified "-", meaning + "standard output", as the capture file. */ + sync_pipe_stop(cap_session); + cap_session->closed(cap_session, NULL); + return FALSE; + } + break; + case SP_PACKET_COUNT: + if (!ws_strtou32(buffer, NULL, &npackets)) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "Invalid packets number: %s", buffer); + } + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: new packets %u", npackets); + cap_session->count += npackets; + cap_session->new_packets(cap_session, npackets); + break; + case SP_ERROR_MSG: + /* convert primary message */ + pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_len); + primary_msg = buffer+4; + /* convert secondary message */ + pipe_convert_header((guchar*)primary_msg + primary_len, 4, &indicator, &secondary_len); + secondary_msg = primary_msg + primary_len + 4; + /* message output */ + cap_session->error(cap_session, primary_msg, secondary_msg); + /* the capture child will close the sync_pipe, nothing to do for now */ + /* (an error message doesn't mean we have to stop capturing) */ + break; + case SP_BAD_FILTER: { + const char *message=NULL; + guint32 indx = 0; + const gchar* end; + + if (ws_strtou32(buffer, &end, &indx) && end[0] == ':') { + message = end + 1; + } + + cap_session->cfilter_error(cap_session, indx, message); + /* the capture child will close the sync_pipe, nothing to do for now */ + break; + } + case SP_DROPS: { + const char *name = NULL; + const gchar* end; + guint32 num = 0; + + if (ws_strtou32(buffer, &end, &num) && end[0] == ':') { + name = end + 1; + } + + cap_session->drops(cap_session, num, name); + break; + } + default: + g_assert_not_reached(); + } + + return TRUE; +} + + + +/* + * dumpcap is exiting; wait for it to exit. On success, *msgp is + * unchanged, and the exit status of dumpcap is returned. On + * failure (which includes "dumpcap exited due to being killed by + * a signal or an exception"), *msgp points to an error message + * for the failure, and -1 is returned. In the latter case, *msgp + * must be freed with g_free(). + */ +static int +sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) +{ + int fork_child_status; +#ifndef _WIN32 + int retry_waitpid = 3; +#endif + int ret = -1; + gint64 start_time; + double elapsed; + + start_time = g_get_monotonic_time(); + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: wait till child closed"); + g_assert(fork_child != WS_INVALID_PID); + + *msgp = NULL; /* assume no error */ +#ifdef _WIN32 + if (_cwait(&fork_child_status, (intptr_t) fork_child, _WAIT_CHILD) == -1) { + *msgp = g_strdup_printf("Error from cwait(): %s", g_strerror(errno)); + ret = -1; + } else { + /* + * The child exited; return its exit status. Do not treat this as + * an error. + */ + ret = fork_child_status; + if ((fork_child_status & 0xC0000000) == ERROR_SEVERITY_ERROR) { + /* Probably an exception code */ + *msgp = g_strdup_printf("Child dumpcap process died: %s", + win32strexception(fork_child_status)); + ret = -1; + } + } +#else + while (--retry_waitpid >= 0) { + if (waitpid(fork_child, &fork_child_status, 0) != -1) { + /* waitpid() succeeded */ + if (WIFEXITED(fork_child_status)) { + /* + * The child exited; return its exit status. Do not treat this as + * an error. + */ + ret = WEXITSTATUS(fork_child_status); + } else if (WIFSTOPPED(fork_child_status)) { + /* It stopped, rather than exiting. "Should not happen." */ + *msgp = g_strdup_printf("Child dumpcap process stopped: %s", + sync_pipe_signame(WSTOPSIG(fork_child_status))); + ret = -1; + } else if (WIFSIGNALED(fork_child_status)) { + /* It died with a signal. */ + *msgp = g_strdup_printf("Child dumpcap process died: %s%s", + sync_pipe_signame(WTERMSIG(fork_child_status)), + WCOREDUMP(fork_child_status) ? " - core dumped" : ""); + ret = -1; + } else { + /* What? It had to either have exited, or stopped, or died with + a signal; what happened here? */ + *msgp = g_strdup_printf("Bad status from waitpid(): %#o", + fork_child_status); + ret = -1; + } + } else { + /* waitpid() failed */ + if (errno == EINTR) { + /* + * Signal interrupted waitpid(). + * + * If it's SIGALRM, we just want to keep waiting, in case + * there's some timer using it (e.g., in a GUI toolkit). + * + * If you ^C TShark (or Wireshark), that should deliver + * SIGINT to dumpcap as well. dumpcap catches SIGINT, + * and should clean up and exit, so we should eventually + * see that and clean up and terminate. + * + * If we're sent a SIGTERM, we should (and do) catch it, + * and TShark, at least, calls sync_pipe_stop(). which + * kills dumpcap, so we should eventually see that and + * clean up and terminate. + */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "sync_pipe_wait_for_child: waitpid returned EINTR. retrying."); + continue; + } else if (errno == ECHILD) { + /* + * The process identified by fork_child either doesn't + * exist any more or isn't our child process (anymore?). + * + * echld might have already reaped the child. + */ + ret = fetch_dumpcap_pid ? 0 : -1; + } else { + /* Unknown error. */ + *msgp = g_strdup_printf("Error from waitpid(): %s", g_strerror(errno)); + ret = -1; + } + } + break; + } +#endif + + elapsed = (g_get_monotonic_time() - start_time) / 1e6; + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: capture child closed after %.3fs", elapsed); + return ret; +} + + +#ifndef _WIN32 +/* convert signal to corresponding name */ +static const char * +sync_pipe_signame(int sig) +{ + const char *sigmsg; + static char sigmsg_buf[6+1+3+1]; + + switch (sig) { + + case SIGHUP: + sigmsg = "Hangup"; + break; + + case SIGINT: + sigmsg = "Interrupted"; + break; + + case SIGQUIT: + sigmsg = "Quit"; + break; + + case SIGILL: + sigmsg = "Illegal instruction"; + break; + + case SIGTRAP: + sigmsg = "Trace trap"; + break; + + case SIGABRT: + sigmsg = "Abort"; + break; + + case SIGFPE: + sigmsg = "Arithmetic exception"; + break; + + case SIGKILL: + sigmsg = "Killed"; + break; + + case SIGBUS: + sigmsg = "Bus error"; + break; + + case SIGSEGV: + sigmsg = "Segmentation violation"; + break; + + /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO + Linux is POSIX compliant. These are not POSIX-defined signals --- + ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez: + + ``The signals SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS + were omitted from POSIX.1 because their behavior is + implementation dependent and could not be adequately catego- + rized. Conforming implementations may deliver these sig- + nals, but must document the circumstances under which they + are delivered and note any restrictions concerning their + delivery.'' + + So we only check for SIGSYS on those systems that happen to + implement them (a system can be POSIX-compliant and implement + them, it's just that POSIX doesn't *require* a POSIX-compliant + system to implement them). + */ + +#ifdef SIGSYS + case SIGSYS: + sigmsg = "Bad system call"; + break; +#endif + + case SIGPIPE: + sigmsg = "Broken pipe"; + break; + + case SIGALRM: + sigmsg = "Alarm clock"; + break; + + case SIGTERM: + sigmsg = "Terminated"; + break; + + default: + /* Returning a static buffer is ok in the context we use it here */ + g_snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig); + sigmsg = sigmsg_buf; + break; + } + return sigmsg; +} +#endif + + +#ifdef _WIN32 + +static void create_dummy_signal_pipe() { + gchar *dummy_signal_pipe_name; + + if (dummy_signal_pipe != NULL) return; + + if (!dummy_control_id) { + dummy_control_id = g_strdup_printf("%d.dummy", GetCurrentProcessId()); + } + + /* Create the signal pipe */ + dummy_signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, dummy_control_id); + dummy_signal_pipe = CreateNamedPipe(utf_8to16(dummy_signal_pipe_name), + PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL); + g_free(dummy_signal_pipe_name); +} + +/* tell the child through the signal pipe that we want to quit the capture */ +static void +signal_pipe_capquit_to_child(capture_session *cap_session) +{ + const char quit_msg[] = "QUIT"; + int ret; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "signal_pipe_capquit_to_child"); + + /* it doesn't matter *what* we send here, the first byte will stop the capture */ + /* simply sending a "QUIT" string */ + /*pipe_write_block(cap_session->signal_pipe_write_fd, SP_QUIT, quit_msg);*/ + ret = ws_write(cap_session->signal_pipe_write_fd, quit_msg, sizeof quit_msg); + if(ret == -1) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "signal_pipe_capquit_to_child: %d header: error %s", cap_session->signal_pipe_write_fd, g_strerror(errno)); + } +} +#endif + + +/* user wants to stop the capture run */ +void +sync_pipe_stop(capture_session *cap_session) +{ +#ifdef _WIN32 + int count; + DWORD childstatus; + gboolean terminate = TRUE; +#endif + if (cap_session->fork_child != WS_INVALID_PID) { +#ifndef _WIN32 + /* send the SIGINT signal to close the capture child gracefully. */ + int sts = kill(cap_session->fork_child, SIGINT); + if (sts != 0) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "Sending SIGINT to child failed: %s\n", g_strerror(errno)); + } +#else +#define STOP_SLEEP_TIME 500 /* ms */ +#define STOP_CHECK_TIME 50 + /* First, use the special signal pipe to try to close the capture child + * gracefully. + */ + signal_pipe_capquit_to_child(cap_session); + + /* Next, wait for the process to exit on its own */ + for (count = 0; count < STOP_SLEEP_TIME / STOP_CHECK_TIME; count++) { + if (GetExitCodeProcess((HANDLE) cap_session->fork_child, &childstatus) && + childstatus != STILL_ACTIVE) { + terminate = FALSE; + break; + } + Sleep(STOP_CHECK_TIME); + } + + /* Force the issue. */ + if (terminate) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "sync_pipe_stop: forcing child to exit"); + sync_pipe_kill(cap_session->fork_child); + } +#endif + } +} + + +/* Wireshark has to exit, force the capture child to close */ +void +sync_pipe_kill(ws_process_id fork_child) +{ + if (fork_child != WS_INVALID_PID) { +#ifndef _WIN32 + int sts = kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */ + if (sts != 0) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "Sending SIGTERM to child failed: %s\n", g_strerror(errno)); + } +#else + /* Remark: This is not the preferred method of closing a process! + * the clean way would be getting the process id of the child process, + * then getting window handle hWnd of that process (using EnumChildWindows), + * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0) + * + * Unfortunately, I don't know how to get the process id from the + * handle. OpenProcess will get an handle (not a window handle) + * from the process ID; it will not get a window handle from the + * process ID. (How could it? A process can have more than one + * window. For that matter, a process might have *no* windows, + * as a process running dumpcap, the normal child process program, + * probably does.) + * + * Hint: GenerateConsoleCtrlEvent() will only work if both processes are + * running in the same console; that's not necessarily the case for + * us, as we might not be running in a console. + * And this also will require to have the process id. + */ + TerminateProcess((HANDLE) (fork_child), 0); + +#endif + } +} + +void capture_sync_set_fetch_dumpcap_pid_cb(void(*cb)(ws_process_id pid)) { + fetch_dumpcap_pid = cb; +} + +#endif /* HAVE_LIBPCAP */ diff --git a/capture/capture_sync.h b/capture/capture_sync.h new file mode 100644 index 0000000000..b2c6751eff --- /dev/null +++ b/capture/capture_sync.h @@ -0,0 +1,112 @@ +/* capture_sync.h + * Synchronisation between Wireshark capture parent and child instances + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +/** @file + * + * Sync mode capture (internal interface). + * + * Will start a new Wireshark child instance which will do the actual capture + * work. + */ + +#ifndef __CAPTURE_SYNC_H__ +#define __CAPTURE_SYNC_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct _info_data; + +/** + * Start a new capture session. + * Create a capture child which is doing the real capture work. + * The various capture_input_... functions will be called, if something had + * happened. + * + * Most of the parameters are passed through the global capture_opts. + * + * @param capture_opts the options + * @param cap_session a handle for the capture session + * @param cap_data a struct with capture info data + * @param update_cb update screen + * @return TRUE if a capture could be started, FALSE if not + */ +extern gboolean +sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, struct _info_data* cap_data, void(*update_cb)(void)); + +/** User wants to stop capturing, gracefully close the capture child */ +extern void +sync_pipe_stop(capture_session *cap_session); + +/** User wants to stop the program, just kill the child as soon as possible */ +extern void +sync_pipe_kill(ws_process_id fork_child); + +/** + * Set wireless channel using dumpcap + * On success, *data points to a buffer containing the dumpcap output, + * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data + * must be freed with g_free(). + * + * On failure, *data is NULL, *primary_msg points to an error message, + * *secondary_msg either points to an additional error message or is + * NULL, and -1 or errno value is returned; *primary_msg, and + * *secondary_msg if not NULL must be freed with g_free(). + * + * @param iface (monitor) network interface name + * @param freq channel control frequency string (in MHz) + * @param type channel type string (or NULL if not used) + * @param center_freq1 VHT channel center frequency (or NULL if not used) + * @param center_freq2 VHT channel center frequency 2 (or NULL if not used) + * @param data On success, *data points to a buffer containing the dumpcap output, On failure *data is NULL + * @param primary_msg On success NULL, On failure points to an error message + * @param secondary_msg On success NULL, On failure either points to an additional error message or is NULL + * @param update_cb update callback + * @return 0 on success + */ +extern int +sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type, + const gchar *center_freq1, const gchar *center_freq2, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)); + +/** Get an interface list using dumpcap */ +extern int +sync_interface_list_open(gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)); + +/** Get interface capabilities using dumpcap */ +extern int +sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, const gchar* auth, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)); + +/** Start getting interface statistics using dumpcap. */ +extern int +sync_interface_stats_open(int *read_fd, ws_process_id *fork_child, gchar **msg, void (*update_cb)(void)); + +/** Stop gathering statistics. */ +extern int +sync_interface_stats_close(int *read_fd, ws_process_id *fork_child, gchar **msg); + +/** Read a line from a pipe, similar to fgets. Non-blocking. */ +extern int +sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max); + +/* set a callback to be called after fork with the pid of the forked child */ +extern void capture_sync_set_fetch_dumpcap_pid_cb(void(*cb)(ws_process_id pid)); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPTURE_SYNC_H__ */ diff --git a/capture/capture_win_ifnames.c b/capture/capture_win_ifnames.c new file mode 100644 index 0000000000..90cc1cbffd --- /dev/null +++ b/capture/capture_win_ifnames.c @@ -0,0 +1,271 @@ +/* capture_win_ifnames.c + * Routines supporting the use of Windows friendly interface names within Wireshark + * Copyright 2011-2012, Mike Garratt + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#ifdef _WIN32 + +#include +#include +#include +#include +#include + +#include + +#include + +#ifndef NDIS_IF_MAX_STRING_SIZE +#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE /* =256 in */ +#endif + +#ifndef NETIO_STATUS +#define NETIO_STATUS DWORD +#endif + +#include "log.h" + +#include "capture/capture_ifinfo.h" +#include "capture/capture_win_ifnames.h" + +#include + +static int gethexdigit(const char *p) +{ + if(*p >= '0' && *p <= '9'){ + return *p - '0'; + }else if(*p >= 'A' && *p <= 'F'){ + return *p - 'A' + 0xA; + }else if(*p >= 'a' && *p <= 'f'){ + return *p - 'a' + 0xa; + }else{ + return -1; /* Not a hex digit */ + } +} + +static gboolean get8hexdigits(const char *p, DWORD *d) +{ + int digit; + DWORD val; + int i; + + val = 0; + for(i = 0; i < 8; i++){ + digit = gethexdigit(p++); + if(digit == -1){ + return FALSE; /* Not a hex digit */ + } + val = (val << 4) | digit; + } + *d = val; + return TRUE; +} + +static gboolean get4hexdigits(const char *p, WORD *w) +{ + int digit; + WORD val; + int i; + + val = 0; + for(i = 0; i < 4; i++){ + digit = gethexdigit(p++); + if(digit == -1){ + return FALSE; /* Not a hex digit */ + } + val = (val << 4) | digit; + } + *w = val; + return TRUE; +} + +/* + * If a string is a GUID in {}, fill in a GUID structure with the GUID + * value and return TRUE; otherwise, if the string is not a valid GUID + * in {}, return FALSE. + */ +gboolean +parse_as_guid(const char *guid_text, GUID *guid) +{ + int i; + int digit1, digit2; + + if(*guid_text != '{'){ + return FALSE; /* Nope, not enclosed in {} */ + } + guid_text++; + /* There must be 8 hex digits; if so, they go into guid->Data1 */ + if(!get8hexdigits(guid_text, &guid->Data1)){ + return FALSE; /* nope, not 8 hex digits */ + } + guid_text += 8; + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* There must be 4 hex digits; if so, they go into guid->Data2 */ + if(!get4hexdigits(guid_text, &guid->Data2)){ + return FALSE; /* nope, not 4 hex digits */ + } + guid_text += 4; + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* There must be 4 hex digits; if so, they go into guid->Data3 */ + if(!get4hexdigits(guid_text, &guid->Data3)){ + return FALSE; /* nope, not 4 hex digits */ + } + guid_text += 4; + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* + * There must be 4 hex digits; if so, they go into the first 2 bytes + * of guid->Data4. + */ + for(i = 0; i < 2; i++){ + digit1 = gethexdigit(guid_text); + if(digit1 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + digit2 = gethexdigit(guid_text); + if(digit2 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + guid->Data4[i] = (digit1 << 4)|(digit2); + } + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* + * There must be 12 hex digits; if so,t hey go into the next 6 bytes + * of guid->Data4. + */ + for(i = 0; i < 6; i++){ + digit1 = gethexdigit(guid_text); + if(digit1 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + digit2 = gethexdigit(guid_text); + if(digit2 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + guid->Data4[i+2] = (digit1 << 4)|(digit2); + } + /* Now there must be a closing } */ + if(*guid_text != '}'){ + return FALSE; /* Nope */ + } + guid_text++; + /* And that must be the end of the string */ + if(*guid_text != '\0'){ + return FALSE; /* Nope */ + } + return TRUE; +} + +/**********************************************************************************/ +/* Get the friendly name for the given GUID */ +char * +get_interface_friendly_name_from_device_guid(__in GUID *guid) +{ + HRESULT hr; + + /* Need to convert an Interface GUID to the interface friendly name (e.g. "Local Area Connection") + * The functions required to do this all reside within iphlpapi.dll + */ + + NET_LUID InterfaceLuid; + hr = ConvertInterfaceGuidToLuid(guid, &InterfaceLuid); + if(hr == NO_ERROR) { + /* guid->luid success */ + WCHAR wName[NDIS_IF_MAX_STRING_SIZE + 1]; + hr = ConvertInterfaceLuidToAlias(&InterfaceLuid, wName, NDIS_IF_MAX_STRING_SIZE+1); + if(hr == NO_ERROR) { + /* luid->friendly name success */ + + /* Get the required buffer size, and then convert the string + * from UTF-16 to UTF-8. */ + int size; + char *name; + size = WideCharToMultiByte(CP_UTF8, 0, wName, -1, NULL, 0, NULL, NULL); + if(size != 0) { + name = (char *) g_malloc(size); + if (name != NULL) { + size = WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, size, NULL, NULL); + if(size != 0) { + return name; + } + /* Failed, clean up the allocation */ + g_free(name); + } + } + } + } + + /* Failed to get a name */ + return NULL; +} + +/* + * Given an interface name, try to extract the GUID from it and parse it. + * If that fails, return NULL; if that succeeds, attempt to get the + * friendly name for the interface in question. If that fails, return + * NULL, otherwise return the friendly name, allocated with g_malloc() + * (so that it must be freed with g_free()). + */ +char * +get_windows_interface_friendly_name(const char *interface_devicename) +{ + const char* guid_text; + GUID guid; + + /* Extract the guid text from the interface device name */ + if(strncmp("\\Device\\NPF_", interface_devicename, 12)==0){ + guid_text=interface_devicename+12; /* skip over the '\Device\NPF_' prefix, assume the rest is the guid text */ + }else{ + guid_text=interface_devicename; + } + + if (!parse_as_guid(guid_text, &guid)){ + return NULL; /* not a GUID, so no friendly name */ + } + + /* guid okay, get the interface friendly name associated with the guid */ + return get_interface_friendly_name_from_device_guid(&guid); +} + +/**************************************************************************************/ +#endif + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/capture/capture_win_ifnames.h b/capture/capture_win_ifnames.h new file mode 100644 index 0000000000..9289d2499f --- /dev/null +++ b/capture/capture_win_ifnames.h @@ -0,0 +1,34 @@ +/* capture_win_ifnames.h + * Routines supporting the use of Windows friendly interface names within Wireshark + * Copyright 2011-2012, Mike Garratt + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_WIN_IFNAMES_H +#define CAPTURE_WIN_IFNAMES_H + +/* + * If a string is a GUID in {}, fill in a GUID structure with the GUID + * value and return TRUE; otherwise, if the string is not a valid GUID + * in {}, return FALSE. + */ +extern gboolean parse_as_guid(const char *guid_text, GUID *guid); + +/* Get the friendly name for the given GUID */ +extern char *get_interface_friendly_name_from_device_guid(GUID *guid); + +/* + * Given an interface name, try to extract the GUID from it and parse it. + * If that fails, return NULL; if that succeeds, attempt to get the + * friendly name for the interface in question. If that fails, return + * NULL, otherwise return the friendly name, allocated with g_malloc() + * (so that it must be freed with g_free()). + */ +extern char *get_windows_interface_friendly_name(const char *interface_devicename); + +#endif diff --git a/capture/iface_monitor.c b/capture/iface_monitor.c new file mode 100644 index 0000000000..6791a60103 --- /dev/null +++ b/capture/iface_monitor.c @@ -0,0 +1,398 @@ +/* iface_monitor.c + * interface monitor by Pontus Fuchs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#ifdef HAVE_LIBPCAP + +#include +#include "ws_attributes.h" + +#if defined(HAVE_LIBNL) + +/* + * Linux with libnl. + */ + +#include +#include +#include + +DIAG_OFF_PEDANTIC +#include +DIAG_ON_PEDANTIC +#include +DIAG_OFF_PEDANTIC +#include +DIAG_ON_PEDANTIC + +#ifndef IFF_UP +/* + * Apparently, some versions of libnl drag in headers that define IFF_UP + * and others don't. Include iff IFF_UP isn't already defined, + * so that if has been included by some or all of the + * netlink headers, we don't include and get a bunch of + * complaints about various structures being redefined. + */ +#include +#endif + +/* libnl 1.x compatibility code */ +#ifdef HAVE_LIBNL1 +#define nl_sock nl_handle +#define nl_socket_disable_seq_check nl_disable_sequence_check + +static inline struct nl_handle *nl_socket_alloc(void) +{ + return nl_handle_alloc(); +} + +static inline void nl_socket_free(struct nl_sock *h) +{ + nl_handle_destroy(h); +} +#endif /* HAVE_LIBNL1 */ + +static struct nl_sock *iface_mon_sock; + +static void +iface_mon_handler2(struct nl_object *obj, void *arg) +{ + struct rtnl_link *filter; + struct rtnl_link *link_obj; + int flags, up; + char *ifname; + iface_mon_cb cb = (iface_mon_cb)arg; + + filter = rtnl_link_alloc(); + if (!filter) { + fprintf(stderr, "error allocating filter\n"); + return; + } + + if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) { + rtnl_link_put(filter); + return; + } + + link_obj = (struct rtnl_link *) obj; + flags = rtnl_link_get_flags (link_obj); + ifname = rtnl_link_get_name(link_obj); + + /* + * You can't bind a PF_PACKET socket to an interface that's not + * up, so an interface going down is an "interface should be + * removed" indication. + * + * XXX - what indication, if any, do we get if the interface + * *completely goes away*? + * + * XXX - can we get events if an interface's link-layer or + * network addresses change? + */ + up = (flags & IFF_UP) ? 1 : 0; + +#ifdef HAVE_LIBNL1 + cb(ifname, 0, up); +#else + int msg_type = nl_object_get_msgtype(obj); + + switch (msg_type) { + case RTM_NEWLINK: + cb(ifname, 1, up); + break; + case RTM_DELLINK: + cb(ifname, 0, 0); + break; + default: + /* Ignore other events */ + break; + } +#endif + + rtnl_link_put(filter); + + return; +} + +static int +iface_mon_handler(struct nl_msg *msg, void *arg) +{ + nl_msg_parse (msg, &iface_mon_handler2, arg); + return 0; +} + +void +iface_mon_event(void) +{ + nl_recvmsgs_default(iface_mon_sock); +} + +int +iface_mon_get_sock(void) +{ + return nl_socket_get_fd(iface_mon_sock); +} + +int +iface_mon_start(iface_mon_cb cb) +{ + int err; + + iface_mon_sock = nl_socket_alloc(); + if (!iface_mon_sock) { + fprintf(stderr, "Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + nl_socket_disable_seq_check(iface_mon_sock); + + nl_socket_modify_cb(iface_mon_sock, NL_CB_VALID, NL_CB_CUSTOM, iface_mon_handler, (void *)cb); + + if (nl_connect(iface_mon_sock, NETLINK_ROUTE)) { + fprintf(stderr, "Failed to connect to generic netlink.\n"); + err = -ENOLINK; + goto out_handle_destroy; + } + + nl_socket_add_membership(iface_mon_sock, RTNLGRP_LINK); + + return 0; + +out_handle_destroy: + nl_socket_free(iface_mon_sock); + return err; +} + +void +iface_mon_stop(void) +{ + if(iface_mon_sock) + nl_socket_free(iface_mon_sock); + iface_mon_sock = NULL; +} + +#elif defined(__APPLE__) + +/* + * macOS. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int s; +static iface_mon_cb callback; + +int +iface_mon_start(iface_mon_cb cb) +{ + int ret; + struct kev_request key; + + /* Create a socket of type PF_SYSTEM to listen for events. */ + s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); + if (s == -1) + return -errno; + + /* + * Ask for DLIL messages. + * + * XXX - also ask for KEV_INET_SUBCLASS and KEV_INET6_SUBCLASS, + * to detect new or changed network addresses, so those can be + * updated as well? Can we specify multiple filters on a socket, + * or must we specify KEV_ANY_SUBCLASS and filter the events after + * receiving them? + */ + key.vendor_code = KEV_VENDOR_APPLE; + key.kev_class = KEV_NETWORK_CLASS; + key.kev_subclass = KEV_DL_SUBCLASS; + if (ioctl(s, SIOCSKEVFILT, &key) == -1) { + ret = -errno; + close(s); + return ret; + } + + callback = cb; + return 0; +} + +void +iface_mon_stop(void) +{ + close(s); +} + +int +iface_mon_get_sock(void) +{ + return s; +} + +/* + * Size of buffer for kernel network event. + */ +#define NET_EVENT_DATA_SIZE (KEV_MSG_HEADER_SIZE + sizeof (struct net_event_data)) + +void +iface_mon_event(void) +{ + char msg[NET_EVENT_DATA_SIZE]; + ssize_t received; + struct kern_event_msg *kem; + struct net_event_data *evd; + size_t evd_len; + char ifr_name[IFNAMSIZ]; + + received = recv(s, msg, sizeof msg, 0); + if (received < 0) { + /* Error - ignore. */ + return; + } + if ((size_t)received < sizeof msg) { + /* Short read - ignore. */ + return; + } + kem = (struct kern_event_msg *)msg; + evd_len = kem->total_size - KEV_MSG_HEADER_SIZE; + if (evd_len != sizeof (struct net_event_data)) { + /* Length of the message is bogus. */ + return; + } + evd = (struct net_event_data *)&kem->event_data[0]; + g_snprintf(ifr_name, IFNAMSIZ, "%s%u", evd->if_name, evd->if_unit); + + /* + * Check type of event. + * + * Note: if we also ask for KEV_INET_SUBCLASS, we will get + * events with keys + * + * KEV_INET_NEW_ADDR + * KEV_INET_CHANGED_ADDR + * KEV_INET_CHANGED_ADDR + * KEV_INET_SIFDSTADDR + * KEV_INET_SIFBRDADDR + * KEV_INET_SIFNETMASK + * + * reflecting network address changes, with the data being a + * struct kev_in_data rather than struct net_event_data, and + * if we also ask for KEV_INET6_SUBCLASS, we will get events + * with keys + * + * KEV_INET6_NEW_LL_ADDR + * KEV_INET6_NEW_USER_ADDR + * KEV_INET6_NEW_RTADV_ADDR + * KEV_INET6_ADDR_DELETED + * + * with the data being a struct kev_in6_data. + */ + switch (kem->event_code) { + + case KEV_DL_IF_ATTACHED: + /* + * A new interface has arrived. + * + * XXX - what we really want is "a new BPFable interface + * has arrived", but that's not available. While we're + * asking for additional help from BPF, it'd also be + * nice if we could ask it for a list of all interfaces + * that have had bpfattach()/bpf_attach() done on them, + * so we don't have to try to open the device in order + * to see whether we should show it as something on + * which we can capture. + */ + callback(ifr_name, 1, 1); + break; + + case KEV_DL_IF_DETACHED: + /* + * An existing interface has been removed. + * + * XXX - use KEV_DL_IF_DETACHING instead, as that's + * called shortly after bpfdetach() is called, and + * bpfdetach() makes an interface no longer BPFable, + * and that's what we *really* care about. + */ + callback(ifr_name, 0, 0); + break; + + default: + /* + * Is there any reason to care about: + * + * KEV_DL_LINK_ON + * KEV_DL_LINK_OFF + * KEV_DL_SIFFLAGS + * KEV_DL_LINK_ADDRESS_CHANGED + * KEV_DL_IFCAP_CHANGED + * + * or any of the other events? On Snow Leopard and, I think, + * earlier releases, you can't attach a BPF device to an + * interface that's not up, so KEV_DL_SIFFLAGS might be + * worth listening to so that we only say "here's a new + * interface" when it goes up; on Lion (and possibly Mountain + * Lion), an interface doesn't have to be up in order to + * have a BPF device attached to it. + */ + break; + } +} + +#else /* don't have something we support */ + +int +iface_mon_start(iface_mon_cb cb _U_) +{ + return -1; +} + +void +iface_mon_stop(void) +{ +} + +int +iface_mon_get_sock(void) +{ + return -1; +} + +void +iface_mon_event(void) +{ +} + +#endif /* HAVE_LIBNL */ + +#endif /* HAVE_LIBPCAP */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/capture/iface_monitor.h b/capture/iface_monitor.h new file mode 100644 index 0000000000..785ab34a63 --- /dev/null +++ b/capture/iface_monitor.h @@ -0,0 +1,71 @@ +/* iface_monitor.h + * interface monitor by Pontus Fuchs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef IFACE_MONITOR_H +#define IFACE_MONITOR_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef HAVE_LIBPCAP + +/* + * Callback for interface changes. + * + * iface is a pointer to the name of the interface. + * + * up is 1 if the interface is up, 0 if it's down. + * + * XXX - we really want "gone", not "down", where "gone" may include + * "down" if the OS requires an interface to be up in order to start + * a capture on it (as is the case in Linux and in macOS prior to + * Lion), but should also include *gone*, as in "there is no longer + * an interface with this name, so it's neither down nor up". + * + * We also may want other events, such as address changes, so what + * we might want is "add", "remove", and "modify" as the events. + */ +typedef void (*iface_mon_cb)(const char *iface, int added, int up); + +/* + * Start watching for interface changes. + */ +int +iface_mon_start(iface_mon_cb cb); + +/* + * Stop watching for interface changes. + */ +void +iface_mon_stop(void); + +/* + * Get the socket on which interface changes are delivered, so that + * we can add it to the event loop. + * + * XXX - what if it's not a socket or other file descriptor? + */ +int +iface_mon_get_sock(void); + +/* + * Call this if something is readable from the interface change socket. + * It will call the callback as appropriate. + */ +void +iface_mon_event(void); + +#endif /* HAVE_LIBPCAP */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* IFACE_MONITOR_H */ diff --git a/capture/ws80211_utils.c b/capture/ws80211_utils.c new file mode 100644 index 0000000000..6c34b8e929 --- /dev/null +++ b/capture/ws80211_utils.c @@ -0,0 +1,1275 @@ +/* + * ws80211 utilities + * Copyright 2012, Pontus Fuchs + +Parts of this file was copied from iw: + +Copyright (c) 2007, 2008 Johannes Berg +Copyright (c) 2007 Andy Lutomirski +Copyright (c) 2007 Mike Kershaw +Copyright (c) 2008-2009 Luis R. Rodriguez + +SPDX-License-Identifier: ISC +*/ + +#include + +#include + +#include +#include + +#include "ws80211_utils.h" + +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) +#include +#include +#include + +#include +#include + +DIAG_OFF_PEDANTIC +#include +DIAG_ON_PEDANTIC +#include +#include +DIAG_OFF_PEDANTIC +#include +DIAG_ON_PEDANTIC +#include + +#include + +#include + +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +static int ws80211_get_protocol_features(int* features); +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ + +/* libnl 1.x compatibility code */ +#ifdef HAVE_LIBNL1 +#define nl_sock nl_handle +static inline struct nl_handle *nl_socket_alloc(void) +{ + return nl_handle_alloc(); +} + +static inline void nl_socket_free(struct nl_sock *h) +{ + nl_handle_destroy(h); +} +#endif /* HAVE_LIBNL1 */ + +struct nl80211_state { + struct nl_sock *nl_sock; + int nl80211_id; + int have_split_wiphy; +}; + +static struct nl80211_state nl_state; + +int ws80211_init(void) +{ + int err; +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP + int features = 0; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ + + struct nl80211_state *state = &nl_state; + + state->nl_sock = nl_socket_alloc(); + if (!state->nl_sock) { + fprintf(stderr, "Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + if (genl_connect(state->nl_sock)) { + fprintf(stderr, "Failed to connect to generic netlink.\n"); + err = -ENOLINK; + goto out_handle_destroy; + } + + state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); + if (state->nl80211_id < 0) { + fprintf(stderr, "nl80211 not found.\n"); + err = -ENOENT; + goto out_handle_destroy; + } +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP + ws80211_get_protocol_features(&features); + if (features & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + state->have_split_wiphy = TRUE; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ + + return WS80211_INIT_OK; + + out_handle_destroy: + nl_socket_free(state->nl_sock); + state->nl_sock = 0; + return err; +} + +static int error_handler(struct sockaddr_nl *nla _U_, struct nlmsgerr *err, + void *arg) +{ + int *ret = (int *)arg; + *ret = err->error; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int *)arg; + *ret = 0; + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int *)arg; + *ret = 0; + return NL_STOP; +} + +static int nl80211_do_cmd(struct nl_msg *msg, struct nl_cb *cb) +{ + /* + * XXX - Coverity doesn't understand how libnl works, so it + * doesn't know that nl_recvmsgs() calls the callback, and + * that the callback has had a pointer to err registered + * with it, and therefore that nl_recvmsgs() can change + * err as a side-effect, so it thinks this can loop + * infinitely. + * + * The proper way to address this is to help Coverity to + * understand the behaviour of nl_recvmsgs(), in that it + * does call the callback, setting err. This help would be + * provided through a so called 'model' of this function. + * We declare err to be volatile to work around it. + * + * XXX - that workaround provokes a compiler complaint that + * casting a pointer to it to "void *" discards the + * volatile qualifier. Perhaps we should just re-close + * Coverity CID 997052 as "false positive". + */ + volatile int err; + + if (!nl_state.nl_sock) + return -ENOLINK; + + err = nl_send_auto_complete(nl_state.nl_sock, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, (void *)&err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, (void *)&err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, (void *)&err); + + while (err > 0) + nl_recvmsgs(nl_state.nl_sock, cb); + out: + nl_cb_put(cb); + + return err; +} + +struct nliface_cookie +{ + char *ifname; + GArray *interfaces; +}; + +static struct ws80211_interface * + get_interface_by_name(GArray *interfaces, + char* ifname) +{ + unsigned int i; + struct ws80211_interface *iface; + + for (i = 0; i < interfaces->len; i++) { + iface = g_array_index(interfaces, struct ws80211_interface *, i); + if (!strcmp(iface->ifname, ifname)) + return iface; + } + return NULL; +} + +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +static int get_features_handler(struct nl_msg *msg, void *arg) +{ + int *feat = (int*) arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) + *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); + + return NL_SKIP; +} + +static int ws80211_get_protocol_features(int* features) +{ + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, 0, + NL80211_CMD_GET_PROTOCOL_FEATURES, 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_features_handler, features); + + ret = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + return ret; +} +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ + +#ifdef NL80211_BAND_ATTR_HT_CAPA +static void parse_band_ht_capa(struct ws80211_interface *iface, + struct nlattr *tb) +{ + gboolean ht40; + + if (!tb) return; + + iface->channel_types |= 1 << WS80211_CHAN_HT20; + ht40 = !!(nla_get_u16(tb) & 0x02); + if (ht40) { + iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS; + iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS; + } +} +#endif /* NL80211_BAND_ATTR_HT_CAPA */ + +#ifdef HAVE_NL80211_VHT_CAPABILITY +static void parse_band_vht_capa(struct ws80211_interface *iface, + struct nlattr *tb) +{ + guint32 chan_capa; + if (!tb) return; + + chan_capa = (nla_get_u32(tb) >> 2) & 3; + if (chan_capa == 1) { + iface->channel_types |= 1 << WS80211_CHAN_VHT160; + } + if (chan_capa == 2) { + iface->channel_types |= 1 << WS80211_CHAN_VHT160; + iface->channel_types |= 1 << WS80211_CHAN_VHT80P80; + } + iface->channel_types |= 1 << WS80211_CHAN_VHT80; +} +#endif /* HAVE_NL80211_VHT_CAPABILITY */ + +static void parse_supported_iftypes(struct ws80211_interface *iface, + struct nlattr *tb) +{ + struct nlattr *nl_mode; + int rem_mode; + + if (!tb) return; + + nla_for_each_nested(nl_mode, tb, rem_mode) { + if (nla_type(nl_mode) == NL80211_IFTYPE_MONITOR) + iface->cap_monitor = 1; + } +} + +static void parse_band_freqs(struct ws80211_interface *iface, + struct nlattr *tb) +{ + struct nlattr *nl_freq; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + {NLA_UNSPEC, 0, 0}, /* __NL80211_FREQUENCY_ATTR_INVALID */ + {NLA_U32, 0, 0}, /* NL80211_FREQUENCY_ATTR_FREQ */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_DISABLED */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_PASSIVE_SCAN */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_NO_IBSS */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_RADAR */ + {NLA_U32, 0, 0} /* NL80211_FREQUENCY_ATTR_MAX_TX_POWER */ + }; + int rem_freq; + + if (!tb) return; + + nla_for_each_nested(nl_freq, tb, rem_freq) { + uint32_t freq; + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + (struct nlattr *)nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + continue; + + freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + g_array_append_val(iface->frequencies, freq); + } +} + +static void parse_wiphy_bands(struct ws80211_interface *iface, + struct nlattr *tb) +{ + struct nlattr *nl_band; + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + int bandidx = 1; + int rem_band; + + if (!tb) return; + + nla_for_each_nested(nl_band, tb, rem_band) { + bandidx++; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, + (struct nlattr *)nla_data(nl_band), + nla_len(nl_band), NULL); + +#ifdef NL80211_BAND_ATTR_HT_CAPA + parse_band_ht_capa(iface, tb_band[NL80211_BAND_ATTR_HT_CAPA]); +#endif /* NL80211_BAND_ATTR_HT_CAPA */ +#ifdef HAVE_NL80211_VHT_CAPABILITY + parse_band_vht_capa(iface, tb_band[NL80211_BAND_ATTR_VHT_CAPA]); +#endif /* HAVE_NL80211_VHT_CAPABILITY */ + parse_band_freqs(iface, tb_band[NL80211_BAND_ATTR_FREQS]); + } +} + +static void parse_supported_commands(struct ws80211_interface *iface, + struct nlattr *tb) +{ + /* Can frequency be set? Only newer versions of cfg80211 supports this */ +#ifdef HAVE_NL80211_CMD_SET_CHANNEL + int cmd; + struct nlattr *nl_cmd; + + if (!tb) return; + + nla_for_each_nested(nl_cmd, tb, cmd) { + if(nla_get_u32(nl_cmd) == NL80211_CMD_SET_CHANNEL) + iface->can_set_freq = TRUE; + } +#else + iface->can_set_freq = TRUE; +#endif +} + +static int get_phys_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + + struct nliface_cookie *cookie = (struct nliface_cookie *)arg; + + struct ws80211_interface *iface; + char* ifname; + int added = 0; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_NAME]) + return NL_SKIP; + + ifname = g_strdup_printf("%s.mon", nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); + iface = get_interface_by_name(cookie->interfaces, ifname); + + if (!iface) { + iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface)); + if (!iface) { + g_free(ifname); + return NL_SKIP; + } + added = 1; + iface->ifname = ifname; + iface->frequencies = g_array_new(FALSE, FALSE, sizeof(uint32_t)); + iface->channel_types = 1 << WS80211_CHAN_NO_HT; + } else { + g_free(ifname); + } + + parse_supported_iftypes(iface, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]); + parse_wiphy_bands(iface, tb_msg[NL80211_ATTR_WIPHY_BANDS]); + parse_supported_commands(iface, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]); + + if (added) + g_array_append_val(cookie->interfaces, iface); + + return NL_SKIP; +} + +static int ws80211_get_phys(GArray *interfaces) +{ + struct nliface_cookie cookie; + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + cookie.interfaces = interfaces; + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + NLM_F_DUMP, NL80211_CMD_GET_WIPHY, 0); + +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP + if (nl_state.have_split_wiphy) { + NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); + } +#endif /* #ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP */ + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_phys_handler, &cookie); + + ret = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + return ret; + +#ifdef HAVE_NL80211_SPLIT_WIPHY_DUMP +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return -1; +#endif /* HAVE_NL80211_SPLIT_WIPHY_DUMP */ +} + +static int get_freq_wext(const char *ifname) +{ + int fd; + int ret = -1; + /* Ugly hack to avoid including wireless.h */ + struct { + char name1[IFNAMSIZ]; + __s32 m; + __s16 e; + __u8 i; + __u8 flags; + } wrq; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) + return -1; + + g_strlcpy(wrq.name1, ifname, IFNAMSIZ); + /* SIOCGIWFREQ */ + if (ioctl(fd, 0x8B05, &wrq) == 0) { + if (wrq.e == 6) + ret = wrq.m; + } + close(fd); + return ret; +} + +struct __iface_info +{ + struct ws80211_iface_info *pub; + int type; + int phyidx; +}; + +static int get_iface_info_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct __iface_info *iface_info = (struct __iface_info *)arg; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_IFTYPE]) { + iface_info->type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]); + } + if (tb_msg[NL80211_ATTR_WIPHY]) { + iface_info->phyidx = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); + } + + if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) { + gboolean found_ch_width = FALSE; + iface_info->pub->current_freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]); + iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT; +#ifdef HAVE_NL80211_VHT_CAPABILITY + if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH]) { + switch (nla_get_u32(tb_msg[NL80211_ATTR_CHANNEL_WIDTH])) { + case NL80211_CHAN_WIDTH_80: + iface_info->pub->current_chan_type = WS80211_CHAN_VHT80; + found_ch_width = TRUE; + break; + case NL80211_CHAN_WIDTH_80P80: + iface_info->pub->current_chan_type = WS80211_CHAN_VHT80P80; + found_ch_width = TRUE; + break; + case NL80211_CHAN_WIDTH_160: + iface_info->pub->current_chan_type = WS80211_CHAN_VHT160; + found_ch_width = TRUE; + break; + } + } + if (tb_msg[NL80211_ATTR_CENTER_FREQ1]) { + iface_info->pub->current_center_freq1 = + nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ1]); + } + if (tb_msg[NL80211_ATTR_CENTER_FREQ2]) { + iface_info->pub->current_center_freq2 = + nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ2]); + } +#endif + if (!found_ch_width && tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + switch (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + + case NL80211_CHAN_NO_HT: + iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT; + break; + + case NL80211_CHAN_HT20: + iface_info->pub->current_chan_type = WS80211_CHAN_HT20; + break; + + case NL80211_CHAN_HT40MINUS: + iface_info->pub->current_chan_type = WS80211_CHAN_HT40MINUS; + break; + + case NL80211_CHAN_HT40PLUS: + iface_info->pub->current_chan_type = WS80211_CHAN_HT40PLUS; + break; + } + } + + } + return NL_SKIP; +} + + +static int __ws80211_get_iface_info(const char *name, struct __iface_info *iface_info) +{ + int devidx; + struct nl_msg *msg; + struct nl_cb *cb; + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + devidx = if_nametoindex(name); + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_GET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_info_handler, iface_info); + + if (nl80211_do_cmd(msg, cb)) { + nlmsg_free(msg); + return -1; + } + + /* Old kernels can't get the current freq via netlink. Try WEXT too :( */ + if (iface_info->pub->current_freq == -1) + iface_info->pub->current_freq = get_freq_wext(name); + nlmsg_free(msg); + return 0; + +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return -1; +} + +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info) +{ + struct __iface_info __iface_info; + + memset(iface_info, 0, sizeof(*iface_info)); + __iface_info.pub = iface_info; + __iface_info.type = -1; + __iface_info.phyidx= -1; + __iface_info.pub->current_freq = -1; + __iface_info.pub->current_chan_type = WS80211_CHAN_NO_HT; + + return __ws80211_get_iface_info(name, &__iface_info); +} + +static int ws80211_keep_only_monitor(GArray *interfaces) +{ + unsigned int j; + struct ws80211_interface *iface; +restart: + for (j = 0; j < interfaces->len; j++) { + iface = g_array_index(interfaces, struct ws80211_interface *, j); + if (!iface->cap_monitor) { + g_array_remove_index(interfaces, j); + g_array_free(iface->frequencies, TRUE); + g_free(iface->ifname); + g_free(iface); + goto restart; + } + } + return 0; +} + +static int ws80211_populate_devices(GArray *interfaces) +{ + FILE *fh; + char line[200]; + char *t; + gchar *t2; + char *ret; + int i; + unsigned int j; + + struct ws80211_iface_info pub = {-1, WS80211_CHAN_NO_HT, -1, -1, WS80211_FCS_ALL}; + struct __iface_info iface_info; + struct ws80211_interface *iface; + + /* Get a list of phy's that can handle monitor mode */ + ws80211_get_phys(interfaces); + ws80211_keep_only_monitor(interfaces); + + fh = g_fopen("/proc/net/dev", "r"); + if(!fh) { + fprintf(stderr, "Cannot open /proc/net/dev"); + return -ENOENT; + } + + /* Skip the first two lines */ + for (i = 0; i < 2; i++) { + ret = fgets(line, sizeof(line), fh); + if (ret == NULL) { + fprintf(stderr, "Error parsing /proc/net/dev"); + fclose(fh); + return -1; + } + } + + /* Update names of user created monitor interfaces */ + while(fgets(line, sizeof(line), fh)) { + t = index(line, ':'); + if (!t) + continue; + *t = 0; + t = line; + while (*t == ' ') + t++; + memset(&iface_info, 0, sizeof(iface_info)); + iface_info.pub = &pub; + __ws80211_get_iface_info(t, &iface_info); + + if (iface_info.type == NL80211_IFTYPE_MONITOR) { + for (j = 0; j < interfaces->len; j++) { + iface = g_array_index(interfaces, struct ws80211_interface *, j); + t2 = g_strdup_printf("phy%d.mon", iface_info.phyidx); + if (t2) { + if (!strcmp(t2, iface->ifname)) { + g_free(iface->ifname); + iface->ifname = g_strdup(t); + } + g_free(t2); + } + } + } + } + fclose(fh); + return 0; +} + +static int ws80211_iface_up(const char *ifname) +{ + int sock; + struct ifreq ifreq; + + sock = socket(AF_PACKET, SOCK_RAW, 0); + if (sock == -1) + return -1; + + g_strlcpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name)); + + if (ioctl(sock, SIOCGIFFLAGS, &ifreq)) + goto out_err; + + ifreq.ifr_flags |= IFF_UP; + + if (ioctl(sock, SIOCSIFFLAGS, &ifreq)) + goto out_err; + + close(sock); + return 0; + +out_err: + close(sock); + return -1; +} + +/* Needed for NLA_PUT_STRING, which passes strlen as an int */ +DIAG_OFF_CLANG(shorten-64-to-32) +static int ws80211_create_on_demand_interface(const char *name) +{ + int devidx, phyidx, err; + struct nl_msg *msg; + struct nl_cb *cb; + + devidx = if_nametoindex(name); + if (devidx) + return ws80211_iface_up(name); + + if (sscanf(name, "phy%d.mon", &phyidx) != 1) + return -EINVAL; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phyidx); + + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); + + err = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + if (err) + return err; + return ws80211_iface_up(name); + +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return 2; +} +DIAG_ON_CLANG(shorten-64-to-32) + +int ws80211_set_freq(const char *name, guint32 freq, int chan_type, guint32 _U_ center_freq, guint32 _U_ center_freq2) +{ + int devidx, err; + struct nl_msg *msg; + struct nl_cb *cb; + + err = ws80211_create_on_demand_interface(name); + if (err) + return err; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + devidx = if_nametoindex(name); + +#ifdef HAVE_NL80211_CMD_SET_CHANNEL + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_SET_CHANNEL, 0); +#else + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_SET_WIPHY, 0); +#endif + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + + switch (chan_type) { + +#ifdef NL80211_BAND_ATTR_HT_CAPA + case WS80211_CHAN_NO_HT: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_NO_HT); + break; + + case WS80211_CHAN_HT20: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); + break; + + case WS80211_CHAN_HT40MINUS: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); + break; + + case WS80211_CHAN_HT40PLUS: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS); + break; +#endif +#ifdef HAVE_NL80211_VHT_CAPABILITY + case WS80211_CHAN_VHT80: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80); + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq); + break; + + case WS80211_CHAN_VHT80P80: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80P80); + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq); + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, center_freq2); + break; + + case WS80211_CHAN_VHT160: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_160); + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq); + break; +#endif + default: + break; + } + err = nl80211_do_cmd(msg, cb); + nlmsg_free(msg); + return err; + +nla_put_failure: + nlmsg_free(msg); + fprintf(stderr, "building message failed\n"); + return 2; + +} + +GArray* ws80211_find_interfaces(void) +{ + GArray *interfaces; + + if (!nl_state.nl_sock) + return NULL; + + interfaces = g_array_new(FALSE, FALSE, sizeof(struct ws80211_interface *)); + if (!interfaces) + return NULL; + + if (ws80211_populate_devices(interfaces)) { + ws80211_free_interfaces(interfaces); + return NULL; + } + return interfaces; +} + +int +ws80211_str_to_chan_type(const gchar *s) +{ + int ret = -1; + if (!s) + return -1; + + if (!strcmp(s, CHAN_NO_HT)) + ret = WS80211_CHAN_NO_HT; + if (!strcmp(s, CHAN_HT20)) + ret = WS80211_CHAN_HT20; + if (!strcmp(s, CHAN_HT40MINUS)) + ret = WS80211_CHAN_HT40MINUS; + if (!strcmp(s, CHAN_HT40PLUS)) + ret = WS80211_CHAN_HT40PLUS; + if (!strcmp(s, CHAN_VHT80)) + ret = WS80211_CHAN_VHT80; + if (!strcmp(s, CHAN_VHT80P80)) + ret = WS80211_CHAN_VHT80P80; + if (!strcmp(s, CHAN_VHT160)) + ret = WS80211_CHAN_VHT160; + + return ret; +} + +const gchar +*ws80211_chan_type_to_str(int type) +{ + switch (type) { + case WS80211_CHAN_NO_HT: + return CHAN_NO_HT; + case WS80211_CHAN_HT20: + return CHAN_HT20; + case WS80211_CHAN_HT40MINUS: + return CHAN_HT40MINUS; + case WS80211_CHAN_HT40PLUS: + return CHAN_HT40PLUS; + case WS80211_CHAN_VHT80: + return CHAN_VHT80; + case WS80211_CHAN_VHT80P80: + return CHAN_VHT80P80; + case WS80211_CHAN_VHT160: + return CHAN_VHT160; + } + return NULL; +} + +gboolean ws80211_has_fcs_filter(void) +{ + return FALSE; +} + +int ws80211_set_fcs_validation(const char *name _U_, enum ws80211_fcs_validation fcs_validation _U_) +{ + return -1; +} + +const char *network_manager_path = "/usr/sbin/NetworkManager"; /* Is this correct? */ +const char *ws80211_get_helper_path(void) { + if (g_file_test(network_manager_path, G_FILE_TEST_IS_EXECUTABLE)) { + return network_manager_path; + } + return NULL; +} + +#elif defined(HAVE_AIRPCAP) + +#include + +#include "airpcap.h" +#include "airpcap_loader.h" + +int ws80211_init(void) +{ + if (airpcap_get_dll_state() == AIRPCAP_DLL_OK) { + return WS80211_INIT_OK; + } + return WS80211_INIT_NOT_SUPPORTED; +} + +static const char *airpcap_dev_prefix_ = "\\\\.\\"; + +GArray* ws80211_find_interfaces(void) +{ + GArray *interfaces; + GList *airpcap_if_list, *cur_if; + int err; + gchar *err_str = NULL; + + interfaces = g_array_new(FALSE, FALSE, sizeof(struct ws80211_interface *)); + if (!interfaces) + return NULL; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + g_array_free(interfaces, TRUE); + return NULL; + } + + for (cur_if = airpcap_if_list; cur_if; cur_if = g_list_next(cur_if)) { + struct ws80211_interface *iface; + airpcap_if_info_t *airpcap_if_info = (airpcap_if_info_t *) cur_if->data; + char *ifname; + guint32 chan; + guint32 i; + + if (!airpcap_if_info) continue; + ifname = airpcap_if_info->name; + if (strlen(ifname) > 4 && g_str_has_prefix(ifname, airpcap_dev_prefix_)) ifname += 4; + + iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface)); + iface->ifname = g_strdup(ifname); + iface->can_set_freq = TRUE; + iface->frequencies = g_array_new(FALSE, FALSE, sizeof(guint32)); + + iface->channel_types = 1 << WS80211_CHAN_NO_HT; + /* + * AirPcap stores per-channel capabilities. We should probably + * do the same. */ + for (i = 0; i < airpcap_if_info->numSupportedChannels; i++) { + if (airpcap_if_info->pSupportedChannels[i].Flags & FLAG_CAN_BE_HIGH) { + iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS; + iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS; + break; + } + } + + iface->cap_monitor = 1; + + for (chan = 0; chan < airpcap_if_info->numSupportedChannels; chan++) { + g_array_append_val(iface->frequencies, airpcap_if_info->pSupportedChannels[chan].Frequency); + } + + g_array_append_val(interfaces, iface); + } + + return interfaces; +} + +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info) +{ + GList *airpcap_if_list; + int err; + gchar *err_str = NULL; + airpcap_if_info_t *airpcap_if_info; + + if (!iface_info) return -1; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + return -1; + } + + airpcap_if_info = get_airpcap_if_from_name(airpcap_if_list, name); + + if (!airpcap_if_info) { + free_airpcap_interface_list(airpcap_if_list); + return -1; + } + + memset(iface_info, 0, sizeof(*iface_info)); + iface_info->current_freq = airpcap_if_info->channelInfo.Frequency; + switch (airpcap_if_info->channelInfo.ExtChannel) { + case 0: + iface_info->current_chan_type = WS80211_CHAN_NO_HT; + break; + case -1: + iface_info->current_chan_type = WS80211_CHAN_HT40MINUS; + break; + case 1: + iface_info->current_chan_type = WS80211_CHAN_HT40PLUS; + break; + default: + return -1; + } + + switch (airpcap_if_info->CrcValidationOn) { + case AIRPCAP_VT_ACCEPT_CORRECT_FRAMES: + iface_info->current_fcs_validation = WS80211_FCS_VALID; + break; + case AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES: + iface_info->current_fcs_validation = WS80211_FCS_INVALID; + break; + default: + iface_info->current_fcs_validation = WS80211_FCS_ALL; + break; + } + + return 0; +} + +int ws80211_set_freq(const char *name, guint32 freq, int chan_type, guint32 _U_ center_freq, guint32 _U_ center_freq2) +{ + GList *airpcap_if_list; + int err; + gchar *err_str = NULL; + airpcap_if_info_t *airpcap_if_info; + gchar err_buf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle adapter; + int ret_val = -1; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + return ret_val; + } + + airpcap_if_info = get_airpcap_if_from_name(airpcap_if_list, name); + + if (!airpcap_if_info) { + free_airpcap_interface_list(airpcap_if_list); + return ret_val; + } + + adapter = airpcap_if_open(airpcap_if_info->name, err_buf); + if (adapter) { + airpcap_if_info->channelInfo.Frequency = freq; + switch (chan_type) { + case WS80211_CHAN_HT40MINUS: + airpcap_if_info->channelInfo.ExtChannel = -1; + break; + case WS80211_CHAN_HT40PLUS: + airpcap_if_info->channelInfo.ExtChannel = 1; + break; + default: + airpcap_if_info->channelInfo.ExtChannel = 0; + break; + } + + if (airpcap_if_set_device_channel_ex(adapter, airpcap_if_info->channelInfo)) { + ret_val = 0; + } + airpcap_if_close(adapter); + } + + free_airpcap_interface_list(airpcap_if_list); + return ret_val; +} + +int ws80211_str_to_chan_type(const gchar *s _U_) +{ + return -1; +} + +const gchar *ws80211_chan_type_to_str(int type _U_) +{ + return NULL; +} + +gboolean ws80211_has_fcs_filter(void) +{ + return TRUE; +} + +int ws80211_set_fcs_validation(const char *name, enum ws80211_fcs_validation fcs_validation) +{ + GList *airpcap_if_list; + int err; + gchar *err_str = NULL; + airpcap_if_info_t *airpcap_if_info; + gchar err_buf[AIRPCAP_ERRBUF_SIZE]; + PAirpcapHandle adapter; + int ret_val = -1; + + airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){ + g_free(err_str); + return ret_val; + } + + airpcap_if_info = get_airpcap_if_from_name(airpcap_if_list, name); + + if (!airpcap_if_info) { + free_airpcap_interface_list(airpcap_if_list); + return ret_val; + } + + adapter = airpcap_if_open(airpcap_if_info->name, err_buf); + if (adapter) { + AirpcapValidationType val_type = AIRPCAP_VT_ACCEPT_EVERYTHING; + switch (fcs_validation) { + case WS80211_FCS_VALID: + val_type = AIRPCAP_VT_ACCEPT_CORRECT_FRAMES; + break; + case WS80211_FCS_INVALID: + val_type = AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES; + break; + default: + break; + } + + if (airpcap_if_set_fcs_validation(adapter, val_type)) { + /* Appears to be necessary for this to take effect. */ + airpcap_if_store_cur_config_as_adapter_default(adapter); + ret_val = 0; + } + airpcap_if_close(adapter); + } + + free_airpcap_interface_list(airpcap_if_list); + return ret_val; +} + +static char *airpcap_conf_path = NULL; +const char *ws80211_get_helper_path(void) +{ + HKEY h_key = NULL; + + if (!airpcap_conf_path && RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\AirPcap"), 0, KEY_QUERY_VALUE|KEY_WOW64_32KEY, &h_key) == ERROR_SUCCESS) { + DWORD reg_ret; + TCHAR airpcap_dir_utf16[MAX_PATH]; + DWORD ad_size = sizeof(airpcap_dir_utf16)/sizeof(TCHAR); + + reg_ret = RegQueryValueEx(h_key, NULL, NULL, NULL, + (LPBYTE) &airpcap_dir_utf16, &ad_size); + + if (reg_ret == ERROR_SUCCESS) { + airpcap_dir_utf16[ad_size-1] = L'\0'; + g_free(airpcap_conf_path); + airpcap_conf_path = g_strdup_printf("%s\\AirpcapConf.exe", utf_16to8(airpcap_dir_utf16)); + + if (!g_file_test(airpcap_conf_path, G_FILE_TEST_IS_EXECUTABLE)) { + g_free(airpcap_conf_path); + airpcap_conf_path = NULL; + } + } + } + + return airpcap_conf_path; +} + +#else /* Everyone else. */ +int ws80211_init(void) +{ + return WS80211_INIT_NOT_SUPPORTED; +} + +GArray* ws80211_find_interfaces(void) +{ + return NULL; +} + +int ws80211_get_iface_info(const char *name _U_, struct ws80211_iface_info *iface_info _U_) +{ + return -1; +} + +int ws80211_set_freq(const char *name _U_, guint32 freq _U_, int _U_ chan_type, guint32 _U_ center_freq, guint32 _U_ center_freq2) +{ + return -1; +} + +int ws80211_str_to_chan_type(const gchar *s _U_) +{ + return -1; +} + +const gchar *ws80211_chan_type_to_str(int type _U_) +{ + return NULL; +} + +gboolean ws80211_has_fcs_filter(void) +{ + return FALSE; +} + +int ws80211_set_fcs_validation(const char *name _U_, enum ws80211_fcs_validation fcs_validation _U_) +{ + return -1; +} + +const char *ws80211_get_helper_path(void) { + return NULL; +} + +#endif /* HAVE_LIBNL && HAVE_NL80211 */ + +/* Common to everyone */ + +void ws80211_free_interfaces(GArray *interfaces) +{ + struct ws80211_interface *iface; + + if (!interfaces) + return; + + while (interfaces->len) { + iface = g_array_index(interfaces, struct ws80211_interface *, 0); + g_array_remove_index(interfaces, 0); + g_array_free(iface->frequencies, TRUE); + g_free(iface->ifname); + g_free(iface); + } + g_array_free(interfaces, TRUE); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/capture/ws80211_utils.h b/capture/ws80211_utils.h new file mode 100644 index 0000000000..cc945ce69a --- /dev/null +++ b/capture/ws80211_utils.h @@ -0,0 +1,134 @@ +/* + * Copyright 2012, Pontus Fuchs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __WS80211_UTILS_H__ +#define __WS80211_UTILS_H__ + +#include "ws_attributes.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum ws80211_channel_type { + WS80211_CHAN_NO_HT, + WS80211_CHAN_HT20, + WS80211_CHAN_HT40MINUS, + WS80211_CHAN_HT40PLUS, + WS80211_CHAN_VHT80, + WS80211_CHAN_VHT80P80, + WS80211_CHAN_VHT160 +}; + +#define CHAN_NO_HT "NOHT" +#define CHAN_HT20 "HT20" +#define CHAN_HT40MINUS "HT40-" +#define CHAN_HT40PLUS "HT40+" +#define CHAN_VHT80 "VHT80" +#define CHAN_VHT80P80 "VHT80+80" +#define CHAN_VHT160 "VHT160" + +/* XXX This doesn't match AirpcapValidationType. Should it? */ +enum ws80211_fcs_validation { + WS80211_FCS_ALL, + WS80211_FCS_VALID, + WS80211_FCS_INVALID +}; + +struct ws80211_interface +{ + char *ifname; + gboolean can_set_freq; + gboolean can_check_fcs; + GArray *frequencies; /* Array of guint32? */ + int channel_types; /* Union for all bands */ + int cap_monitor; +}; + +struct ws80211_iface_info { + int current_freq; + enum ws80211_channel_type current_chan_type; + int current_center_freq1; + int current_center_freq2; + enum ws80211_fcs_validation current_fcs_validation; +}; + +/** Initialize the 802.11 environment. + * On Linux this initializes an nl80211_state struct. + * On Windows this checks the AirPcap status. It does *not* load the + * AirPcap DLL. That happens when the program starts. + * + * @return WS80211_INIT_OK on success, WS80211_INIT_NOT_SUPPORTED if the + * 802.11 environment isn't supported, or the negative of an errno value + * on failure. + */ +#define WS80211_INIT_OK 0 +#define WS80211_INIT_NOT_SUPPORTED 1 + +int ws80211_init(void); + +/** Build a list of 802.11 interfaces. + * + * @return A GArray of pointers to struct ws80211_interface on success, NULL on failure. + */ +/* XXX Should we make this an array of structs instead of an array of struct pointers? + * It'd save a bit of mallocing and freeing. */ +GArray* ws80211_find_interfaces(void); + +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info); + +/** Free an interface list. + * + * @param interfaces A list of interfaces created with ws80211_find_interfaces(). + */ +void ws80211_free_interfaces(GArray *interfaces); + +/** Set the frequency and channel width for an interface. + * + * @param name The interface name. + * @param freq The frequency in MHz. + * @param chan_type The HT channel type (no, 20Mhz, 40Mhz...). + * @param center_freq The center frequency in MHz (if 80MHz, 80+80MHz or 160MHz). + * @param center_freq2 The 2nd center frequency in MHz (if 80+80MHz). + * @return Zero on success, nonzero on failure. + */ +int ws80211_set_freq(const char *name, guint32 freq, int chan_type, guint32 _U_ center_freq, guint32 _U_ center_freq2); + +int ws80211_str_to_chan_type(const gchar *s); +const gchar *ws80211_chan_type_to_str(int type); + +/** Check to see if we have FCS filtering. + * + * @return TRUE if FCS filtering is supported on this platform. + */ +gboolean ws80211_has_fcs_filter(void); + +/** Set the FCS validation behavior for an interface. + * + * @param name The interface name. + * @param fcs_validation The desired validation behavior. + * @return Zero on success, nonzero on failure. + */ +int ws80211_set_fcs_validation(const char *name, enum ws80211_fcs_validation fcs_validation); + + +/** Get the path to a helper application. + * Return the path to a separate 802.11 helper application, e.g. + * the AirPcap control panel or the GNOME Network Manager. + * + * @return The path to the helper on success, NULL on failure. + */ +const char *ws80211_get_helper_path(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __WS80211_UTILS_H__ */ -- cgit v1.2.3