diff --git a/core/libs/prx/libSceNet/CMakeLists.txt b/core/libs/prx/libSceNet/CMakeLists.txt index 84e0b910f..aa2b08ccd 100644 --- a/core/libs/prx/libSceNet/CMakeLists.txt +++ b/core/libs/prx/libSceNet/CMakeLists.txt @@ -10,6 +10,7 @@ if(BUILD_TESTING) add_test(NAME guest_sce_net COMMAND guest_sce_net_tests) set_tests_properties(guest_sce_net PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77) if(WIN32) + target_link_libraries(guest_sce_net_tests PRIVATE ws2_32) set_tests_properties(guest_sce_net PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index cce92d275..b1f0fe4fb 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -1,3 +1,12 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#include +#include +#endif #include "prx/libc/include/general/VabiMacros.hpp" #include "SceTypes.hpp" #include @@ -213,6 +222,35 @@ static void CheckAddressText(int family, const char* text) { static bool ipv6Unavailable = false; +static bool HostIpv6LoopbackUnavailable() { + const auto hostSocket = ::socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); +#ifdef _WIN32 + if (hostSocket == INVALID_SOCKET) { + const int error = WSAGetLastError(); + return error == WSAEAFNOSUPPORT || error == WSAEPROTONOSUPPORT; + } +#else + if (hostSocket < 0) return errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT; +#endif + + sockaddr_in6 loopback{}; + loopback.sin6_family = AF_INET6; + loopback.sin6_port = htons(9); + loopback.sin6_addr = in6addr_loopback; + const char payload = 0; + const auto sent = ::sendto(hostSocket, &payload, sizeof(payload), 0, + reinterpret_cast(&loopback), sizeof(loopback)); +#ifdef _WIN32 + const int error = sent < 0 ? WSAGetLastError() : 0; + closesocket(hostSocket); + return sent < 0 && (error == WSAEADDRNOTAVAIL || error == WSAENETUNREACH || error == WSAEHOSTUNREACH); +#else + const int error = sent < 0 ? errno : 0; + close(hostSocket); + return sent < 0 && (error == EADDRNOTAVAIL || error == ENETUNREACH || error == EHOSTUNREACH); +#endif +} + static void CheckUnspecifiedIpv6() { std::array unspecified{}; Require(sceNetInetPton(28, "::", unspecified.data()) == 1); @@ -239,7 +277,14 @@ static void CheckUnspecifiedIpv6() { Require(sceNetInetPton(28, "::1", address.data() + 8) == 1); const char payload[] = "IPv6 wildcard receive"; - Require(sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size()) == sizeof(payload)); + const auto sent = sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size()); + if (sent < 0 && *sceNetErrnoLoc() == 51 && HostIpv6LoopbackUnavailable()) { + Require(sceNetSocketClose(sender) == 0); + Require(sceNetSocketClose(receiver) == 0); + ipv6Unavailable = true; + return; + } + Require(sent == sizeof(payload)); char received[sizeof(payload)]{}; std::array peer{}; size = peer.size(); @@ -430,7 +475,7 @@ int main() { Require(sceNetCtlGetState(&state) == 0); Require(state == 0 || state == 3); if (ipv6Unavailable) { - std::puts("skipped the IPv6 socket checks, the host has no IPv6"); + std::puts("skipped the IPv6 socket checks, the host has no usable IPv6 loopback"); return 77; } }