diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index e5a46dcab..74f79f1a4 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -528,6 +528,13 @@ int APS5_VABI sceNetSocket(const char* name, int family, int type, int protocol) if (!initialize_sockets()) return fail(5); const NativeSocket native = ::socket(family == NET_AF_INET ? AF_INET : AF_INET6, type, protocol); if (native == INVALID_NATIVE_SOCKET) return fail(native_error()); + const int ipv6_only = 1; + if (family == NET_AF_INET6 && + ::setsockopt(native, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&ipv6_only), sizeof(ipv6_only)) != 0) { + const int error = native_error(); + close_socket(native); + return fail(error); + } std::shared_ptr handle; try { handle = std::make_shared(native); diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 63817743b..1423b86db 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -506,6 +506,9 @@ int APS5_VABI socket_nid_postfix(int family, int type, int protocol) { const auto native = ::socket(family == 2 ? AF_INET : AF_INET6, nativeType, protocol); if (native == Invalid) return Fail(NativeError()); Socket guard(native, family, plainType); + const int ipv6Only = 1; + if (family == 28 && ::setsockopt(native, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&ipv6Only), sizeof(ipv6Only))) + return Fail(NativeError()); try { auto socket = std::make_shared(native, family, plainType); guard.value = Invalid; diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 993e40205..aeada8627 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -338,6 +338,25 @@ static void CheckUnspecifiedIpv6() { Require(sceNetSocketClose(receiver) == 0); } +static void CheckIpv6SocketsAreIpv6Only() { + const int inet = sceNetSocket("ipv4-any", 2, 2, 17); + Require(inet >= 0); + std::array any{16, 2}; + Require(sceNetBind_nid_postfix(inet, any.data(), any.size()) == 0); + std::uint32_t size = any.size(); + Require(sceNetGetsockname(inet, any.data(), &size) == 0 && size == any.size()); + const int inet6 = sceNetSocket("ipv6-any-same-port", 28, 2, 17); + Require(inet6 >= 0); + std::array any6{28, 28, any[2], any[3]}; + if (sceNetBind_nid_postfix(inet6, any6.data(), any6.size()) != 0) { + std::fprintf(stderr, "sceNetBind of [::]:%d failed with errno %d while an IPv4 socket holds 0.0.0.0:%d\n", + (any[2] << 8) | any[3], *sceNetErrnoLoc(), (any[2] << 8) | any[3]); + std::abort(); + } + Require(sceNetSocketClose(inet6) == 0); + Require(sceNetSocketClose(inet) == 0); +} + int main() { const auto epoch = std::chrono::steady_clock::time_point{}; Require(KernelSocketSelect::DeadlineAfter(epoch, 2, 250000) == epoch + std::chrono::milliseconds(2250)); @@ -359,6 +378,7 @@ int main() { CheckAddressText(28, "1234:5678:9abc:def0:1234:5678:9abc:def0"); CheckLinkLocalAllNodesIpv6(); CheckUnspecifiedIpv6(); + if (!ipv6Unavailable) CheckIpv6SocketsAreIpv6Only(); const int closed = sceNetSocket("closed-by-kernel", 2, 2, 17); Require(closed >= 0); diff --git a/core/libs/tests/GuestTcp.cpp b/core/libs/tests/GuestTcp.cpp index c6fb00256..b69116a4f 100644 --- a/core/libs/tests/GuestTcp.cpp +++ b/core/libs/tests/GuestTcp.cpp @@ -1,6 +1,7 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include #include #include @@ -47,8 +48,36 @@ static void StreamReceiveReportsNoSender() { for (const int descriptor : {client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0); } +static void ListenersShareAPortAcrossFamilies() { + const int inet = socket_nid_postfix(2, 1, 0); + Require(inet >= 0); + std::array any{16, 2}; + Require(bind_nid_postfix(inet, any.data(), any.size()) == 0 && listen_nid_postfix(inet, 4) == 0); + std::uint32_t size = any.size(); + Require(getsockname_nid_postfix(inet, any.data(), &size) == 0 && size == any.size()); + const int inet6 = socket_nid_postfix(28, 1, 0); + if (inet6 < 0) { + Require(*__error_nid_postfix() == 47); + Require(close_nid_postfix(inet) == 0); + return; + } + std::array any6{28, 28, any[2], any[3]}; + if (bind_nid_postfix(inet6, any6.data(), any6.size()) != 0) { + if (*__error_nid_postfix() != 49) { + std::fprintf(stderr, "bind of [::]:%d failed with errno %d while an IPv4 listener holds 0.0.0.0:%d\n", + (any[2] << 8) | any[3], *__error_nid_postfix(), (any[2] << 8) | any[3]); + std::abort(); + } + } else { + Require(listen_nid_postfix(inet6, 4) == 0); + } + Require(close_nid_postfix(inet6) == 0); + Require(close_nid_postfix(inet) == 0); +} + int main() { StreamReceiveReportsNoSender(); + ListenersShareAPortAcrossFamilies(); const int listener = socket_nid_postfix(2, 1, 6); Require(listener >= 0); std::array address{16, 2, 0, 0, 127, 0, 0, 1};