145 lines
5.2 KiB
C++
145 lines
5.2 KiB
C++
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
#else
|
|
#include <ifaddrs.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
#include <mutex>
|
|
#ifdef _WIN32
|
|
#include <vector>
|
|
#endif
|
|
|
|
// Network-control state reflects host interface addressing, not PSN sign-in status.
|
|
static constexpr int SCE_NET_CTL_ERROR_CALLBACK_MAX = static_cast<int>(0x80412103);
|
|
static constexpr int SCE_NET_CTL_ERROR_INVALID_ID = static_cast<int>(0x80412105);
|
|
static constexpr int SCE_NET_CTL_ERROR_INVALID_ADDR = static_cast<int>(0x80412107);
|
|
static constexpr int SCE_NET_CTL_ERROR_NOT_CONNECTED = static_cast<int>(0x80412108);
|
|
static constexpr int NET_CTL_STATE_DISCONNECTED = 0;
|
|
static constexpr int NET_CTL_STATE_IPOBTAINED = 3;
|
|
static constexpr int MAX_CALLBACKS = 8;
|
|
|
|
static std::mutex g_callbackLock;
|
|
static NetCtlCallback g_callbacks[MAX_CALLBACKS] = {};
|
|
|
|
static bool HostHasAddress(int family) {
|
|
#ifdef _WIN32
|
|
static const int socket_status = [] {
|
|
WSADATA data{};
|
|
return WSAStartup(MAKEWORD(2, 2), &data);
|
|
}();
|
|
if (socket_status != 0) return false;
|
|
ULONG size = 0;
|
|
const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
|
if (GetAdaptersAddresses(family, flags, nullptr, nullptr, &size) != ERROR_BUFFER_OVERFLOW) return false;
|
|
std::vector<std::uint8_t> storage(size);
|
|
auto* adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(storage.data());
|
|
if (GetAdaptersAddresses(family, flags, nullptr, adapters, &size) != NO_ERROR) return false;
|
|
for (auto* adapter = adapters; adapter; adapter = adapter->Next) {
|
|
if (adapter->OperStatus != IfOperStatusUp || adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue;
|
|
for (auto* address = adapter->FirstUnicastAddress; address; address = address->Next) {
|
|
if (!address->Address.lpSockaddr) continue;
|
|
if (address->Address.lpSockaddr->sa_family == AF_INET) {
|
|
const auto* v4 = reinterpret_cast<const sockaddr_in*>(address->Address.lpSockaddr);
|
|
const auto host = ntohl(v4->sin_addr.s_addr);
|
|
if (host != 0 && (host >> 24) != 127) return true;
|
|
} else if (address->Address.lpSockaddr->sa_family == AF_INET6) {
|
|
const auto* v6 = reinterpret_cast<const sockaddr_in6*>(address->Address.lpSockaddr);
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(&v6->sin6_addr) && !IN6_IS_ADDR_LOOPBACK(&v6->sin6_addr)) return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
#else
|
|
ifaddrs* interfaces = nullptr;
|
|
if (getifaddrs(&interfaces) != 0) return false;
|
|
bool found = false;
|
|
for (auto* item = interfaces; item && !found; item = item->ifa_next) {
|
|
if (!item->ifa_addr || !(item->ifa_flags & IFF_UP) || (item->ifa_flags & IFF_LOOPBACK)) continue;
|
|
const int address_family = item->ifa_addr->sa_family;
|
|
if ((family == AF_UNSPEC || family == address_family) && address_family == AF_INET) {
|
|
const auto* v4 = reinterpret_cast<const sockaddr_in*>(item->ifa_addr);
|
|
const auto host = ntohl(v4->sin_addr.s_addr);
|
|
found = host != 0 && (host >> 24) != 127;
|
|
} else if ((family == AF_UNSPEC || family == address_family) && address_family == AF_INET6) {
|
|
const auto* v6 = reinterpret_cast<const sockaddr_in6*>(item->ifa_addr);
|
|
found = !IN6_IS_ADDR_UNSPECIFIED(&v6->sin6_addr) && !IN6_IS_ADDR_LOOPBACK(&v6->sin6_addr);
|
|
}
|
|
}
|
|
freeifaddrs(interfaces);
|
|
return found;
|
|
#endif
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
int APS5_VABI sceNetCtlCheckCallback(void) {
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlGetInfo(int code, NetCtlInfo* info) {
|
|
(void)code;
|
|
(void)info;
|
|
return SCE_NET_CTL_ERROR_NOT_CONNECTED;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlGetNatInfo(NetCtlNatInfo* nat_info) {
|
|
(void)nat_info;
|
|
return SCE_NET_CTL_ERROR_NOT_CONNECTED;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlGetResult(int event_type, int* error_code) {
|
|
(void)event_type;
|
|
if (!error_code) return SCE_NET_CTL_ERROR_INVALID_ADDR;
|
|
*error_code = 0;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlGetState(int* state) {
|
|
if (!state) return SCE_NET_CTL_ERROR_INVALID_ADDR;
|
|
*state = HostHasAddress(AF_UNSPEC) ? NET_CTL_STATE_IPOBTAINED : NET_CTL_STATE_DISCONNECTED;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlGetStateV6(int* state) {
|
|
if (!state) return SCE_NET_CTL_ERROR_INVALID_ADDR;
|
|
*state = HostHasAddress(AF_INET6) ? NET_CTL_STATE_IPOBTAINED : NET_CTL_STATE_DISCONNECTED;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlInit(void) {
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlRegisterCallback(NetCtlCallback func, void* arg, int* cid) {
|
|
if (!func || !cid) return SCE_NET_CTL_ERROR_INVALID_ADDR;
|
|
std::lock_guard lock(g_callbackLock);
|
|
for (int index = 0; index < MAX_CALLBACKS; ++index) {
|
|
if (!g_callbacks[index]) {
|
|
g_callbacks[index] = func;
|
|
*cid = index;
|
|
return 0;
|
|
}
|
|
}
|
|
return SCE_NET_CTL_ERROR_CALLBACK_MAX;
|
|
}
|
|
|
|
void APS5_VABI sceNetCtlTerm(void) {
|
|
}
|
|
|
|
int APS5_VABI sceNetCtlUnregisterCallback(int cid) {
|
|
if (cid < 0 || cid >= MAX_CALLBACKS) return SCE_NET_CTL_ERROR_INVALID_ID;
|
|
std::lock_guard lock(g_callbackLock);
|
|
g_callbacks[cid] = nullptr;
|
|
return 0;
|
|
}
|
|
|
|
}
|