From 6ddd82ff60f331b10fd83be4e4fa78d17fcf853c Mon Sep 17 00:00:00 2001 From: Sher Jan Date: Fri, 6 Nov 2026 11:40:57 +0500 Subject: [PATCH] fix(libSceNetCtl): read the STUN OTHER-ADDRESS attribute from 0x802c per RFC 5780 STUN_ATTR_OTHER_ADDRESS was 0x802b. RFC 5780 section 9.1 registers 0x802b as RESPONSE-ORIGIN and 0x802c as OTHER-ADDRESS (section 7.4 notes that OTHER-ADDRESS reuses the 0x0005 number of RFC 3489 CHANGED-ADDRESS, which stays accepted). ParseStunBindingResponse therefore ignored the alternate server a binding response advertised in OTHER-ADDRESS, and decoded a RESPONSE-ORIGIN (the address the response was sent from) as the alternate server. GuestNetCtl.cpp gains two cases: a response carrying OTHER-ADDRESS (0x802c) makes sceNetCtlGetNatInfo probe the advertised server, and a response carrying RESPONSE-ORIGIN (0x802b) makes it probe nothing. --- core/libs/prx/libSceNetCtl/Export.cpp | 2 +- core/libs/tests/GuestNetCtl.cpp | 45 +++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index 39fab2b0fb..cf9bb04870 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -70,7 +70,7 @@ static constexpr std::uint16_t STUN_ATTR_MAPPED_ADDRESS = 0x0001; static constexpr std::uint16_t STUN_ATTR_CHANGED_ADDRESS = 0x0005; static constexpr std::uint16_t STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020; static constexpr std::uint16_t STUN_ATTR_XOR_MAPPED_ADDRESS_LEGACY = 0x8020; -static constexpr std::uint16_t STUN_ATTR_OTHER_ADDRESS = 0x802b; +static constexpr std::uint16_t STUN_ATTR_OTHER_ADDRESS = 0x802c; static constexpr std::size_t STUN_HEADER_SIZE = 20; static constexpr std::size_t STUN_TRANSACTION_ID_SIZE = 12; static constexpr int STUN_DEFAULT_TIMEOUT_MS = 300; diff --git a/core/libs/tests/GuestNetCtl.cpp b/core/libs/tests/GuestNetCtl.cpp index 5da79b2fbf..f878b59ed0 100644 --- a/core/libs/tests/GuestNetCtl.cpp +++ b/core/libs/tests/GuestNetCtl.cpp @@ -418,7 +418,7 @@ StunTxId ReceiveStunBindingRequest(TestSocket serverSocket, sockaddr_in& client) return txId; } -void SendStunBindingResponse(TestSocket serverSocket, const sockaddr_in& client, const StunTxId& txId, std::uint32_t mappedAddr, std::uint16_t mappedHostPort, bool xorMapped, const sockaddr_in* otherServer = nullptr) { +void SendStunBindingResponse(TestSocket serverSocket, const sockaddr_in& client, const StunTxId& txId, std::uint32_t mappedAddr, std::uint16_t mappedHostPort, bool xorMapped, const sockaddr_in* otherServer = nullptr, std::uint16_t otherAttr = 0x0005) { const std::uint16_t effectivePort = mappedHostPort != 0 ? mappedHostPort : ntohs(client.sin_port); const std::uint32_t effectiveAddr = mappedAddr != 0 ? mappedAddr : client.sin_addr.s_addr; std::vector response(20, 0); @@ -429,7 +429,7 @@ void SendStunBindingResponse(TestSocket serverSocket, const sockaddr_in& client, std::memcpy(response.data() + 8, txId.data(), txId.size()); AppendStunAddressAttribute(response, xorMapped ? 0x0020 : 0x0001, effectiveAddr, effectivePort, xorMapped); if (otherServer) { - AppendStunAddressAttribute(response, 0x0005, otherServer->sin_addr.s_addr, ntohs(otherServer->sin_port), false); + AppendStunAddressAttribute(response, otherAttr, otherServer->sin_addr.s_addr, ntohs(otherServer->sin_port), false); } const auto attrLen = static_cast(response.size() - 20); response[2] = static_cast(attrLen >> 8); @@ -578,6 +578,47 @@ void CheckNatDiscovery(in_addr address) { Require(natInfo.nat_type == 3); Require(natInfo.mapped_addr == externalIp); + constexpr std::uint16_t kStunResponseOrigin = 0x802b; + constexpr std::uint16_t kStunOtherAddress = 0x802c; + + NetCtlSetStunServers_nid_no_patch(&server1Addr, 1, 1000); + StunTxId otherAddressTx1{}; + StunTxId otherAddressTx2{}; + bool otherAddressProbed = false; + std::thread otherAddressWorker([&] { + sockaddr_in firstClient{}; + otherAddressTx1 = ReceiveStunBindingRequest(server1, firstClient); + SendStunBindingResponse(server1, firstClient, otherAddressTx1, externalIp, 54321, false, &server2Addr, kStunOtherAddress); + if (WaitReadable(server2, 1500)) { + otherAddressProbed = true; + sockaddr_in secondClient{}; + otherAddressTx2 = ReceiveStunBindingRequest(server2, secondClient); + SendStunBindingResponse(server2, secondClient, otherAddressTx2, externalIp, 54322, false); + } + }); + natInfo = {sizeof(natInfo), 99, 99, 0}; + Require(sceNetCtlGetNatInfo(&natInfo) == 0); + otherAddressWorker.join(); + Require(otherAddressProbed); + RecordUniqueTransactionId(seenIds, otherAddressTx1); + RecordUniqueTransactionId(seenIds, otherAddressTx2); + Require(natInfo.stun_status == 2); + Require(natInfo.nat_type == 3); + Require(natInfo.mapped_addr == externalIp); + + NetCtlSetStunServers_nid_no_patch(&server1Addr, 1, 300); + StunTxId responseOriginTx{}; + std::thread responseOriginWorker([&] { + sockaddr_in client{}; + responseOriginTx = ReceiveStunBindingRequest(server1, client); + SendStunBindingResponse(server1, client, responseOriginTx, externalIp, 54321, false, &server2Addr, kStunResponseOrigin); + }); + natInfo = {sizeof(natInfo), 99, 99, 0}; + Require(sceNetCtlGetNatInfo(&natInfo) == 0); + responseOriginWorker.join(); + RecordUniqueTransactionId(seenIds, responseOriginTx); + Require(!WaitReadable(server2, 300)); + NetCtlSetStunServers_nid_no_patch(nullptr, 0, 0); CloseTestSocket(server1); CloseTestSocket(server2);