From 4dd8c0ca9e56b573b8c50ce89051623837befa6f Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 9 Dec 2025 11:22:25 +0000 Subject: [PATCH] wifi scanner --- src/core/CMakeLists.txt | 8 +- .../internal_network/network_interface.cpp | 92 ++++++++++++++-- src/core/internal_network/wifi_scanner.cpp | 103 ++++++++---------- .../internal_network/wifi_scanner_dummy.cpp | 8 ++ 4 files changed, 139 insertions(+), 72 deletions(-) create mode 100644 src/core/internal_network/wifi_scanner_dummy.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6a8b9b93b3..4f81764f9c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1160,8 +1160,12 @@ add_library(core STATIC if (ENABLE_WIFI_SCAN) # find_package(libiw REQUIRED) - target_compile_definitions(core PRIVATE ENABLE_WIFI_SCAN) - target_link_libraries(core PRIVATE iw) + target_sources(core PRIVATE internal_network/wifi_scanner.cpp) + if (PLATFORM_LINUX) + target_link_libraries(core PRIVATE iw) + endif() +else() + target_sources(core PRIVATE internal_network/wifi_scanner_dummy.cpp) endif() if (WIN32) diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index be780d3127..753ee1c92a 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -9,21 +9,37 @@ #include #include -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "core/internal_network/emu_net_state.h" -#include "core/internal_network/network_interface.h" - #ifdef _WIN32 #include -#else +#elif defined(__linux__) || defined(__ANDROID__) #include #include #include +#elif defined(__FreeBSD__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/internal_network/emu_net_state.h" +#include "core/internal_network/network_interface.h" + namespace Network { #ifdef _WIN32 @@ -86,8 +102,8 @@ std::vector GetAvailableNetworkInterfaces() { #else std::vector GetAvailableNetworkInterfaces() { +#if defined(__ANDROID__) || defined(__linux__) struct ifaddrs* ifaddr = nullptr; - if (getifaddrs(&ifaddr) != 0) { LOG_ERROR(Network, "getifaddrs: {}", std::strerror(errno)); return {}; @@ -101,7 +117,7 @@ std::vector GetAvailableNetworkInterfaces() { u32 flags; }; std::vector routes{}; -#ifdef ANDROID +#ifdef __ANDROID__ // Even through Linux based, we can't reliably obtain routing information from there :( #else if (std::ifstream file("/proc/net/route"); file.is_open()) { @@ -140,6 +156,62 @@ std::vector GetAvailableNetworkInterfaces() { } freeifaddrs(ifaddr); return ifaces; +#elif defined(__FreeBSD__) + std::vector ifaces; + int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (fd < 0) { + LOG_ERROR(Network, "socket: {}", std::strerror(errno)); + return {}; + } + + size_t bufsz = 0; + int mib[6] = { + CTL_NET, PF_ROUTE, 0, + AF_UNSPEC, NET_RT_IFLIST, 0 + }; + if (::sysctl(mib, sizeof(mib) / sizeof(mib[0]), nullptr, &bufsz, nullptr, 0) < 0) { + LOG_ERROR(Network, "sysctl.1: {}", std::strerror(errno)); + return {}; + } + std::vector buf(bufsz); + if (::sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf.data(), &bufsz, nullptr, 0) < 0) { + LOG_ERROR(Network, "sysctl.2: {}", std::strerror(errno)); + return {}; + } + + struct rt_msghdr const *rtm = NULL; + for (char *next = buf.data(); next < buf.data() + bufsz; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr const *)next; + if (rtm->rtm_type == RTM_IFINFO) { + struct if_msghdr const* ifm = (struct if_msghdr const *)rtm; + size_t msglen = rtm->rtm_msglen - sizeof(*ifm); + char const* p = (char const*)(ifm + 1); + + Network::NetworkInterface iface{}; + for (size_t i = 0; i < RTAX_MAX; i++) + if ((ifm->ifm_addrs & (1 << i)) != 0) { + struct sockaddr const* sa = reinterpret_cast(p); + if (msglen == 0 || msglen < SA_SIZE(sa)) + break; + if (i == RTA_NETMASK && sa->sa_family == AF_LINK) { + size_t namelen = 0; + struct sockaddr_dl const* sdl = reinterpret_cast(sa); + ::link_ntoa_r(sdl, nullptr, &namelen); + iface.name = std::string(namelen, ' '); + ::link_ntoa_r(sdl, iface.name.data(), &namelen); + std::memcpy(&iface.ip_address, sa, sizeof(struct sockaddr_in)); + } + msglen -= SA_SIZE(sa); + p += SA_SIZE(sa); + } + ifaces.push_back(iface); + } + } + ::close(fd); + return ifaces; +#else + return {}; +#endif } #endif // _WIN32 diff --git a/src/core/internal_network/wifi_scanner.cpp b/src/core/internal_network/wifi_scanner.cpp index 127221099f..e8f8aa7534 100644 --- a/src/core/internal_network/wifi_scanner.cpp +++ b/src/core/internal_network/wifi_scanner.cpp @@ -6,11 +6,6 @@ #include #include -#include "common/logging/log.h" -#include "core/internal_network/wifi_scanner.h" - -using namespace std::chrono_literals; - #ifdef _WIN32 #define NOMINMAX #include @@ -18,16 +13,28 @@ using namespace std::chrono_literals; #ifdef _MSC_VER #pragma comment(lib, "wlanapi.lib") #endif +#elif defined(__linux__) && !defined(__ANDROID__) +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#include +#include #endif +#include "common/logging/log.h" +#include "core/internal_network/wifi_scanner.h" + +using namespace std::chrono_literals; + namespace Network { -#ifdef ENABLE_WIFI_SCAN #ifdef _WIN32 static u8 QualityToPercent(DWORD q) { - return static_cast(q); + return u8(q); } -static std::vector ScanWifiWin(std::chrono::milliseconds deadline) { +std::vector ScanWifiNetworks(std::chrono::milliseconds deadline) { std::vector out; HANDLE hClient{}; @@ -85,38 +92,16 @@ static std::vector ScanWifiWin(std::chrono::milliseconds dead WlanCloseHandle(hClient, nullptr); return out; } -#endif /* _WIN32 */ - -#if defined(__linux__) && !defined(_WIN32) && !defined(ANDROID) -#include - +#elif defined(__linux__) && !defined(__ANDROID__) static u8 QualityToPercent(const iwrange& r, const wireless_scan* ws) { const iw_quality qual = ws->stats.qual; const int lvl = qual.level; const int max = r.max_qual.level ? r.max_qual.level : 100; - return static_cast(std::clamp(100 * lvl / max, 0, 100)); -} - -static int wifi_callback(int skfd, char* ifname, char* args[], int count) -{ - iwrange range; - - int res = iw_get_range_info(skfd, ifname, &range); - - LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res); - - if (res >= 0) { - strncpy(args[0], ifname, IFNAMSIZ - 1); - args[0][IFNAMSIZ - 1] = 0; - - return 1; - } - - return 0; + return u8(std::clamp(100 * lvl / max, 0, 100)); } // TODO(crueter, Maufeat): Check if driver supports wireless extensions, fallback to nl80211 if not -static std::vector ScanWifiLinux(std::chrono::milliseconds deadline) { +std::vector ScanWifiNetworks(std::chrono::milliseconds deadline) { std::vector out; int sock = iw_sockets_open(); if (sock < 0) { @@ -127,7 +112,17 @@ static std::vector ScanWifiLinux(std::chrono::milliseconds de char ifname[IFNAMSIZ] = {0}; char *args[1] = {ifname}; - iw_enum_devices(sock, &wifi_callback, args, 0); + iw_enum_devices(sock, [](int skfd, char* ifname, char* args[], int count) -> int { + iwrange range; + int res = iw_get_range_info(skfd, ifname, &range); + LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res); + if (res >= 0) { + strncpy(args[0], ifname, IFNAMSIZ - 1); + args[0][IFNAMSIZ - 1] = 0; + return 1; + } + return 0; + }, args, 0); if (strlen(ifname) == 0) { LOG_WARNING(Network, "No wireless interface found"); @@ -153,20 +148,19 @@ static std::vector ScanWifiLinux(std::chrono::milliseconds de out.clear(); for (auto* ws = head.result; ws; ws = ws->next) { - if (!ws->b.has_essid) - continue; + if (ws->b.has_essid) { + Network::ScanData sd{}; + sd.ssid_len = static_cast(std::min(ws->b.essid_len, 0x20)); + std::memcpy(sd.ssid, ws->b.essid, sd.ssid_len); + sd.quality = QualityToPercent(range, ws); + sd.flags |= 1; + if (ws->b.has_key) + sd.flags |= 2; - Network::ScanData sd{}; - sd.ssid_len = static_cast(std::min(ws->b.essid_len, 0x20)); - std::memcpy(sd.ssid, ws->b.essid, sd.ssid_len); - sd.quality = QualityToPercent(range, ws); - sd.flags |= 1; - if (ws->b.has_key) - sd.flags |= 2; - - out.emplace_back(sd); - char tmp[0x22]{}; - std::memcpy(tmp, sd.ssid, sd.ssid_len); + out.emplace_back(sd); + char tmp[0x22]{}; + std::memcpy(tmp, sd.ssid, sd.ssid_len); + } } have = !out.empty(); } @@ -174,21 +168,10 @@ static std::vector ScanWifiLinux(std::chrono::milliseconds de iw_sockets_close(sock); return out; } -#endif /* linux */ -#endif - -std::vector ScanWifiNetworks(std::chrono::milliseconds deadline) { -#ifdef ENABLE_WIFI_SCAN -#if defined(_WIN32) - return ScanWifiWin(deadline); -#elif defined(__linux__) && !defined(ANDROID) - return ScanWifiLinux(deadline); -#else - return {}; // unsupported host, pretend no results -#endif #else +std::vector ScanWifiNetworks(std::chrono::milliseconds deadline) { return {}; // disabled, pretend no results -#endif } +#endif } // namespace Network diff --git a/src/core/internal_network/wifi_scanner_dummy.cpp b/src/core/internal_network/wifi_scanner_dummy.cpp new file mode 100644 index 0000000000..7dcd9e49a0 --- /dev/null +++ b/src/core/internal_network/wifi_scanner_dummy.cpp @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +namespace Network { +std::vector ScanWifiNetworks(std::chrono::milliseconds deadline) { + return {}; // disabled, pretend no results +} +} // namespace Network