summaryrefslogtreecommitdiffstats
path: root/capture
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-19 04:14:26 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-19 04:14:26 +0000
commitc4e8a3222648fcf22ca207f1815ebbf7cd144eeb (patch)
tree93d5c6aa93d9987680dd1adad5685e2ad698f223 /capture
parentAdding upstream version 4.2.6. (diff)
downloadwireshark-c4e8a3222648fcf22ca207f1815ebbf7cd144eeb.tar.xz
wireshark-c4e8a3222648fcf22ca207f1815ebbf7cd144eeb.zip
Adding upstream version 4.4.0.upstream/4.4.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'capture')
-rw-r--r--capture/airpcap_loader.c10
-rw-r--r--capture/airpcap_loader.h12
-rw-r--r--capture/capture-pcap-util-int.h19
-rw-r--r--capture/capture-pcap-util.c550
-rw-r--r--capture/capture-wpcap.c2
-rw-r--r--capture/capture_ifinfo.c540
-rw-r--r--capture/capture_ifinfo.h57
-rw-r--r--capture/capture_sync.c805
-rw-r--r--capture/capture_sync.h7
-rw-r--r--capture/iface_monitor.c174
-rw-r--r--capture/ws80211_utils.c6
11 files changed, 1626 insertions, 556 deletions
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 \"<Yes>\" 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 <wsutil/feature_list.h>
-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 <capture/capture-pcap-util.h>
+#include <capture/capture-pcap-util-int.h>
#include <capture/capture_ifinfo.h>
#include <wsutil/inet_addr.h>
+#include <wsutil/wsjson.h>
#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
@@ -36,6 +36,21 @@ typedef enum {
} 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.
*
@@ -84,6 +102,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 <signal.h>
+#include <ws_exit_codes.h>
+
#include <wsutil/strtoi.h>
#include <wsutil/ws_assert.h>
@@ -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 <stdio.h>
@@ -181,6 +183,8 @@ iface_mon_stop(void)
/*
* macOS.
+ *
+ * Use a PF_SYSTEM socket to get indications of new/removed intrfaces.
*/
#include <stddef.h>
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+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 <glib.h>
#include <glib/gstdio.h>
+#include <wsutil/array.h>
+
#if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
#include <string.h>
#include <errno.h>
@@ -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);