diff options
Diffstat (limited to 'capture/capture-pcap-util.c')
-rw-r--r-- | capture/capture-pcap-util.c | 874 |
1 files changed, 746 insertions, 128 deletions
diff --git a/capture/capture-pcap-util.c b/capture/capture-pcap-util.c index cd837fbb9d..d8f6641d16 100644 --- a/capture/capture-pcap-util.c +++ b/capture/capture-pcap-util.c @@ -13,7 +13,7 @@ #ifdef HAVE_LIBPCAP -#include <glib.h> +#include <wireshark.h> #include <stdlib.h> #include <stdio.h> @@ -68,6 +68,11 @@ #include "capture/capture_ifinfo.h" #include "capture/capture-pcap-util.h" #include "capture/capture-pcap-util-int.h" +#ifdef _WIN32 +#include "capture/capture-wpcap.h" +#else +#define ws_pcap_findalldevs_ex pcap_findalldevs_ex +#endif #include <wsutil/file_util.h> #include <wsutil/please_report_bug.h> @@ -75,6 +80,8 @@ #ifndef _WIN32 #include <netinet/in.h> +#else +#include <ws2tcpip.h> #endif #ifdef _WIN32 @@ -88,6 +95,7 @@ #include <errno.h> #include <net/if.h> #include <sys/sockio.h> +#include <sys/ioctl.h> #endif /* @@ -231,7 +239,7 @@ add_unix_interface_ifinfo(if_info_t *if_info, const char *name, * Look for /sys/class/net/{device}/wireless. If it exists, * it's a wireless interface. */ - wireless_path = g_strdup_printf("/sys/class/net/%s/wireless", name); + wireless_path = ws_strdup_printf("/sys/class/net/%s/wireless", name); if (wireless_path != NULL) { if (ws_stat64(wireless_path, &statb) == 0) if_info->type = IF_WIRELESS; @@ -252,7 +260,7 @@ add_unix_interface_ifinfo(if_info_t *if_info, const char *name, if_info->type = IF_USB; } } -#else +#elif !defined(_WIN32) /* * On other UN*Xes, if there is a description, it's a friendly * name, and there is no vendor description. ("Other UN*Xes" @@ -386,7 +394,7 @@ if_info_get(const char *name) descr_size = sizeof (descr_prefix) + 10; description = g_malloc(descr_size); if (description != NULL) { - g_snprintf(description, descr_size, + snprintf(description, descr_size, "%s%ld", descr_prefix, busnum); } } @@ -394,24 +402,118 @@ if_info_get(const char *name) } #endif /* __FreeBSD__ */ #endif /* SIOCGIFDESCR */ - if_info = if_info_new(name, description, FALSE); + if_info = if_info_new(name, description, false); g_free(description); 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, gboolean loopback) +if_info_new(const char *name, const char *description, bool loopback) { if_info_t *if_info; #ifdef _WIN32 @@ -449,8 +551,8 @@ if_info_new(const char *name, const char *description, gboolean loopback) } else if (description && (strstr(description, "Wireless") != NULL || strstr(description,"802.11") != NULL)) { if_info->type = IF_WIRELESS; - } else if (description && strstr(description, "AirPcap") != NULL || - strstr(name, "airpcap") != NULL) { + } else if (description && (strstr(description, "AirPcap") != NULL || + strstr(name, "airpcap") != NULL)) { if_info->type = IF_AIRPCAP; } else if (description && strstr(description, "Bluetooth") != NULL ) { if_info->type = IF_BLUETOOTH; @@ -541,6 +643,7 @@ if_info_new(const char *name, const char *description, gboolean loopback) #endif if_info->loopback = loopback; if_info->addrs = NULL; + if_info->caps = NULL; return if_info; } @@ -557,8 +660,7 @@ if_info_add_address(if_info_t *if_info, struct sockaddr *addr) ai = (struct sockaddr_in *)(void *)addr; if_addr = (if_addr_t *)g_malloc(sizeof(*if_addr)); if_addr->ifat_type = IF_AT_IPv4; - if_addr->addr.ip4_addr = - *((guint32 *)&(ai->sin_addr.s_addr)); + if_addr->addr.ip4_addr = ai->sin_addr.s_addr; if_info->addrs = g_slist_prepend(if_info->addrs, if_addr); break; @@ -613,6 +715,19 @@ get_interface_list_findalldevs_ex(const char *hostname, const char *port, if (pcap_createsrcstr(source, PCAP_SRC_IFREMOTE, hostname, port, NULL, errbuf) == -1) { *err = CANT_GET_INTERFACE_LIST; + if (strcmp(errbuf, "not supported") == 0) { + /* + * macOS 14's pcap_createsrcstr(), which is a + * stub that always returns -1 with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + g_strlcpy(errbuf, "Remote capture not supported", + PCAP_ERRBUF_SIZE); + } if (err_str != NULL) *err_str = cant_get_if_list_error_message(errbuf); return NULL; @@ -622,8 +737,21 @@ get_interface_list_findalldevs_ex(const char *hostname, const char *port, auth.username = g_strdup(username); auth.password = g_strdup(passwd); - if (pcap_findalldevs_ex(source, &auth, &alldevs, errbuf) == -1) { + if (ws_pcap_findalldevs_ex(source, &auth, &alldevs, errbuf) == -1) { *err = CANT_GET_INTERFACE_LIST; + if (strcmp(errbuf, "not supported") == 0) { + /* + * macOS 14's pcap_findalldevs_ex(), which is a + * stub that always returns -1 with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + g_strlcpy(errbuf, "Remote capture not supported", + PCAP_ERRBUF_SIZE); + } if (err_str != NULL) *err_str = cant_get_if_list_error_message(errbuf); g_free(auth.username); @@ -645,7 +773,7 @@ get_interface_list_findalldevs_ex(const char *hostname, const char *port, for (dev = alldevs; dev != NULL; dev = dev->next) { if_info = if_info_new(dev->name, dev->description, - (dev->flags & PCAP_IF_LOOPBACK) ? TRUE : FALSE); + (dev->flags & PCAP_IF_LOOPBACK) ? true : false); il = g_list_append(il, if_info); if_info_ip(if_info, dev); } @@ -684,7 +812,7 @@ get_interface_list_findalldevs(int *err, char **err_str) for (dev = alldevs; dev != NULL; dev = dev->next) { if_info = if_info_new(dev->name, dev->description, - (dev->flags & PCAP_IF_LOOPBACK) ? TRUE : FALSE); + (dev->flags & PCAP_IF_LOOPBACK) ? true : false); il = g_list_append(il, if_info); if_info_ip(if_info, dev); } @@ -694,7 +822,7 @@ get_interface_list_findalldevs(int *err, char **err_str) } static void -free_if_cb(gpointer data, gpointer user_data _U_) +free_if_cb(void * data, void * user_data _U_) { if_info_free((if_info_t *)data); } @@ -706,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(gpointer data, gpointer user_data _U_) +free_linktype_cb(void * data) { data_link_info_t *linktype_info = (data_link_info_t *)data; @@ -717,7 +851,7 @@ free_linktype_cb(gpointer data, gpointer user_data _U_) } static void -free_timestamp_cb(gpointer data, gpointer user_data _U_) +free_timestamp_cb(void * data) { timestamp_info_t *timestamp_info = (timestamp_info_t *)data; @@ -729,11 +863,12 @@ free_timestamp_cb(gpointer data, gpointer 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); } @@ -877,7 +1012,7 @@ get_pcap_datalink(pcap_t *pch, } /* Set the data link type on a pcap. */ -gboolean +bool set_pcap_datalink(pcap_t *pcap_h, int datalink, char *name, char *errmsg, size_t errmsg_len, char *secondary_errmsg, size_t secondary_errmsg_len) @@ -885,22 +1020,22 @@ set_pcap_datalink(pcap_t *pcap_h, int datalink, char *name, char *set_datalink_err_str; if (datalink == -1) - return TRUE; /* just use the default */ + return true; /* just use the default */ if (pcap_set_datalink(pcap_h, datalink) == 0) - return TRUE; /* no error */ + return true; /* no error */ set_datalink_err_str = pcap_geterr(pcap_h); - g_snprintf(errmsg, (gulong) errmsg_len, "Unable to set data link type on interface '%s' (%s).", + snprintf(errmsg, errmsg_len, "Unable to set data link type on interface '%s' (%s).", name, set_datalink_err_str); /* * If the error isn't "XXX is not one of the DLTs supported by this device", * tell the user to tell the Wireshark developers about it. */ if (strstr(set_datalink_err_str, "is not one of the DLTs supported by this device") == NULL) - g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, + snprintf(secondary_errmsg, secondary_errmsg_len, "%s", please_report_bug()); else secondary_errmsg[0] = '\0'; - return FALSE; + return false; } static data_link_info_t * @@ -915,7 +1050,7 @@ create_data_link_info(int dlt) if (text != NULL) data_link_info->name = g_strdup(text); else - data_link_info->name = g_strdup_printf("DLT %d", dlt); + data_link_info->name = ws_strdup_printf("DLT %d", dlt); text = pcap_datalink_val_to_description(dlt); data_link_info->description = g_strdup(text); return data_link_info; @@ -945,20 +1080,26 @@ get_data_link_types(pcap_t *pch, interface_options *interface_opts, * PCAP_ERROR_NOT_ACTIVATED. and we should report * them properly. */ - if (nlt == PCAP_ERROR) { - *status = CAP_DEVICE_OPEN_ERR_GENERIC; - *status_str = g_strdup_printf("pcap_list_datalinks() failed: %s", + switch (nlt) { + + case PCAP_ERROR: + *status = CAP_DEVICE_OPEN_ERROR_OTHER; + *status_str = ws_strdup_printf("pcap_list_datalinks() failed: %s", pcap_geterr(pch)); - } else { - if (nlt == PCAP_ERROR_PERM_DENIED) - *status = CAP_DEVICE_OPEN_ERR_PERMISSIONS; - else - *status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; - *status_str = g_strdup(pcap_statustostr(nlt)); + break; + + default: + /* + * This "shouldn't happen". + */ + *status = CAP_DEVICE_OPEN_ERROR_OTHER; + *status_str = ws_strdup_printf("pcap_list_datalinks() failed: %s - %s", + pcap_statustostr(nlt), pcap_geterr(pch)); + break; } #else /* HAVE_PCAP_CREATE */ - *status = CAP_DEVICE_OPEN_ERR_GENERIC; - *status_str = g_strdup_printf("pcap_list_datalinks() failed: %s", + *status = CAP_DEVICE_OPEN_ERROR_OTHER; + *status_str = ws_strdup_printf("pcap_list_datalinks() failed: %s", pcap_geterr(pch)); #endif /* HAVE_PCAP_CREATE */ return NULL; @@ -1040,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() @@ -1067,51 +1206,64 @@ request_high_resolution_timestamp(pcap_t *pcap_h) * libpcap, and dlsym() to find a pointer to pcap_set_tstamp_precision(), * and if we find the pointer, call it. */ - static gboolean initialized = FALSE; + static bool initialized = false; static int (*p_pcap_set_tstamp_precision)(pcap_t *, int); if (!initialized) { p_pcap_set_tstamp_precision = (int (*)(pcap_t *, int)) dlsym(RTLD_NEXT, "pcap_set_tstamp_precision"); - initialized = TRUE; + initialized = true; + } + 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; } - if (p_pcap_set_tstamp_precision != NULL) - (*p_pcap_set_tstamp_precision)(pcap_h, PCAP_TSTAMP_PRECISION_NANO); #else /* __APPLE__ */ /* * On other UN*Xes we require that we be run on an OS version * with a libpcap equal to or later than the version with which * we were built. */ - pcap_set_tstamp_precision(pcap_h, PCAP_TSTAMP_PRECISION_NANO); + 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; } /* - * Return TRUE if the pcap_t in question is set up for high-precision - * time stamps, FALSE otherwise. + * Return true if the pcap_t in question is set up for high-precision + * time stamps, false otherwise. */ -gboolean +bool have_high_resolution_timestamp(pcap_t *pcap_h) { #ifdef __APPLE__ /* * See above. */ - static gboolean initialized = FALSE; + static bool initialized = false; static int (*p_pcap_get_tstamp_precision)(pcap_t *); if (!initialized) { p_pcap_get_tstamp_precision = (int (*)(pcap_t *)) dlsym(RTLD_NEXT, "pcap_get_tstamp_precision"); - initialized = TRUE; + initialized = true; } if (p_pcap_get_tstamp_precision != NULL) return (*p_pcap_get_tstamp_precision)(pcap_h) == PCAP_TSTAMP_PRECISION_NANO; else - return FALSE; /* Can't get implies couldn't set */ + return false; /* Can't get implies couldn't set */ #else /* __APPLE__ */ /* * On other UN*Xes we require that we be run on an OS version @@ -1126,7 +1278,7 @@ have_high_resolution_timestamp(pcap_t *pcap_h) #ifdef HAVE_PCAP_CREATE #ifdef HAVE_BONDING -static gboolean +static bool is_linux_bonding_device(const char *ifname) { int fd; @@ -1135,7 +1287,7 @@ is_linux_bonding_device(const char *ifname) fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) - return FALSE; + return false; memset(&ifr, 0, sizeof ifr); (void) g_strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); @@ -1144,23 +1296,23 @@ is_linux_bonding_device(const char *ifname) #if defined(SIOCBONDINFOQUERY) if (ioctl(fd, SIOCBONDINFOQUERY, &ifr) == 0) { close(fd); - return TRUE; + return true; } #else if (ioctl(fd, BOND_INFO_QUERY_OLD, &ifr) == 0) { close(fd); - return TRUE; + return true; } #endif close(fd); - return FALSE; + return false; } #else -static gboolean +static bool is_linux_bonding_device(const char *ifname _U_) { - return FALSE; + return false; } #endif @@ -1175,7 +1327,7 @@ get_if_capabilities_pcap_create(interface_options *interface_opts, pch = pcap_create(interface_opts->name, errbuf); if (pch == NULL) { - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; *open_status_str = g_strdup(errbuf); return NULL; } @@ -1195,30 +1347,46 @@ get_if_capabilities_pcap_create(interface_options *interface_opts, } if (status < 0) { /* Error. */ - if (status == PCAP_ERROR) { - *open_status = CAP_DEVICE_OPEN_ERR_GENERIC; - *open_status_str = g_strdup_printf("pcap_can_set_rfmon() failed: %s", + switch (status) { + + case PCAP_ERROR_NO_SUCH_DEVICE: + *open_status = CAP_DEVICE_OPEN_ERROR_NO_SUCH_DEVICE; + *open_status_str = ws_strdup_printf("pcap_can_set_rfmon() failed: %s", pcap_geterr(pch)); - } else { - if (status == PCAP_ERROR_PERM_DENIED) - *open_status = CAP_DEVICE_OPEN_ERR_PERMISSIONS; - else - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; - *open_status_str = g_strdup(pcap_statustostr(status)); + break; + + case PCAP_ERROR_PERM_DENIED: + *open_status = CAP_DEVICE_OPEN_ERROR_PERM_DENIED; + *open_status_str = ws_strdup_printf("pcap_can_set_rfmon() failed: %s", + pcap_geterr(pch)); + break; + + case PCAP_ERROR: + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + *open_status_str = ws_strdup_printf("pcap_can_set_rfmon() failed: %s", + pcap_geterr(pch)); + break; + + default: + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + *open_status_str = ws_strdup_printf("pcap_can_set_rfmon() failed: %s - %s", + pcap_statustostr(status), pcap_geterr(pch)); + break; } 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; + caps->can_set_rfmon = false; else if (status == 1) { - caps->can_set_rfmon = TRUE; - if (interface_opts->monitor_mode) - pcap_set_rfmon(pch, 1); + caps->can_set_rfmon = true; } else { - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; - *open_status_str = g_strdup_printf("pcap_can_set_rfmon() returned %d", + /* + * This "should not happen". + */ + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + *open_status_str = ws_strdup_printf("pcap_can_set_rfmon() returned %d", status); pcap_close(pch); g_free(caps); @@ -1228,16 +1396,37 @@ get_if_capabilities_pcap_create(interface_options *interface_opts, status = pcap_activate(pch); if (status < 0) { /* Error. */ - if (status == PCAP_ERROR) { - *open_status = CAP_DEVICE_OPEN_ERR_GENERIC; - *open_status_str = g_strdup_printf("pcap_activate() failed: %s", + switch (status) { + + case PCAP_ERROR_NO_SUCH_DEVICE: + *open_status = CAP_DEVICE_OPEN_ERROR_NO_SUCH_DEVICE; + *open_status_str = ws_strdup_printf("pcap_activate() failed: %s", pcap_geterr(pch)); - } else { - if (status == PCAP_ERROR_PERM_DENIED) - *open_status = CAP_DEVICE_OPEN_ERR_PERMISSIONS; - else - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; - *open_status_str = g_strdup(pcap_statustostr(status)); + break; + + case PCAP_ERROR_PERM_DENIED: + *open_status = CAP_DEVICE_OPEN_ERROR_PERM_DENIED; + *open_status_str = ws_strdup_printf("pcap_activate() failed: %s", + pcap_geterr(pch)); + break; + + case PCAP_ERROR_IFACE_NOT_UP: + *open_status = CAP_DEVICE_OPEN_ERROR_IFACE_NOT_UP; + *open_status_str = ws_strdup_printf("pcap_activate() failed: %s", + pcap_geterr(pch)); + break; + + case PCAP_ERROR: + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + *open_status_str = ws_strdup_printf("pcap_activate() failed: %s", + pcap_geterr(pch)); + break; + + default: + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + *open_status_str = ws_strdup_printf("pcap_activate() failed: %s - %s", + pcap_statustostr(status), pcap_geterr(pch)); + break; } pcap_close(pch); g_free(caps); @@ -1256,11 +1445,83 @@ 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) @@ -1279,18 +1540,39 @@ open_capture_device_pcap_create( pcap_h = pcap_create(interface_opts->name, *open_status_str); ws_debug("pcap_create() returned %p.", (void *)pcap_h); if (pcap_h == NULL) { - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; return NULL; } 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 /* @@ -1312,8 +1594,16 @@ open_capture_device_pcap_create( * nanosecond resolution timing), we just use microsecond- * resolution time stamps. */ - if (capture_opts->use_pcapng) - request_high_resolution_timestamp(pcap_h); + if (capture_opts->use_pcapng) { + 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 @@ -1323,10 +1613,9 @@ open_capture_device_pcap_create( * XXX - what if it fails because that time stamp type * isn't supported? */ - if (status == PCAP_ERROR) { - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; - (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), - sizeof *open_status_str); + 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; } @@ -1334,27 +1623,75 @@ 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) { /* Failed to activate, set to NULL */ - if (status == PCAP_ERROR) { - *open_status = CAP_DEVICE_OPEN_ERR_GENERIC; + switch (status) { + + case PCAP_ERROR_NO_SUCH_DEVICE: + *open_status = CAP_DEVICE_OPEN_ERROR_NO_SUCH_DEVICE; (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), sizeof *open_status_str); - } else { - if (status == PCAP_ERROR_PERM_DENIED) - *open_status = CAP_DEVICE_OPEN_ERR_PERMISSIONS; - else - *open_status = CAP_DEVICE_OPEN_ERR_NOT_PERMISSIONS; - (void) g_strlcpy(*open_status_str, pcap_statustostr(status), + break; + + case PCAP_ERROR_PERM_DENIED: + *open_status = CAP_DEVICE_OPEN_ERROR_PERM_DENIED; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; + +#ifdef HAVE_PCAP_ERROR_PROMISC_PERM_DENIED + case PCAP_ERROR_PROMISC_PERM_DENIED: + *open_status = CAP_DEVICE_OPEN_ERROR_PROMISC_PERM_DENIED; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; +#endif + + case PCAP_ERROR_RFMON_NOTSUP: + *open_status = CAP_DEVICE_OPEN_ERROR_RFMON_NOTSUP; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; + + case PCAP_ERROR_IFACE_NOT_UP: + *open_status = CAP_DEVICE_OPEN_ERROR_IFACE_NOT_UP; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; + + case PCAP_ERROR: + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), sizeof *open_status_str); + break; + + default: + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; + snprintf(*open_status_str, sizeof *open_status_str, + "%s - %s", pcap_statustostr(status), pcap_geterr(pcap_h)); + break; } pcap_close(pcap_h); return NULL; @@ -1364,13 +1701,33 @@ open_capture_device_pcap_create( * Warning. The call succeeded, but something happened * that the user might want to know. */ - *open_status = CAP_DEVICE_OPEN_WARNING_GENERIC; - if (status == PCAP_WARNING) { - g_snprintf(*open_status_str, sizeof *open_status_str, - "Warning: %s", pcap_geterr(pcap_h)); - } else { - g_snprintf(*open_status_str, sizeof *open_status_str, - "Warning: %s", pcap_statustostr(status)); + switch (status) { + + case PCAP_WARNING_PROMISC_NOTSUP: + *open_status = CAP_DEVICE_OPEN_WARNING_PROMISC_NOTSUP; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; + +#ifdef HAVE_PCAP_WARNING_TSTAMP_TYPE_NOTSUP + case PCAP_WARNING_TSTAMP_TYPE_NOTSUP: + *open_status = CAP_DEVICE_OPEN_WARNING_TSTAMP_TYPE_NOTSUP; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; +#endif + + case PCAP_WARNING: + *open_status = CAP_DEVICE_OPEN_WARNING_OTHER; + (void) g_strlcpy(*open_status_str, pcap_geterr(pcap_h), + sizeof *open_status_str); + break; + + default: + *open_status = CAP_DEVICE_OPEN_WARNING_OTHER; + snprintf(*open_status_str, sizeof *open_status_str, + "%s - %s", pcap_statustostr(status), pcap_geterr(pcap_h)); + break; } } else { /* @@ -1393,13 +1750,13 @@ get_if_capabilities_pcap_open_live(interface_options *interface_opts, pch = pcap_open_live(interface_opts->name, MIN_PACKET_SIZE, 0, 0, errbuf); if (pch == NULL) { - *open_status = CAP_DEVICE_OPEN_ERR_GENERIC; + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; *open_status_str = g_strdup(errbuf[0] == '\0' ? "Unknown error (pcap bug; actual error cause not reported)" : errbuf); return NULL; } - caps = (if_capabilities_t *)g_malloc(sizeof *caps); - caps->can_set_rfmon = FALSE; + caps = (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); if (caps->data_link_types == NULL) { @@ -1451,7 +1808,7 @@ open_capture_device_pcap_open_live(interface_options *interface_opts, interface_opts->promisc_mode, timeout, *open_status_str); ws_debug("pcap_open_live() returned %p.", (void *)pcap_h); if (pcap_h == NULL) { - *open_status = CAP_DEVICE_OPEN_ERR_GENERIC; + *open_status = CAP_DEVICE_OPEN_ERROR_OTHER; return NULL; } if ((*open_status_str)[0] != '\0') { @@ -1459,7 +1816,7 @@ open_capture_device_pcap_open_live(interface_options *interface_opts, * Warning. The call succeeded, but something happened * that the user might want to know. */ - *open_status = CAP_DEVICE_OPEN_WARNING_GENERIC; + *open_status = CAP_DEVICE_OPEN_WARNING_OTHER; } else { /* * No warning issued. @@ -1530,13 +1887,26 @@ get_if_capabilities(interface_options *interface_opts, * permission for their own remote account or will have * to use an account that *does* have permissions. */ - *status = CAP_DEVICE_OPEN_ERR_GENERIC; + *status = CAP_DEVICE_OPEN_ERROR_GENERIC; + if (strcmp(errbuf, "not supported") == 0) { + /* + * macOS 14's pcap_open(), which is a stub that + * always returns NULL with an error message of + * "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + g_strlcpy(errbuf, "Remote capture not supported", + PCAP_ERRBUF_SIZE); + } *status_str = g_strdup(errbuf[0] == '\0' ? "Unknown error (pcap bug; actual error cause not reported)" : errbuf); return NULL; } - caps = (if_capabilities_t *)g_malloc(sizeof *caps); - caps->can_set_rfmon = FALSE; + caps = (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); data_link_info = create_data_link_info(deflt); @@ -1620,7 +1990,22 @@ open_capture_device(capture_options *capture_opts, * or maybe we just have to ask politely for * permission.) */ - *open_status = CAP_DEVICE_OPEN_ERR_GENERIC; + *open_status = CAP_DEVICE_OPEN_ERROR_GENERIC; + if (strcmp(*open_status_str, "not supported") == 0) { + /* + * macOS 14's pcap_open(), which is a stub + * that always returns NULL with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + g_strlcpy(*open_status_str, + "Remote capture not supported", + PCAP_ERRBUF_SIZE); + } + /* Did pcap actually supply an error message? */ if ((*open_status_str)[0] == '\0') { /* @@ -1650,6 +2035,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 */ /* |