From c4e8a3222648fcf22ca207f1815ebbf7cd144eeb Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 19 Sep 2024 06:14:26 +0200 Subject: Adding upstream version 4.4.0. Signed-off-by: Daniel Baumann --- capture/airpcap_loader.c | 10 +- capture/airpcap_loader.h | 12 +- capture/capture-pcap-util-int.h | 19 +- capture/capture-pcap-util.c | 550 ++++++++++++++++++++++++--- capture/capture-wpcap.c | 2 +- capture/capture_ifinfo.c | 540 +++++++++++++++++++-------- capture/capture_ifinfo.h | 57 ++- capture/capture_sync.c | 805 ++++++++++++++++++++++++---------------- capture/capture_sync.h | 7 +- capture/iface_monitor.c | 174 +++++++++ capture/ws80211_utils.c | 6 +- 11 files changed, 1626 insertions(+), 556 deletions(-) (limited to 'capture') diff --git a/capture/airpcap_loader.c b/capture/airpcap_loader.c index c57f93f4..fbfdeab8 100644 --- a/capture/airpcap_loader.c +++ b/capture/airpcap_loader.c @@ -27,14 +27,14 @@ * Set to true if the DLL was successfully loaded AND all functions * are present. */ -static bool AirpcapLoaded = false; +static bool AirpcapLoaded; #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 void * AirpcapLib; static AirpcapGetLastErrorHandler g_PAirpcapGetLastError; static AirpcapSetKernelBufferHandler g_PAirpcapSetKernelBuffer; @@ -77,13 +77,13 @@ static AirpcapGetDeviceChannelExHandler g_PAirpcapGetDeviceChannelEx; static AirpcapGetDeviceSupportedChannelsHandler g_PAirpcapGetDeviceSupportedChannels; /* Airpcap interface list */ -GList *g_airpcap_if_list = NULL; +GList *g_airpcap_if_list; /* Airpcap current selected interface */ -airpcap_if_info_t *airpcap_if_selected = NULL; +airpcap_if_info_t *airpcap_if_selected; /* Airpcap current active interface */ -airpcap_if_info_t *airpcap_if_active = NULL; +airpcap_if_info_t *airpcap_if_active; Dot11Channel *pSupportedChannels; unsigned numSupportedChannels; diff --git a/capture/airpcap_loader.h b/capture/airpcap_loader.h index 9ca9fa45..4f49f74c 100644 --- a/capture/airpcap_loader.h +++ b/capture/airpcap_loader.h @@ -106,13 +106,13 @@ typedef struct { AirpcapValidationType CrcValidationOn; /* Capture Frames with Wrong CRC */ AirpcapDecryptionState DecryptionOn; /* true if decryption is on, false otherwise */ PAirpcapKeysCollection keysCollection; /* WEP Key collection for the adapter */ - unsigned keysCollectionSize; /* Size of the key collection */ - bool blinking; /* true if is blinkng, false otherwise */ - bool led; /* true if on, false if off */ - bool saved; /* true if current configuration has been saved, false otherwise */ - int tag; /* int for the gtk blinking callback */ + unsigned keysCollectionSize; /* Size of the key collection */ + bool blinking; /* true if is blinking, false otherwise */ + bool led; /* true if on, false if off */ + bool saved; /* true if current configuration has been saved, false otherwise */ + int tag; /* int for the gtk blinking callback */ Dot11Channel *pSupportedChannels; - uint32_t numSupportedChannels; + uint32_t numSupportedChannels; } airpcap_if_info_t; /* diff --git a/capture/capture-pcap-util-int.h b/capture/capture-pcap-util-int.h index b148fb77..4a0ff7cd 100644 --- a/capture/capture-pcap-util-int.h +++ b/capture/capture-pcap-util-int.h @@ -22,17 +22,6 @@ extern GList *get_interface_list_findalldevs_ex(const char *hostname, #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_status *status, char **status_str); extern pcap_t *open_capture_device_local(capture_options *capture_opts, @@ -60,4 +49,12 @@ extern pcap_t *open_capture_device_pcap_open_live(interface_options *interface_o */ extern char *cant_get_if_list_error_message(const char *err_str); +/* + * Get a longer, secondary error message corrresponding to why getting + * capabilities or opening a device failed. This is used to let the error + * message string be platform-dependent. + */ +extern const char *get_pcap_failure_secondary_error_message(cap_device_open_status open_status, + const char *open_status_str); + #endif /* __PCAP_UTIL_INT_H__ */ diff --git a/capture/capture-pcap-util.c b/capture/capture-pcap-util.c index ac54bb36..b7f92f8e 100644 --- a/capture/capture-pcap-util.c +++ b/capture/capture-pcap-util.c @@ -407,17 +407,111 @@ if_info_get(const char *name) return if_info; } +if_addr_t * +if_addr_copy(const if_addr_t *addr) +{ + if_addr_t *new_addr = g_new(if_addr_t, 1); + new_addr->ifat_type = addr->ifat_type; + switch (addr->ifat_type) { + case IF_AT_IPv4: + new_addr->addr.ip4_addr = addr->addr.ip4_addr; + break; + case IF_AT_IPv6: + memcpy(new_addr->addr.ip6_addr, addr->addr.ip6_addr, sizeof(addr->addr)); + break; + default: + /* In case we add non-IP addresses */ + break; + } + return new_addr; +} + +static void* +if_addr_copy_cb(const void *data, void *user_data _U_) +{ + return if_addr_copy((if_addr_t*)data); +} + void if_info_free(if_info_t *if_info) { + if (if_info == NULL) { + return; + } 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); + if (if_info->caps) { + free_if_capabilities(if_info->caps); + } g_free(if_info); } +static void* +copy_linktype_cb(const void *data, void *user_data _U_) +{ + data_link_info_t *linktype_info = (data_link_info_t *)data; + + data_link_info_t *ret = g_new(data_link_info_t, 1); + ret->dlt = linktype_info->dlt; + ret->name = g_strdup(linktype_info->name); + ret->description = g_strdup(linktype_info->description); + return ret; +} + +static void* +copy_timestamp_cb(const void *data, void *user_data _U_) +{ + timestamp_info_t *timestamp_info = (timestamp_info_t *)data; + + timestamp_info_t *ret = g_new(timestamp_info_t, 1); + ret->name = g_strdup(timestamp_info->name); + ret->description = g_strdup(timestamp_info->description); + return ret; +} + +static if_capabilities_t * +if_capabilities_copy(const if_capabilities_t *caps) +{ + if (caps == NULL) return NULL; + + if_capabilities_t *ret = g_new(if_capabilities_t, 1); + ret->can_set_rfmon = caps->can_set_rfmon; + ret->data_link_types = g_list_copy_deep(caps->data_link_types, copy_linktype_cb, NULL); + ret->timestamp_types = g_list_copy_deep(caps->timestamp_types, copy_timestamp_cb, NULL); + ret->data_link_types_rfmon = g_list_copy_deep(caps->data_link_types_rfmon, copy_linktype_cb, NULL); + ret->primary_msg = g_strdup(caps->primary_msg); + ret->secondary_msg = caps->secondary_msg; + + return ret; +} + +if_info_t * +if_info_copy(const if_info_t *if_info) +{ + if_info_t *new_if_info; + new_if_info = g_new(if_info_t, 1); + new_if_info->name = g_strdup(if_info->name); + /* g_strdup accepts NULL as input and returns NULL. */ + new_if_info->friendly_name = g_strdup(if_info->friendly_name); + new_if_info->vendor_description = g_strdup(if_info->vendor_description); + new_if_info->addrs = g_slist_copy_deep(if_info->addrs, if_addr_copy_cb, NULL); + new_if_info->type = if_info->type; + new_if_info->loopback = if_info->loopback; + new_if_info->extcap = g_strdup(if_info->extcap); + new_if_info->caps = if_capabilities_copy(if_info->caps); + + return new_if_info; +} + +static void* +if_info_copy_cb(const void* data, void *user_data _U_) +{ + return if_info_copy((const if_info_t*)data); +} + if_info_t * if_info_new(const char *name, const char *description, bool loopback) { @@ -549,6 +643,7 @@ if_info_new(const char *name, const char *description, bool loopback) #endif if_info->loopback = loopback; if_info->addrs = NULL; + if_info->caps = NULL; return if_info; } @@ -739,8 +834,14 @@ free_interface_list(GList *if_list) g_list_free(if_list); } +GList* +interface_list_copy(GList *if_list) +{ + return g_list_copy_deep(if_list, if_info_copy_cb, NULL); +} + static void -free_linktype_cb(void * data, void * user_data _U_) +free_linktype_cb(void * data) { data_link_info_t *linktype_info = (data_link_info_t *)data; @@ -750,7 +851,7 @@ free_linktype_cb(void * data, void * user_data _U_) } static void -free_timestamp_cb(void * data, void * user_data _U_) +free_timestamp_cb(void * data) { timestamp_info_t *timestamp_info = (timestamp_info_t *)data; @@ -762,11 +863,12 @@ free_timestamp_cb(void * data, void * user_data _U_) 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_free_full(caps->data_link_types, free_linktype_cb); + g_list_free_full(caps->data_link_types_rfmon, free_linktype_cb); - g_list_foreach(caps->timestamp_types, free_timestamp_cb, NULL); - g_list_free(caps->timestamp_types); + g_list_free_full(caps->timestamp_types, free_timestamp_cb); + + g_free(caps->primary_msg); g_free(caps); } @@ -1079,17 +1181,15 @@ get_pcap_timestamp_types(pcap_t *pch _U_, char **err_str _U_) /* * 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. + * If this fails with PCAP_ERROR_TSTAMP_PRECISION_NOTSUP, that means + * that boring old microsecond-resolution time stamps are all that + * are supported, so we just live with that. */ -void +static int request_high_resolution_timestamp(pcap_t *pcap_h) { + int status; + #ifdef __APPLE__ /* * On macOS, if you build with a newer SDK, pcap_set_tstamp_precision() @@ -1115,16 +1215,29 @@ request_high_resolution_timestamp(pcap_t *pcap_h) 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); + if (p_pcap_set_tstamp_precision != NULL) { + status = (*p_pcap_set_tstamp_precision)(pcap_h, + PCAP_TSTAMP_PRECISION_NANO); + } else { + /* + * Older libpcap, which doesn't have support + * for setting the time stamp resolution. + */ + status = PCAP_ERROR_TSTAMP_PRECISION_NOTSUP; + } #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); + status = pcap_set_tstamp_precision(pcap_h, PCAP_TSTAMP_PRECISION_NANO); #endif /* __APPLE__ */ + if (status == PCAP_ERROR_TSTAMP_PRECISION_NOTSUP) { + /* This isn't a fatal error. */ + status = 0; + } + return status; } /* @@ -1263,13 +1376,11 @@ get_if_capabilities_pcap_create(interface_options *interface_opts, pcap_close(pch); return NULL; } - caps = (if_capabilities_t *)g_malloc(sizeof *caps); + caps = (if_capabilities_t *)g_malloc0(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 { /* * This "should not happen". @@ -1334,18 +1445,86 @@ get_if_capabilities_pcap_create(interface_options *interface_opts, pcap_close(pch); + if (caps->can_set_rfmon) { + /* This devices claims it can set rfmon. Get the capabilities + * when in monitor mode. We just succeeded above, so if we + * fail on anything here, just say that despite claims we + * can't actually set monitor mode. + */ + pch = pcap_create(interface_opts->name, errbuf); + if (pch == NULL) { + /* + * This "should not happen". + * It just succeeded above, what can this mean? + */ + return caps; + } + + status = pcap_set_rfmon(pch, 1); + if (status < 0) { + /* + * This "should not happen". + * It claims that monitor mode can be set, but + * there's an error when we try to do so. + */ +#if 0 + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + *open_status_str = ws_strdup_printf("pcap_set_rfmon() returned %d", + status); +#endif + caps->can_set_rfmon = false; + pcap_close(pch); + return caps; + } + + status = pcap_activate(pch); + if (status < 0) { + /* + * This "should not happen". (It just succeeded above.) + * pcap_set_rfmon didn't return an error, but it + * can't be activated after setting monitor mode? + */ + caps->can_set_rfmon = false; + pcap_close(pch); + return NULL; + } + + caps->data_link_types_rfmon = get_data_link_types(pch, + interface_opts, open_status, open_status_str); + if (caps->data_link_types == NULL) { + caps->can_set_rfmon = false; + } + + pcap_close(pch); + } + + *open_status = CAP_DEVICE_OPEN_NO_ERR; if (open_status_str != NULL) *open_status_str = NULL; return caps; } +static void +set_open_status_str(int status, pcap_t *pcap_h, + char (*open_status_str)[PCAP_ERRBUF_SIZE]) +{ + switch (status) { + + case PCAP_ERROR: + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; + + default: + (void) g_strlcpy(*open_status_str, pcap_statustostr(status), + sizeof *open_status_str); + break; + } +} + 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_status *open_status, char (*open_status_str)[PCAP_ERRBUF_SIZE]) @@ -1363,35 +1542,58 @@ open_capture_device_pcap_create( if (interface_opts->has_snaplen) { ws_debug("Calling pcap_set_snaplen() with snaplen %d.", interface_opts->snaplen); - pcap_set_snaplen(pcap_h, interface_opts->snaplen); + status = pcap_set_snaplen(pcap_h, interface_opts->snaplen); + if (status < 0) { + /* Error. */ + set_open_status_str(status, pcap_h, open_status_str); + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + pcap_close(pcap_h); + return NULL; + } } ws_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); + status = pcap_set_promisc(pcap_h, interface_opts->promisc_mode); + if (status < 0) { + /* Error. */ + set_open_status_str(status, pcap_h, open_status_str); + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + pcap_close(pcap_h); + return NULL; + } + status = pcap_set_timeout(pcap_h, timeout); + if (status < 0) { + /* Error. */ + set_open_status_str(status, pcap_h, open_status_str); + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + pcap_close(pcap_h); + return NULL; + } #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. + * Try to enable nanosecond-resolution capture; any code + * that can read pcapng files must be able to handle + * nanosecond-resolution time stamps. We think at this + * point that code that reads pcap files should recognize + * the nanosecond-resolution pcap file magic number. If + * it doesn't, we can downconvert via a program that + * uses libwiretap. * - * 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. + * nanosecond resolution timing), we just use the microsecond- + * resolution time stamps we get. */ - if (capture_opts->use_pcapng) - request_high_resolution_timestamp(pcap_h); + status = request_high_resolution_timestamp(pcap_h); + if (status < 0) { + /* Error. */ + set_open_status_str(status, pcap_h, open_status_str); + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + pcap_close(pcap_h); + return NULL; + } #endif /* HAVE_PCAP_SET_TSTAMP_PRECISION */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE @@ -1401,10 +1603,9 @@ open_capture_device_pcap_create( * XXX - what if it fails because that time stamp type * isn't supported? */ - if (status == PCAP_ERROR) { + if (status < 0) { + set_open_status_str(status, pcap_h, open_status_str); *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; - (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), - sizeof *open_status_str); pcap_close(pcap_h); return NULL; } @@ -1412,12 +1613,26 @@ open_capture_device_pcap_create( #endif /* HAVE_PCAP_SET_TSTAMP_PRECISION */ ws_debug("buffersize %d.", interface_opts->buffer_size); - if (interface_opts->buffer_size != 0) - pcap_set_buffer_size(pcap_h, + if (interface_opts->buffer_size != 0) { + status = pcap_set_buffer_size(pcap_h, interface_opts->buffer_size * 1024 * 1024); + if (status < 0) { + set_open_status_str(status, pcap_h, open_status_str); + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + pcap_close(pcap_h); + return NULL; + } + } ws_debug("monitor_mode %d.", interface_opts->monitor_mode); - if (interface_opts->monitor_mode) - pcap_set_rfmon(pcap_h, 1); + if (interface_opts->monitor_mode) { + status = pcap_set_rfmon(pcap_h, 1); + if (status < 0) { + set_open_status_str(status, pcap_h, open_status_str); + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + pcap_close(pcap_h); + return NULL; + } + } status = pcap_activate(pcap_h); ws_debug("pcap_activate() returned %d.", status); if (status < 0) { @@ -1530,7 +1745,7 @@ get_if_capabilities_pcap_open_live(interface_options *interface_opts, return NULL; } - caps = (if_capabilities_t *)g_malloc(sizeof *caps); + caps = (if_capabilities_t *)g_malloc0(sizeof *caps); caps->can_set_rfmon = false; caps->data_link_types = get_data_link_types(pch, interface_opts, open_status, open_status_str); @@ -1680,7 +1895,7 @@ get_if_capabilities(interface_options *interface_opts, return NULL; } - caps = (if_capabilities_t *)g_malloc(sizeof *caps); + caps = (if_capabilities_t *)g_malloc0(sizeof *caps); caps->can_set_rfmon = false; caps->data_link_types = NULL; deflt = get_pcap_datalink(pch, interface_opts->name); @@ -1810,6 +2025,239 @@ open_capture_device(capture_options *capture_opts, return pcap_h; } +/* + * Platform-dependent suggestions for fixing permissions. + */ + +#ifdef HAVE_LIBCAP + #define LIBCAP_PERMISSIONS_SUGGESTION \ + "\n\n" \ + "If you did not install Wireshark from a package, ensure that Dumpcap " \ + "has the needed CAP_NET_RAW and CAP_NET_ADMIN capabilities by running " \ + "\n\n" \ + " sudo setcap cap_net_raw,cap_net_admin=ep {path/to/}dumpcap" \ + "\n\n" \ + "and then restarting Wireshark." +#else + #define LIBCAP_PERMISSIONS_SUGGESTION +#endif + +#if defined(__linux__) + #define PLATFORM_PERMISSIONS_SUGGESTION \ + "\n\n" \ + "On Debian and Debian derivatives such as Ubuntu, if you have " \ + "installed Wireshark from a package, try running" \ + "\n\n" \ + " sudo dpkg-reconfigure wireshark-common" \ + "\n\n" \ + "selecting \"\" in response to the question" \ + "\n\n" \ + " Should non-superusers be able to capture packets?" \ + "\n\n" \ + "adding yourself to the \"wireshark\" group by running" \ + "\n\n" \ + " sudo usermod -a -G wireshark {your username}" \ + "\n\n" \ + "and then logging out and logging back in again." \ + LIBCAP_PERMISSIONS_SUGGESTION +#elif defined(__APPLE__) + #define PLATFORM_PERMISSIONS_SUGGESTION \ + "\n\n" \ + "If you installed Wireshark using the package from wireshark.org, " \ + "close this dialog and click on the \"installing ChmodBPF\" link in " \ + "\"You can fix this by installing ChmodBPF.\" on the main screen, " \ + "and then complete the installation procedure." +#else + #define PLATFORM_PERMISSIONS_SUGGESTION +#endif + +#if defined(_WIN32) +static const char * +get_platform_pcap_failure_secondary_error_message(const char *open_status_str) +{ + /* + * The error string begins with the error produced by WinPcap + * and Npcap if attempting to set promiscuous mode fails. + * (Note that this string could have a specific error message + * from an NDIS error after the initial part, so we do a prefix + * check rather than an exact match check.) + * + * If this is with Npcap 1.71 through 1.73, which have bugs that + * cause this error on Windows 11 with some drivers, suggest that + * the user upgrade to the current version of Npcap; + * otherwise, suggest that they turn off promiscuous mode + * on that device. + */ + static const char promisc_failed[] = + "failed to set hardware filter to promiscuous mode"; + + if (strncmp(open_status_str, promisc_failed, sizeof promisc_failed - 1) == 0) { + unsigned int npcap_major, npcap_minor; + + if (caplibs_get_npcap_version(&npcap_major, &npcap_minor)) { + if (npcap_major == 1 && + (npcap_minor >= 71 && npcap_minor <= 73)) { + return +"This is a bug in your version of Npcap.\n" +"\n" +"If you need to use promiscuous mode, you must upgrade to the current " +"version of Npcap, which is available from https://npcap.com/\n" +"\n" +"Otherwise, turn off promiscuous mode for this device."; + } + } + return + "Please turn off promiscuous mode for this device."; + } + return NULL; +} +#elif defined(__linux__) +static const char * +get_platform_pcap_failure_secondary_error_message(const char *open_status_str) +{ + /* + * The error string is the message provided by libpcap on + * Linux if an attempt to open a PF_PACKET socket failed + * with EAFNOSUPPORT. This probably means that either 1) + * the kernel doesn't have PF_PACKET support configured in + * or 2) this is a Flatpak version of Wireshark that's been + * sandboxed in a way that disallows opening PF_PACKET + * sockets. + * + * Suggest that the user find some other package of + * Wireshark if they want to capture traffic and are + * running a Flatpak of Wireshark or that they configure + * PF_PACKET support back in if it's configured out. + */ + static const char af_notsup[] = + "socket: Address family not supported by protocol"; + + if (strcmp(open_status_str, af_notsup) == 0) { + return + "If you are running Wireshark from a Flatpak package, " + "it does not support packet capture; you will need " + "to run a different version of Wireshark in order " + "to capture traffic.\n" + "\n" + "Otherwise, if your machine is running a kernel that " + "was not configured with CONFIG_PACKET, that kernel " + "does not support packet capture; you will need to " + "use a kernel configured with CONFIG_PACKET."; + } + return NULL; +} +#else +static const char * +get_platform_pcap_failure_secondary_error_message(const char *open_status_str _U_) +{ + /* No such message for platforms not handled above. */ + return NULL; +} +#endif + +const char * +get_pcap_failure_secondary_error_message(cap_device_open_status open_status, + const char *open_status_str) +{ + const char *platform_secondary_error_message; + +#ifdef _WIN32 + /* + * On Windows, first make sure they *have* Npcap installed. + */ + if (!has_wpcap) { + return + "In order to capture packets, Npcap or WinPcap must be installed. See\n" + "\n" + " https://npcap.com/\n" + "\n" + "for a downloadable version of Npcap and for instructions on how to\n" + "install it."; + } +#endif + + /* + * OK, now just return a largely platform-independent error that might + * have platform-specific suggestions at the end (for example, suggestions + * for how to get permission to capture). + */ + switch (open_status) { + + case CAP_DEVICE_OPEN_NO_ERR: + case CAP_DEVICE_OPEN_WARNING_PROMISC_NOTSUP: + case CAP_DEVICE_OPEN_WARNING_TSTAMP_TYPE_NOTSUP: + case CAP_DEVICE_OPEN_WARNING_OTHER: + /* This should not happen, as those aren't errors. */ + return ""; + + case CAP_DEVICE_OPEN_ERROR_NO_SUCH_DEVICE: + case CAP_DEVICE_OPEN_ERROR_RFMON_NOTSUP: + case CAP_DEVICE_OPEN_ERROR_IFACE_NOT_UP: + /* + * Not clear what suggestions to make for these cases. + */ + return ""; + + case CAP_DEVICE_OPEN_ERROR_PERM_DENIED: + case CAP_DEVICE_OPEN_ERROR_PROMISC_PERM_DENIED: + /* + * This is a permissions error, so no need to specify any other + * warnings. + */ + return + "Please check to make sure you have sufficient permissions." + PLATFORM_PERMISSIONS_SUGGESTION; + break; + + case CAP_DEVICE_OPEN_ERROR_OTHER: + case CAP_DEVICE_OPEN_ERROR_GENERIC: + /* + * We don't know what kind of error it is. See if there's a hint + * in the error string; if not, throw all generic suggestions at + * the user. + * + * First, check for some text that pops up in some errors. + * Do platform-specific checks first. + */ + platform_secondary_error_message = + get_platform_pcap_failure_secondary_error_message(open_status_str); + if (platform_secondary_error_message != NULL) { + /* We got one, so return it. */ + return platform_secondary_error_message; + } + + /* + * Not one of those particular problems. Was this a "generic" + * error from pcap_open_live() or pcap_open(), in which case + * it might be a permissions error? + */ + if (open_status == CAP_DEVICE_OPEN_ERROR_GENERIC) { + /* Yes. */ + return + "Please check to make sure you have sufficient permissions, and that you have " + "the proper interface or pipe specified." + PLATFORM_PERMISSIONS_SUGGESTION; + } else { + /* + * This is not a permissions error, so no need to suggest + * checking permissions. + */ + return + "Please check that you have the proper interface or pipe specified."; + } + break; + + default: + /* + * This is not a permissions error, so no need to suggest + * checking permissions. + */ + return + "Please check that you have the proper interface or pipe specified."; + break; + } +} + #endif /* HAVE_LIBPCAP */ /* diff --git a/capture/capture-wpcap.c b/capture/capture-wpcap.c index 4f7d1dca..792fef70 100644 --- a/capture/capture-wpcap.c +++ b/capture/capture-wpcap.c @@ -26,7 +26,7 @@ #include "capture/capture-wpcap.h" #include -bool has_wpcap = false; +bool has_wpcap; #ifdef HAVE_LIBPCAP diff --git a/capture/capture_ifinfo.c b/capture/capture_ifinfo.c index 45201b88..af1c136b 100644 --- a/capture/capture_ifinfo.c +++ b/capture/capture_ifinfo.c @@ -24,13 +24,16 @@ #include "capture/capture_sync.h" #include "extcap.h" +#include +#include #include #include +#include #ifdef HAVE_PCAP_REMOTE -static GList *remote_interface_list = NULL; +static GList *remote_interface_list; -static GList * append_remote_list(GList *iflist) +GList * append_remote_list(GList *iflist) { GSList *list; GList *rlist; @@ -68,26 +71,266 @@ static GList * append_remote_list(GList *iflist) } #endif +static if_capabilities_t * +deserialize_if_capability(char* data, jsmntok_t *inf_tok) +{ + if_capabilities_t *caps; + GList *linktype_list = NULL, *timestamp_list = NULL; + GList *linktype_rfmon_list = NULL; + int err, i; + char *val_s; + double val_d; + jsmntok_t *array_tok, *cur_tok; + + /* + * Allocate the interface capabilities structure. + */ + caps = (if_capabilities_t *)g_malloc0(sizeof *caps); + + if (inf_tok == NULL || !json_get_double(data, inf_tok, "status", &val_d)) { + ws_info("Capture Interface Capabilities failed with invalid JSON."); + caps->primary_msg = g_strdup("Dumpcap returned bad JSON"); + return caps; + } + + err = (int)val_d; + if (err != 0) { + caps->primary_msg = json_get_string(data, inf_tok, "primary_msg"); + if (caps->primary_msg) { + caps->primary_msg = g_strdup(caps->primary_msg); + caps->secondary_msg = get_pcap_failure_secondary_error_message(err, caps->primary_msg); + } else { + caps->primary_msg = g_strdup("Failed with no message"); + } + ws_info("Capture Interface Capabilities failed. Error %d, %s", + err, caps->primary_msg ? caps->primary_msg : "no message"); + return caps; + } + + bool rfmon; + if (!json_get_boolean(data, inf_tok, "rfmon", &rfmon)) { + ws_message("Capture Interface Capabilities returned bad information."); + ws_message("Didn't return monitor-mode cap"); + caps->primary_msg = g_strdup("Dumpcap didn't return monitor-mode capability"); + return caps; + } + + caps->can_set_rfmon = rfmon; + + /* + * The following are link-layer types. + */ + array_tok = json_get_array(data, inf_tok, "data_link_types"); + if (!array_tok) { + ws_info("Capture Interface Capabilities returned bad data_link information."); + caps->primary_msg = g_strdup("Dumpcap didn't return data link types capability"); + return caps; + } + for (i = 0; i < json_get_array_len(array_tok); i++) { + cur_tok = json_get_array_index(array_tok, i); + + if (!json_get_double(data, cur_tok, "dlt", &val_d)) { + continue; + } + + data_link_info_t *data_link_info; + data_link_info = g_new(data_link_info_t,1); + + data_link_info->dlt = (int)val_d; + val_s = json_get_string(data, cur_tok, "name"); + data_link_info->name = val_s ? g_strdup(val_s) : NULL; + val_s = json_get_string(data, cur_tok, "description"); + if (!val_s || strcmp(val_s, "(not supported)") == 0) { + data_link_info->description = NULL; + } else { + data_link_info->description = g_strdup(val_s); + } + linktype_list = g_list_append(linktype_list, data_link_info); + } + + if (rfmon) { + array_tok = json_get_array(data, inf_tok, "data_link_types_rfmon"); + + if (!array_tok) { + ws_info("Capture Interface Capabilities returned bad data_link information for monitor mode."); + caps->primary_msg = g_strdup("Dumpcap claimed that interface supported monitor mode, but didn't return data link types when in monitor mode"); + caps->can_set_rfmon = false; + } else for (i = 0; i < json_get_array_len(array_tok); i++) { + cur_tok = json_get_array_index(array_tok, i); + + if (!json_get_double(data, cur_tok, "dlt", &val_d)) { + continue; + } + + data_link_info_t *data_link_info; + data_link_info = g_new(data_link_info_t,1); + + data_link_info->dlt = (int)val_d; + val_s = json_get_string(data, cur_tok, "name"); + data_link_info->name = val_s ? g_strdup(val_s) : NULL; + val_s = json_get_string(data, cur_tok, "description"); + if (!val_s || strcmp(val_s, "(not supported)") == 0) { + data_link_info->description = NULL; + } else { + data_link_info->description = g_strdup(val_s); + } + linktype_rfmon_list = g_list_append(linktype_rfmon_list, data_link_info); + } + } + + array_tok = json_get_array(data, inf_tok, "timestamp_types"); + if (array_tok) { + for (i = 0; i < json_get_array_len(array_tok); i++) { + cur_tok = json_get_array_index(array_tok, i); + + timestamp_info_t *timestamp_info; + timestamp_info = g_new(timestamp_info_t,1); + val_s = json_get_string(data, cur_tok, "name"); + timestamp_info->name = val_s ? g_strdup(val_s) : NULL; + val_s = json_get_string(data, cur_tok, "description"); + timestamp_info->description = val_s ? g_strdup(val_s) : NULL; + + timestamp_list = g_list_append(timestamp_list, timestamp_info); + } + } + + caps->data_link_types = linktype_list; + /* Should be NULL if rfmon unsupported. */ + caps->data_link_types_rfmon = linktype_rfmon_list; + /* Might be NULL. Not all systems report timestamp types */ + caps->timestamp_types = timestamp_list; + + return caps; +} + +GList * +deserialize_interface_list(char *data, int *err, char **err_str) +{ + int i, j; + char *name, *addr; + char *val_s; + double val_d; + bool loopback; + if_info_t *if_info; + interface_type type; + if_addr_t *if_addr; + jsmntok_t *tokens, *if_tok, *addrs_tok, *cur_tok; + GList *if_list = NULL; + + if (data == NULL) { + ws_info("Passed NULL capture interface list"); + *err = CANT_GET_INTERFACE_LIST; + return if_list; + } + + int num_tokens = json_parse(data, NULL, 0); + if (num_tokens <= 0) { + ws_info("Capture Interface List failed with invalid JSON."); + if (err_str) { + *err_str = g_strdup("Dumpcap returned bad JSON."); + } + g_free(data); + *err = CANT_GET_INTERFACE_LIST; + return NULL; + } + + tokens = wmem_alloc_array(NULL, jsmntok_t, num_tokens); + if (json_parse(data, tokens, num_tokens) <= 0) { + ws_info("Capture Interface List failed with invalid JSON."); + if (err_str) { + *err_str = g_strdup("Dumpcap returned bad JSON."); + } + wmem_free(NULL, tokens); + g_free(data); + *err = CANT_GET_INTERFACE_LIST; + return NULL; + } + + for (i = 0; i < json_get_array_len(tokens); i++) { + if_tok = json_get_array_index(tokens, i); + if (if_tok && if_tok->type == JSMN_OBJECT) { + if_tok++; // Key + name = g_strndup(&data[if_tok->start], if_tok->end - if_tok->start); + if (!json_decode_string_inplace(name)) { + g_free(name); + continue; + } + if_tok++; + + if (!json_get_double(data, if_tok, "type", &val_d)) { + g_free(name); + continue; + } + type = (interface_type)val_d; + + if (!json_get_boolean(data, if_tok, "loopback", &loopback)) { + g_free(name); + continue; + } + + if_info = g_new0(if_info_t,1); + if_info->name = name; + val_s = json_get_string(data, if_tok, "friendly_name"); + if_info->friendly_name = g_strdup(val_s); + val_s = json_get_string(data, if_tok, "vendor_description"); + if_info->vendor_description = g_strdup(val_s); + if_info->type = type; + + addrs_tok = json_get_array(data, if_tok, "addrs"); + for (cur_tok = addrs_tok + 1, j = 0; j < json_get_array_len(addrs_tok); cur_tok++, j++) { + addr = g_strndup(&data[cur_tok->start], cur_tok->end - cur_tok->start); + if (json_decode_string_inplace(addr)) { + if_addr = g_new0(if_addr_t, 1); + if (ws_inet_pton4(addr, &if_addr->addr.ip4_addr)) { + if_addr->ifat_type = IF_AT_IPv4; + } else if (ws_inet_pton6(addr, (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); + } + } + g_free(addr); + } + + if_info->loopback = loopback; + + val_s = json_get_string(data, if_tok, "extcap"); + /* if_info->extcap is never NULL, unlike the friendly name + * and vendor description. (see if_info_new) + */ + if_info->extcap = val_s ? g_strdup(val_s) : ""; + + cur_tok = json_get_object(data, if_tok, "caps"); + if (cur_tok) { + if_info->caps = deserialize_if_capability(data, cur_tok); + } + + if_list = g_list_append(if_list, if_info); + } + } + + wmem_free(NULL, tokens); + g_free(data); + + return if_list; +} + /** * 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; - char *data, *primary_msg, *secondary_msg; - char **raw_list, **if_parts, **addr_parts; - char *name; - if_info_t *if_info; - if_addr_t *if_addr; + char *data, *primary_msg, *secondary_msg; *err = 0; if (err_str) { @@ -112,67 +355,12 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) * Add the extcap interfaces that can exist; they may exist * even if no native interfaces have been found. */ - ws_info("Loading External Capture Interface List ..."); + ws_debug("Loading External Capture Interface List ..."); if_list = append_extcap_interface_list(if_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); + if_list = deserialize_interface_list(data, err, err_str); #ifdef HAVE_PCAP_REMOTE /* Add the remote interface list */ @@ -182,14 +370,12 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) #endif /* Add the extcap interfaces after the native and remote interfaces */ - ws_info("Loading External Capture Interface List ..."); + ws_debug("Loading External Capture Interface List ..."); if_list = append_extcap_interface_list(if_list); 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 char *ifname, bool monitor_mode, const char *auth_string, @@ -197,19 +383,20 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode, void (*update_cb)(void)) { if_capabilities_t *caps; - GList *linktype_list = NULL, *timestamp_list = NULL; - int err, i; - char *data, *primary_msg, *secondary_msg; - char **raw_list; + int err; + char *data, *primary_msg, *secondary_msg; + jsmntok_t *tokens, *inf_tok; /* see if the interface is from extcap */ caps = extcap_get_if_dlts(ifname, err_primary_msg); - if (caps != NULL) + if (caps != NULL) { + /* return if the extcap interface generated an error */ + if (caps->primary_msg) { + free_if_capabilities(caps); + 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, @@ -228,100 +415,155 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode, 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); + int num_tokens = json_parse(data, NULL, 0); + if (num_tokens <= 0) { + ws_info("Capture Interface Capabilities failed with invalid JSON."); + if (err_primary_msg) { + *err_primary_msg = g_strdup("Dumpcap returned bad JSON."); + } + g_free(data); + return NULL; + } - /* - * First line is 0 if monitor mode isn't supported, 1 if it is. - */ - if (raw_list[0] == NULL || *raw_list[0] == '\0') { + tokens = wmem_alloc_array(NULL, jsmntok_t, num_tokens); + if (json_parse(data, tokens, num_tokens) <= 0) { ws_info("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); + wmem_free(NULL, tokens); + g_free(data); return NULL; } - /* - * Allocate the interface capabilities structure. - */ - caps = (if_capabilities_t *)g_malloc(sizeof *caps); - switch (*raw_list[0]) { + inf_tok = json_get_array_index(tokens, 0); + if (inf_tok && inf_tok->type == JSMN_OBJECT) { + inf_tok++; // Key + char *ifname2 = g_strndup(&data[inf_tok->start], inf_tok->end - inf_tok->start); + if (json_decode_string_inplace(ifname2) && g_strcmp0(ifname2, ifname) == 0) { + inf_tok++; + caps = deserialize_if_capability(data, inf_tok); + if (caps->primary_msg) { + if (err_primary_msg) { + *err_primary_msg = caps->primary_msg; + caps->primary_msg = NULL; + } + if (caps->secondary_msg && err_secondary_msg) { + *err_secondary_msg = g_strdup(caps->secondary_msg); + } + free_if_capabilities(caps); + caps = NULL; + } + } else if (err_primary_msg) { + *err_primary_msg = g_strdup("Dumpcap returned bad JSON."); + } + g_free(ifname2); + } else if (err_primary_msg) { + *err_primary_msg = g_strdup("Dumpcap returned bad JSON."); + } - case '0': - caps->can_set_rfmon = false; - break; + wmem_free(NULL, tokens); + g_free(data); - case '1': - caps->can_set_rfmon = true; - break; + return caps; +} - default: - ws_info("Capture Interface Capabilities returned bad information."); - if (err_primary_msg) { - *err_primary_msg = ws_strdup_printf("Dumpcap returned \"%s\" for monitor-mode capability", - raw_list[0]); - } - g_free(caps); - g_strfreev(raw_list); - return NULL; +static void +free_if_capabilities_cb(void *data) +{ + if (data != NULL) { + free_if_capabilities((if_capabilities_t*)data); } +} - /* - * 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; +GHashTable* +capture_get_if_list_capabilities(GList *if_cap_queries, + char **err_primary_msg, char **err_secondary_msg, + void (*update_cb)(void)) +{ + if_cap_query_t *query; + if_capabilities_t *caps; + GHashTable *caps_hash; + GList *local_queries = NULL; + int err, i; + char *data, *primary_msg, *secondary_msg; + jsmntok_t *tokens, *inf_tok; + + caps_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_if_capabilities_cb); + for (GList *li = if_cap_queries; li != NULL; li = g_list_next(li)) { + + query = (if_cap_query_t *)li->data; + /* see if the interface is from extcap */ + caps = extcap_get_if_dlts(query->name, NULL); + /* if the extcap interface generated an error, it was from extcap */ + if (caps != NULL) { + g_hash_table_replace(caps_hash, g_strdup(query->name), caps); + } else { + local_queries = g_list_prepend(local_queries, query); } + } - 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]); + if (local_queries == NULL) + return caps_hash; + + local_queries = g_list_reverse(local_queries); + + /* Try to get our interface list */ + err = sync_if_list_capabilities_open(local_queries, &data, + &primary_msg, &secondary_msg, update_cb); + g_list_free(local_queries); + if (err != 0) { + ws_info("Capture Interface Capabilities failed. Error %d, %s", + err, primary_msg ? primary_msg : "no message"); + if (err_primary_msg) + *err_primary_msg = primary_msg; else - data_link_info->description = NULL; - g_strfreev(lt_parts); + g_free(primary_msg); + if (err_secondary_msg) + *err_secondary_msg = secondary_msg; + else + g_free(secondary_msg); + return caps_hash; + } - linktype_list = g_list_append(linktype_list, data_link_info); + int num_tokens = json_parse(data, NULL, 0); + if (num_tokens <= 0) { + ws_info("Capture Interface Capabilities failed with invalid JSON."); + g_free(data); + return caps_hash; } - 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); + tokens = wmem_alloc_array(NULL, jsmntok_t, num_tokens); + if (json_parse(data, tokens, num_tokens) <= 0) { + ws_info("Capture Interface Capabilities returned no information."); + if (err_primary_msg) { + *err_primary_msg = g_strdup("Dumpcap returned no interface capability information"); + } + wmem_free(NULL, tokens); + g_free(data); + return caps_hash; + } + + char *ifname; + for (i = 0; i < json_get_array_len(tokens); i++) { + inf_tok = json_get_array_index(tokens, i); + if (inf_tok && inf_tok->type == JSMN_OBJECT) { + inf_tok++; // Key + ifname = g_strndup(&data[inf_tok->start], inf_tok->end - inf_tok->start); + if (!json_decode_string_inplace(ifname)) { + g_free(ifname); 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); + inf_tok++; + caps = deserialize_if_capability(data, inf_tok); + g_hash_table_replace(caps_hash, ifname, caps); } } - g_strfreev(raw_list); - - caps->data_link_types = linktype_list; - /* Might be NULL. Not all systems report timestamp types */ - caps->timestamp_types = timestamp_list; + wmem_free(NULL, tokens); + g_free(data); - return caps; + return caps_hash; } #ifdef HAVE_PCAP_REMOTE diff --git a/capture/capture_ifinfo.h b/capture/capture_ifinfo.h index 97df2363..021e1913 100644 --- a/capture/capture_ifinfo.h +++ b/capture/capture_ifinfo.h @@ -35,6 +35,21 @@ typedef enum { IF_VIRTUAL = 9 } interface_type; +/* + * "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 { + bool can_set_rfmon; /* true if can be put into monitor mode */ + GList *data_link_types; /* GList of data_link_info_t's */ + GList *data_link_types_rfmon; /* GList of data_link_info_t's */ + GList *timestamp_types; /* GList of timestamp_info_t's */ + int status; + char *primary_msg; /* If non-NULL, the query failed, and a message explaining why */ + const char *secondary_msg; /* An optional supplementary message */ +} if_capabilities_t; + /* * The list of interfaces returned by "get_interface_list()" is * a list of these structures. @@ -51,6 +66,7 @@ typedef struct { interface_type type; /* type of interface */ bool loopback; /* true if loopback, false otherwise */ char *extcap; /* extcap arguments, which present the data to call the extcap interface */ + if_capabilities_t *caps; } if_info_t; /* @@ -69,6 +85,8 @@ typedef struct { } addr; } if_addr_t; +extern GList *deserialize_interface_list(char *data, int *err, char **err_str); + /** * Return the list of interfaces. * @@ -83,6 +101,11 @@ extern GList *capture_interface_list(int *err, char **err_str, void (*update_cb) void free_interface_list(GList *if_list); +/** + * Deep copy an interface list + */ +GList * interface_list_copy(GList *if_list); + /** * Get an if_info_t for a particular interface. * (May require privilege, so should only be used by dumpcap.) @@ -94,16 +117,22 @@ extern if_info_t *if_info_get(const char *name); */ 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. +/** + * Deep copy an if_info_t. + */ +if_info_t *if_info_copy(const if_info_t *if_info); + +/** + * Deep copy an if_addr_t. */ +if_addr_t *if_addr_copy(const if_addr_t *if_addr); + typedef struct { - bool 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; + const char *name; + bool monitor_mode; + const char *auth_username; + const char *auth_password; +} if_cap_query_t; /* * Information about data link types. @@ -131,10 +160,22 @@ capture_get_if_capabilities(const char *devname, bool monitor_mode, char **err_primary_msg, char **err_secondary_msg, void (*update_cb)(void)); +/** + * Fetch the linktype list for the specified interface from a child process. + */ +extern GHashTable * +capture_get_if_list_capabilities(GList *if_cap_queries, + char **err_primary_msg, char **err_secondary_msg, + void (*update_cb)(void)); + void free_if_capabilities(if_capabilities_t *caps); +#ifdef HAVE_PCAP_REMOTE void add_interface_to_remote_list(if_info_t *if_info); +GList* append_remote_list(GList *iflist); +#endif + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/capture/capture_sync.c b/capture/capture_sync.c index 1af14187..2a5db8bc 100644 --- a/capture/capture_sync.c +++ b/capture/capture_sync.c @@ -21,6 +21,8 @@ #include +#include + #include #include @@ -101,6 +103,11 @@ static char *dummy_control_id; static const char *sync_pipe_signame(int); #endif +/* We use this pipe buffer size for both the sync message pipe and the + * data pipe. Ensure that it's large enough for the indicator and header + * plus maximum message size. + */ +#define PIPE_BUF_SIZE (SP_MAX_MSG_LEN+4) static gboolean sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session); static int sync_pipe_wait_for_child(ws_process_id fork_child, char **msgp); @@ -108,15 +115,7 @@ static void pipe_convert_header(const unsigned char *header, int header_len, cha static ssize_t pipe_read_block(GIOChannel *pipe_io, 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); -} +static void (*fetch_dumpcap_pid)(ws_process_id); void capture_session_init(capture_session *cap_session, capture_file *cf, @@ -156,7 +155,7 @@ void capture_process_finished(capture_session *cap_session) unsigned i; if (!extcap_session_stop(cap_session)) { - /* Atleast one extcap process did not fully finish yet, wait for it */ + /* At least one extcap process did not fully finish yet, wait for it */ return; } @@ -189,7 +188,7 @@ void capture_process_finished(capture_session *cap_session) } cap_session->closed(cap_session, message->str); - g_string_free(message, true); + g_string_free(message, TRUE); g_free(capture_opts->closed_msg); capture_opts->closed_msg = NULL; capture_opts->stop_after_extcaps = false; @@ -197,6 +196,8 @@ void capture_process_finished(capture_session *cap_session) /* Append an arg (realloc) to an argc/argv array */ /* (add a string pointer to a NULL-terminated array of string pointers) */ +/* XXX: For glib >= 2.68 we could use a GStrvBuilder. + */ static char ** sync_pipe_add_arg(char **args, int *argc, const char *arg) { @@ -219,6 +220,23 @@ sync_pipe_add_arg(char **args, int *argc, const char *arg) return args; } +/* Take a buffer from an SP_LOG_MSG from dumpcap and send it to our + * current logger. Keep this in sync with the format used in + * dumpcap_log_writer. (We might want to do more proper serialization + * of more than just the log level.) + */ +static void +sync_pipe_handle_log_msg(const char *buffer) { + const char *log_msg = NULL; + const char* end; + uint32_t level = 0; + + if (ws_strtou32(buffer, &end, &level) && end[0] == ':') { + log_msg = end + 1; + } + ws_log(LOG_DOMAIN_CAPCHILD, level, "%s", log_msg); +} + /* Initialize an argument list and add dumpcap to it. */ static char ** init_pipe_args(int *argc) { @@ -240,6 +258,18 @@ init_pipe_args(int *argc) { /* Make that the first argument in the argument list (argv[0]). */ argv = sync_pipe_add_arg(argv, argc, exename); + /* Tell dumpcap to log at the lowest level its domain (Capchild) is + * set to log in the main program. (It might be in the special noisy + * or debug filter, so we can't just check the overall level.) + */ + for (enum ws_log_level level = LOG_LEVEL_NOISY; level != _LOG_LEVEL_LAST; level++) { + if (ws_log_msg_is_active(LOG_DOMAIN_CAPCHILD, level)) { + argv = sync_pipe_add_arg(argv, argc, "--log-level"); + argv = sync_pipe_add_arg(argv, argc, ws_log_level_to_string(level)); + break; + } + } + /* sync_pipe_add_arg strdupes exename, so we should free our copy */ g_free(exename); @@ -269,17 +299,15 @@ pipe_io_cb(GIOChannel *pipe_io, GIOCondition condition _U_, void * user_data) * 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 - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ #define ARGV_NUMBER_LEN 24 -#define PIPE_BUF_SIZE 5120 static int #ifdef _WIN32 -sync_pipe_open_command(char* const argv[], int *data_read_fd, +sync_pipe_open_command(char **argv, int *data_read_fd, GIOChannel **message_read_io, int *signal_write_fd, ws_process_id *fork_child, GArray *ifaces, char **msg, void(*update_cb)(void)) #else -sync_pipe_open_command(char* const argv[], int *data_read_fd, +sync_pipe_open_command(char **argv, int *data_read_fd, GIOChannel **message_read_io, int *signal_write_fd _U_, ws_process_id *fork_child, GArray *ifaces _U_, char **msg, void(*update_cb)(void)) @@ -287,6 +315,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, { enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ int message_read_fd = -1; + char sync_id[ARGV_NUMBER_LEN]; #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 */ @@ -317,8 +346,9 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (!msg) { /* We can't return anything */ + g_strfreev(argv); #ifdef _WIN32 - g_string_free(args, true); + g_string_free(args, TRUE); #endif return -1; } @@ -335,6 +365,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, /* Couldn't create the message pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create sync pipe: %s", win32strerror(GetLastError())); + g_strfreev(argv); return -1; } @@ -348,6 +379,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, message_read_fd = _open_osfhandle( (intptr_t) sync_pipe[PIPE_READ], _O_BINARY); if (message_read_fd == -1) { *msg = ws_strdup_printf("Couldn't get C file handle for message read pipe: %s", g_strerror(errno)); + g_strfreev(argv); CloseHandle(sync_pipe[PIPE_READ]); CloseHandle(sync_pipe[PIPE_WRITE]); return -1; @@ -360,6 +392,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, /* Couldn't create the message pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create data pipe: %s", win32strerror(GetLastError())); + g_strfreev(argv); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); return -1; @@ -375,6 +408,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, *data_read_fd = _open_osfhandle( (intptr_t) data_pipe[PIPE_READ], _O_BINARY); if (*data_read_fd == -1) { *msg = ws_strdup_printf("Couldn't get C file handle for data read pipe: %s", g_strerror(errno)); + g_strfreev(argv); CloseHandle(data_pipe[PIPE_READ]); CloseHandle(data_pipe[PIPE_WRITE]); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ @@ -395,6 +429,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, /* Couldn't create the signal pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create signal pipe: %s", win32strerror(GetLastError())); + g_strfreev(argv); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); return -1; @@ -411,6 +446,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (signal_pipe_write_fd == -1) { /* Couldn't create the pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); + g_strfreev(argv); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); CloseHandle(signal_pipe); @@ -436,7 +472,25 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, si.hStdInput = NULL; /* handle for named pipe*/ si.hStdOutput = data_pipe[PIPE_WRITE]; } - si.hStdError = sync_pipe[PIPE_WRITE]; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + /* On Windows, "[a]n inherited handle refers to the same object in the child + * process as it does in the parent process. It also has the same value." + * https://learn.microsoft.com/en-us/windows/win32/procthread/inheritance + * When converted to a file descriptor (via _open_osfhandle), the fd + * value is not necessarily the same in the two processes, but the handle + * value can be shared. + * A HANDLE is a void* though "64-bit versions of Windows use 32-bit handles + * for interoperability... only the lower 32 bits are significant, so it is + * safe to truncate the handle... or sign-extend the handle" + * https://learn.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication + * So it should be fine to call PtrToLong instead of casting to intptr_t. + * https://learn.microsoft.com/en-us/windows/win32/WinProg64/rules-for-using-pointers + */ + int argc = g_strv_length(argv); + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + snprintf(sync_id, ARGV_NUMBER_LEN, "%ld", PtrToLong(sync_pipe[PIPE_WRITE])); + argv = sync_pipe_add_arg(argv, &argc, sync_id); #endif if (ifaces) { @@ -455,7 +509,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (si.hStdOutput && (si.hStdOutput != si.hStdInput)) { handles[i_handles++] = si.hStdOutput; } - handles[i_handles++] = si.hStdError; + handles[i_handles++] = sync_pipe[PIPE_WRITE]; if (ifaces) { for (j = 0; j < ifaces->len; j++) { interface_opts = &g_array_index(ifaces, interface_options, j); @@ -488,14 +542,16 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, } ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); - g_string_free(args, true); + g_strfreev(argv); + g_string_free(args, TRUE); g_free(handles); return -1; } *fork_child = pi.hProcess; /* We may need to store this and close it later */ CloseHandle(pi.hThread); - g_string_free(args, true); + g_strfreev(argv); + g_string_free(args, TRUE); g_free(handles); if (signal_write_fd != NULL) { @@ -506,6 +562,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (pipe(sync_pipe) < 0) { /* Couldn't create the message pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create sync pipe: %s", g_strerror(errno)); + g_strfreev(argv); return -1; } @@ -514,6 +571,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (pipe(data_pipe) < 0) { /* Couldn't create the data pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create data pipe: %s", g_strerror(errno)); + g_strfreev(argv); ws_close(sync_pipe[PIPE_READ]); ws_close(sync_pipe[PIPE_WRITE]); return -1; @@ -530,11 +588,16 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, 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]); + /* dumpcap should be running in capture child mode (hidden feature) */ +#ifndef DEBUG_CHILD + int argc = g_strv_length(argv); + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + snprintf(sync_id, ARGV_NUMBER_LEN, "%d", sync_pipe[PIPE_WRITE]); + argv = sync_pipe_add_arg(argv, &argc, sync_id); +#endif execv(argv[0], argv); - sync_pipe_write_int_msg(2, SP_EXEC_FAILED, errno); + sync_pipe_write_int_msg(sync_pipe[PIPE_WRITE], SP_EXEC_FAILED, errno); /* Exit with "_exit()", so that we don't close the connection to the X server (and cause stuff buffered up by our parent but @@ -546,6 +609,8 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, _exit(1); } + g_strfreev(argv); + if (fetch_dumpcap_pid && *fork_child > 0) fetch_dumpcap_pid(*fork_child); @@ -553,6 +618,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, *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 @@ -643,10 +709,11 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (capture_opts->ifaces->len > 1) argv = sync_pipe_add_arg(argv, &argc, "-t"); + argv = sync_pipe_add_arg(argv, &argc, "-F"); if (capture_opts->use_pcapng) - argv = sync_pipe_add_arg(argv, &argc, "-n"); + argv = sync_pipe_add_arg(argv, &argc, "pcapng"); else - argv = sync_pipe_add_arg(argv, &argc, "-P"); + argv = sync_pipe_add_arg(argv, &argc, "pcap"); if (capture_comments != NULL) { for (j = 0; j < capture_comments->len; j++) { @@ -696,6 +763,13 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, argv = sync_pipe_add_arg(argv, &argc, sring_num_files); } + if (capture_opts->print_file_names) { + char *print_name = g_strdup_printf("printname:%s", capture_opts->print_name_to); + argv = sync_pipe_add_arg(argv, &argc, "-b"); + argv = sync_pipe_add_arg(argv, &argc, print_name); + g_free(print_name); + } + if (capture_opts->has_nametimenum) { char nametimenum[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); @@ -767,16 +841,18 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, /* 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->descr != NULL) + { + /* Add a description for the interface to put into an IDB and + * use for the temporary filename. */ + argv = sync_pipe_add_arg(argv, &argc, "--ifdescr"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts->descr); + } + 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); @@ -853,14 +929,12 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, } } - /* dumpcap should be running in capture child mode (hidden feature) */ #ifndef DEBUG_CHILD - argv = sync_pipe_add_arg(argv, &argc, "-Z"); #ifdef _WIN32 + /* pass process id to dumpcap for named signal pipe */ + argv = sync_pipe_add_arg(argv, &argc, "--signal-pipe"); snprintf(control_id, ARGV_NUMBER_LEN, "%ld", 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 @@ -889,13 +963,11 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (ret == -1) { report_failure("%s", msg); g_free(msg); - free_argv(argv, argc); return false; } /* Parent process - read messages from the child process over the sync pipe. */ - free_argv(argv, argc); cap_session->fork_child_status = 0; cap_session->cap_data_info = cap_data; @@ -953,10 +1025,8 @@ sync_pipe_close_command(int *data_read_fd, GIOChannel *message_read_io, * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL, * must be freed with g_free(). */ -/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ -#define PIPE_BUF_SIZE 5120 static int -sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg, +sync_pipe_run_command_actual(char **argv, char **data, char **primary_msg, char **secondary_msg, void(*update_cb)(void)) { char *msg; @@ -964,7 +1034,7 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg GIOChannel *sync_pipe_read_io; ws_process_id fork_child; char *wait_msg; - char buffer[PIPE_BUF_SIZE+1] = {0}; + char *buffer = g_malloc(PIPE_BUF_SIZE + 1); ssize_t nread; char indicator; int32_t exec_errno = 0; @@ -982,6 +1052,7 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg *primary_msg = msg; *secondary_msg = NULL; *data = NULL; + g_free(buffer); return -1; } @@ -990,177 +1061,190 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(sync_pipe_read_io, &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 = ws_strdup_printf("%s\n\n%s", *primary_msg, wait_msg); - g_free(*primary_msg); - g_free(wait_msg); - *primary_msg = combined_msg; + do { + nread = pipe_read_block(sync_pipe_read_io, &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. */ + g_io_channel_unref(sync_pipe_read_io); + 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 = ws_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) { + *secondary_msg = NULL; + *data = NULL; + g_free(buffer); - case SP_EXEC_FAILED: - /* - * Exec of dumpcap failed. Get the errno for the failure. - */ - if (!ws_strtoi32(buffer, NULL, &exec_errno)) { - ws_warning("Invalid errno: %s", buffer); + return -1; } - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &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 { + /* we got a valid message block from the child, process it */ + switch(indicator) { + + case SP_EXEC_FAILED: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Exec of dumpcap failed. Get the errno for the failure. */ - *primary_msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", - g_strerror(exec_errno)); - *secondary_msg = NULL; - ret = -1; - } - *data = NULL; - break; - - case SP_ERROR_MSG: - /* - * Error from dumpcap; there will be a primary message and a - * secondary message. - */ - - /* convert primary message */ - pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((unsigned char*)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 */ + if (!ws_strtoi32(buffer, NULL, &exec_errno)) { + ws_warning("Invalid errno: %s", buffer); + } - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &fork_child, &msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - *primary_msg = msg; - *secondary_msg = NULL; - } else { + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &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 = ws_strdup_printf("Couldn't run dumpcap in child process: %s", + g_strerror(exec_errno)); + *secondary_msg = NULL; + ret = -1; + } + *data = NULL; + break; + + case SP_ERROR_MSG: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Error from dumpcap; there will be a primary message and a + * secondary message. */ - *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); - } + /* convert primary message */ + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((unsigned char*)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_io, - &fork_child, &msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - *primary_msg = msg; - *secondary_msg = NULL; - g_string_free(data_buf, true); + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &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; - } else { + break; + + case SP_LOG_MSG: /* - * Child process succeeded. + * Log from dumpcap; pass to our log */ - *primary_msg = NULL; - *secondary_msg = NULL; - *data = g_string_free(data_buf, false); - } - break; + sync_pipe_handle_log_msg(buffer); + 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); + } - default: - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &fork_child, &msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - *primary_msg = msg; - *secondary_msg = NULL; - } else { + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &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: /* - * Child process returned an unknown status. + * Pick up the child status. */ - *primary_msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", - indicator); - *secondary_msg = NULL; - ret = -1; + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &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 = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + *secondary_msg = NULL; + ret = -1; + } + *data = NULL; + break; } - *data = NULL; - break; - } + } while (indicator != SP_SUCCESS && ret != -1); + + g_free(buffer); return ret; } @@ -1168,7 +1252,7 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg * redirects to sync_pipe_run_command_actual() */ static int -sync_pipe_run_command(char* const argv[], char **data, char **primary_msg, +sync_pipe_run_command(char **argv, char **data, char **primary_msg, char **secondary_msg, void (*update_cb)(void)) { int ret, i; @@ -1233,22 +1317,14 @@ sync_interface_set_80211_chan(const char *iface, const char *freq, const char *t *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; } @@ -1286,13 +1362,7 @@ sync_interface_list_open(char **data, char **primary_msg, /* 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; } @@ -1340,13 +1410,51 @@ sync_if_capabilities_open(const char *ifname, bool monitor_mode, const char* aut 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; +} + +int +sync_if_list_capabilities_open(GList *if_queries, + char **data, char **primary_msg, + char **secondary_msg, void (*update_cb)(void)) +{ + int argc; + char **argv; + int ret; + if_cap_query_t *if_cap_query; + + ws_debug("sync_if_list_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; + } + + for (GList *li = if_queries; li != NULL; li = g_list_next(li)) { + if_cap_query = (if_cap_query_t*)li->data; + /* Ask for the interface capabilities */ + argv = sync_pipe_add_arg(argv, &argc, "-i"); + argv = sync_pipe_add_arg(argv, &argc, if_cap_query->name); + if (if_cap_query->monitor_mode) + argv = sync_pipe_add_arg(argv, &argc, "-I"); + if (if_cap_query->auth_username && if_cap_query->auth_password) { + char sauth[256]; + argv = sync_pipe_add_arg(argv, &argc, "-A"); + snprintf(sauth, sizeof(sauth), "%s:%s", + if_cap_query->auth_username, + if_cap_query->auth_password); + argv = sync_pipe_add_arg(argv, &argc, sauth); + } + } + argv = sync_pipe_add_arg(argv, &argc, "-L"); + argv = sync_pipe_add_arg(argv, &argc, "--list-time-stamp-types"); + + ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); return ret; } @@ -1355,16 +1463,18 @@ sync_if_capabilities_open(const char *ifname, bool monitor_mode, const char* aut * 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. + * If data is not NULL, then it will also be set to point to a JSON + * serialization of the list of local interfaces and their capabilities. */ int -sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **msg, void (*update_cb)(void)) +sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **data, char **msg, void (*update_cb)(void)) { int argc; char **argv; int ret; GIOChannel *message_read_io; char *wait_msg; - char buffer[PIPE_BUF_SIZE+1] = {0}; + char *buffer = g_malloc(PIPE_BUF_SIZE + 1); ssize_t nread; char indicator; int32_t exec_errno = 0; @@ -1380,28 +1490,34 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **m if (!argv) { *msg = g_strdup("We don't know where to find dumpcap."); + g_free(buffer); return -1; } /* Ask for the interface statistics */ argv = sync_pipe_add_arg(argv, &argc, "-S"); + /* If requested, ask for the interface list and capabilities. */ + if (data) { + argv = sync_pipe_add_arg(argv, &argc, "-D"); + argv = sync_pipe_add_arg(argv, &argc, "-L"); + } + #ifndef DEBUG_CHILD - argv = sync_pipe_add_arg(argv, &argc, "-Z"); #ifdef _WIN32 + argv = sync_pipe_add_arg(argv, &argc, "--signal-pipe"); ret = create_dummy_signal_pipe(msg); if (ret == -1) { + g_free(buffer); return -1; } 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_io, NULL, fork_child, NULL, msg, update_cb); - free_argv(argv, argc); if (ret == -1) { + g_free(buffer); return -1; } @@ -1410,126 +1526,163 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **m * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(message_read_io, &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); - g_io_channel_unref(message_read_io); - 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 = ws_strdup_printf("%s\n\n%s", *msg, wait_msg); - g_free(*msg); - g_free(wait_msg); - *msg = combined_msg; + do { + nread = pipe_read_block(message_read_io, &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. */ + g_io_channel_unref(message_read_io); + ws_close(*data_read_fd); + 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) + *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 = ws_strdup_printf("%s\n\n%s", *msg, wait_msg); + g_free(*msg); + g_free(wait_msg); + *msg = combined_msg; + } } + g_free(buffer); + return -1; } - return -1; - } - /* we got a valid message block from the child, process it */ - switch(indicator) { + /* we got a valid message block from the child, process it */ + switch(indicator) { - case SP_EXEC_FAILED: - /* - * Exec of dumpcap failed. Get the errno for the failure. - */ - if (!ws_strtoi32(buffer, NULL, &exec_errno)) { - ws_warning("Invalid errno: %s", buffer); - } - *msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", - g_strerror(exec_errno)); + case SP_EXEC_FAILED: + /* + * Exec of dumpcap failed. Get the errno for the failure. + */ + if (!ws_strtoi32(buffer, NULL, &exec_errno)) { + ws_warning("Invalid errno: %s", buffer); + } + *msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", + g_strerror(exec_errno)); - /* - * Pick up the child status. - */ - sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - ret = -1; - break; + /* + * Pick up the child status. + */ + char *close_msg = NULL; + sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, &close_msg); + /* + * Ignore the error from sync_pipe_close_command, presumably the one + * returned by the child is more pertinent to what went wrong. + */ + g_free(close_msg); + ret = -1; + break; - case SP_ERROR_MSG: - /* - * Error from dumpcap; there will be a primary message and a - * secondary message. - */ + case SP_ERROR_MSG: + /* + * Error from dumpcap; there will be a primary message and a + * secondary message. + */ - /* convert primary message */ - pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((unsigned char*)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 */ + /* convert primary message */ + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((unsigned char*)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_io, - fork_child, msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - } else { + ret = sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else if (ret == WS_EXIT_NO_INTERFACES) { + /* + * No interfaces were found. If that's not the + * result of an error when fetching the local + * interfaces, let the user know. + */ + *msg = g_strdup(primary_msg_text); + } 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; + } + g_free(buffer); + return ret; + + case SP_LOG_MSG: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Log from dumpcap; pass to our log */ - *msg = g_strdup(primary_msg_text); - ret = -1; - } - break; - - case SP_SUCCESS: - /* Close the message pipe. */ - g_io_channel_unref(message_read_io); - break; + sync_pipe_handle_log_msg(buffer); + break; - default: - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - if (ret == -1) { + case SP_IFACE_LIST: /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Dumpcap giving us the interface list */ - } else { + + /* convert primary message */ + if (data) { + *data = g_strdup(buffer); + } + break; + + case SP_SUCCESS: + /* Close the message pipe. */ + g_io_channel_unref(message_read_io); + break; + + default: /* - * Child process returned an unknown status. + * Pick up the child status. */ - *msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", - indicator); - ret = -1; + ret = sync_pipe_close_command(data_read_fd, message_read_io, + 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 = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + ret = -1; + } + break; } - break; - } + } while (indicator != SP_SUCCESS && ret != -1); + + g_free(buffer); return ret; } @@ -1680,14 +1833,13 @@ pipe_read_block(GIOChannel *pipe_io, char *indicator, int len, char *msg, 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)); - g_io_channel_read_chars(pipe_io, &msg[sizeof(header)], len-sizeof(header), &bytes_read, &err); + g_io_channel_read_chars(pipe_io, msg, len, &bytes_read, &err); if (err != NULL) { /* error */ ws_debug("read from pipe %p: error(%u): %s", pipe_io, err->code, err->message); g_clear_error(&err); } - *err_msg = ws_strdup_printf("Unknown message from dumpcap reading header, try to show it as a string: %s", - msg); + *err_msg = ws_strdup_printf("Message %c from dumpcap with length %d > buffer size %d! Partial message: %s", + *indicator, required, len, msg); return -1; } len = required; @@ -1716,7 +1868,7 @@ static gboolean sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) { int ret; - char buffer[SP_MAX_MSG_LEN+1] = {0}; + char *buffer = g_malloc(SP_MAX_MSG_LEN + 1); ssize_t nread; char indicator; int32_t exec_errno = 0; @@ -1775,6 +1927,7 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) } else { extcap_request_stop(cap_session); } + g_free(buffer); return false; } @@ -1801,6 +1954,7 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) "standard output", as the capture file. */ sync_pipe_stop(cap_session); cap_session->closed(cap_session, NULL); + g_free(buffer); return false; } break; @@ -1837,6 +1991,12 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) /* 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_LOG_MSG: + /* + * Log from dumpcap; pass to our log + */ + sync_pipe_handle_log_msg(buffer); + break; case SP_BAD_FILTER: { const char *message=NULL; uint32_t indx = 0; @@ -1870,6 +2030,7 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) break; } + g_free(buffer); return true; } diff --git a/capture/capture_sync.h b/capture/capture_sync.h index 7b3020cc..76e5e095 100644 --- a/capture/capture_sync.h +++ b/capture/capture_sync.h @@ -94,9 +94,14 @@ sync_if_capabilities_open(const char *ifname, bool monitor_mode, const char* aut char **data, char **primary_msg, char **secondary_msg, void (*update_cb)(void)); +extern int +sync_if_list_capabilities_open(GList *ifqueries, + char **data, char **primary_msg, + char **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, char **msg, void (*update_cb)(void)); +sync_interface_stats_open(int *read_fd, ws_process_id *fork_child, char **data, char **msg, void (*update_cb)(void)); /** Stop gathering statistics. */ extern int diff --git a/capture/iface_monitor.c b/capture/iface_monitor.c index f742a664..870761ca 100644 --- a/capture/iface_monitor.c +++ b/capture/iface_monitor.c @@ -17,6 +17,8 @@ /* * Linux with libnl. + * + * Use Netlink to get indications of new/removed intrfaces. */ #include @@ -181,6 +183,8 @@ iface_mon_stop(void) /* * macOS. + * + * Use a PF_SYSTEM socket to get indications of new/removed intrfaces. */ #include @@ -354,6 +358,176 @@ iface_mon_event(void) } } +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + +/* + * FreeBSD, NetBSD, OpenBSD, DragonFly BSD. + * + * Use a PF_ROUTE socket to get indications of new/removed intrfaces. + */ +#include +#include +#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) +{ +#ifdef RO_MSGFILTER + unsigned char msgfilter[] = { + RTM_IFANNOUNCE, + }; +#endif + + /* Create a socket of type PF_ROUTE to listen for events. */ + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s == -1) + return -errno; + +#ifdef RO_MSGFILTER + /* + * This OS supports filtering PF_ROUTE sockets for specific + * events; we only want interface announcement events. + * + * If this fails, we just live with extra events that we ignore, + * which we also do on platforms that don't support filtering. + */ + (void) setsockopt(s, PF_ROUTE, RO_MSGFILTER, &msgfilter, sizeof msgfilter); +#endif +#ifdef SO_RERROR + /* + * This OS supports getting error reports from recvmsg() if a + * receive buffer overflow occurs. If that happens, it means + * that we may have lost interface reports, so we should + * probably just refetch all interface data. + * + * If we can't get those error reports, we're out of luck. + */ + int n = 1; + (void) setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)); +#endif + + callback = cb; + return 0; +} + +void +iface_mon_stop(void) +{ + close(s); +} + +int +iface_mon_get_sock(void) +{ + return s; +} + +void +iface_mon_event(void) +{ + union msgbuf { + char buf[2048]; + struct rt_msghdr hd; + struct if_announcemsghdr ifan; + } msgbuf; + struct iovec iov[1]; + struct msghdr msg; + bool message_seen = false; + ssize_t received; + + iov[0].iov_base = &msgbuf; + iov[0].iov_len = sizeof msgbuf; + memset(&msg, 0, sizeof msg); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + while (!message_seen) { + received = recvmsg(s, &msg, 0); + if (received == -1) { + if (errno == ENOBUFS) { + /* + * Receive buffer overflow. Keep reading, + * to get any messages in the socket buffer. + * + * XXX - that means we may have lost indications; + * should there be a callback that indicates that + * all interface data should be refreshed? + */ + continue; + } else { + /* + * Other error - just ignore. + */ + return; + } + } + + /* + * We've seen a message. + */ + message_seen = true; + } + if (received != 0) { + /* + * XXX - should we check received, to make sure it's large + * enough? + */ + if (msgbuf.hd.rtm_version != RTM_VERSION) + return; + + switch (msgbuf.hd.rtm_type) { + + case RTM_IFANNOUNCE: + switch (msgbuf.ifan.ifan_what) { + + case IFAN_ARRIVAL: + /* + * A new interface has arrived. + * + * XXX - see comment about interface arrivals in the + * macOS section; it applies here as well. + */ + callback(msgbuf.ifan.ifan_name, 1, 1); + break; + + case IFAN_DEPARTURE: + /* + * An existing interface has been removed. + */ + callback(msgbuf.ifan.ifan_name, 0, 0); + break; + + default: + /* + * Ignore other notifications. + */ + break; + } + break; + + default: + /* + * Ignore other messages. + */ + break; + } + } +} + #else /* don't have something we support */ int diff --git a/capture/ws80211_utils.c b/capture/ws80211_utils.c index 2211f80c..f5070096 100644 --- a/capture/ws80211_utils.c +++ b/capture/ws80211_utils.c @@ -20,6 +20,8 @@ SPDX-License-Identifier: ISC #include #include +#include + #if defined(HAVE_LIBNL) && defined(HAVE_NL80211) #include #include @@ -1157,7 +1159,7 @@ int ws80211_set_fcs_validation(const char *name, enum ws80211_fcs_validation fcs return ret_val; } -static char *airpcap_conf_path = NULL; +static char *airpcap_conf_path; const char *ws80211_get_helper_path(void) { HKEY h_key = NULL; @@ -1165,7 +1167,7 @@ const char *ws80211_get_helper_path(void) 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); + DWORD ad_size = array_length(airpcap_dir_utf16); reg_ret = RegQueryValueEx(h_key, NULL, NULL, NULL, (LPBYTE) &airpcap_dir_utf16, &ad_size); -- cgit v1.2.3