From e4276f2983cea8f459f1e572ff12e848a58252cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 19 Apr 2018 00:38:04 +0200 Subject: [PATCH 1/5] IOS/SO: Handle invalid GetInterfaceOpt requests --- Source/Core/Core/IOS/Network/IP/Top.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index a53d6d5baa..037cd95589 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -719,6 +719,12 @@ IPCCommandResult NetIPTop::HandleGetInterfaceOptRequest(const IOCtlVRequest& req const u32 param4 = Memory::Read_U32(request.io_vectors[1].address); u32 param5 = 0; + if (param != 0xfffe) + { + WARN_LOG(IOS_NET, "GetInterfaceOpt: received invalid request with param0=%08x", param); + return GetDefaultReply(-51); + } + if (request.io_vectors[0].size >= 8) { param5 = Memory::Read_U32(request.io_vectors[0].address + 4); From 25b198cac8b46f3fabf19b3ba48c055e9c348e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 19 Apr 2018 16:35:27 +0200 Subject: [PATCH 2/5] IOS/SO: Move default interface code into a separate function ...so that the function can be more easily reused. --- Source/Core/Core/IOS/Network/IP/Top.cpp | 133 +++++++++++++----------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index 037cd95589..2081b3a595 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,75 @@ static s32 MapWiiSockOptNameToNative(u32 optname) return optname; } +struct DefaultInterface +{ + u32 inet; ///< IPv4 address + u32 netmask; ///< IPv4 subnet mask + u32 broadcast; ///< IPv4 broadcast address +}; + +static std::optional GetSystemDefaultInterface() +{ +#ifdef _WIN32 + DWORD forwardTableSize, ipTableSize, result; + NET_IFINDEX ifIndex = NET_IFINDEX_UNSPECIFIED; + std::unique_ptr forwardTable; + std::unique_ptr ipTable; + + forwardTableSize = 0; + if (GetIpForwardTable(nullptr, &forwardTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) + { + forwardTable = + std::unique_ptr((PMIB_IPFORWARDTABLE) operator new(forwardTableSize)); + } + + ipTableSize = 0; + if (GetIpAddrTable(nullptr, &ipTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) + { + ipTable = std::unique_ptr((PMIB_IPADDRTABLE) operator new(ipTableSize)); + } + + // find the interface IP used for the default route and use that + result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); + // can return ERROR_MORE_DATA on XP even after the first call + while (result == NO_ERROR || result == ERROR_MORE_DATA) + { + for (DWORD i = 0; i < forwardTable->dwNumEntries; ++i) + { + if (forwardTable->table[i].dwForwardDest == 0) + { + ifIndex = forwardTable->table[i].dwForwardIfIndex; + break; + } + } + + if (result == NO_ERROR || ifIndex != NET_IFINDEX_UNSPECIFIED) + break; + + result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); + } + + if (ifIndex != NET_IFINDEX_UNSPECIFIED && + GetIpAddrTable(ipTable.get(), &ipTableSize, FALSE) == NO_ERROR) + { + for (DWORD i = 0; i < ipTable->dwNumEntries; ++i) + { + const auto& entry = ipTable->table[i]; + if (entry.dwIndex == ifIndex) + return DefaultInterface{entry.dwAddr, entry.dwMask, entry.dwBCastAddr}; + } + } +#endif + return {}; +} + +static DefaultInterface GetSystemDefaultInterfaceOrFallback() +{ + static constexpr DefaultInterface FALLBACK_VALUES{ + inet_addr(10, 0, 1, 30), inet_addr(255, 255, 255, 0), inet_addr(10, 0, 255, 255)}; + return GetSystemDefaultInterface().value_or(FALLBACK_VALUES); +} + IPCCommandResult NetIPTop::IOCtl(const IOCtlRequest& request) { if (Core::WantsDeterminism()) @@ -433,67 +503,8 @@ IPCCommandResult NetIPTop::HandleGetPeerNameRequest(const IOCtlRequest& request) IPCCommandResult NetIPTop::HandleGetHostIDRequest(const IOCtlRequest& request) { request.Log(GetDeviceName(), LogTypes::IOS_WC24); - - s32 return_value = 0; - -#ifdef _WIN32 - DWORD forwardTableSize, ipTableSize, result; - NET_IFINDEX ifIndex = NET_IFINDEX_UNSPECIFIED; - std::unique_ptr forwardTable; - std::unique_ptr ipTable; - - forwardTableSize = 0; - if (GetIpForwardTable(nullptr, &forwardTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) - { - forwardTable = - std::unique_ptr((PMIB_IPFORWARDTABLE) operator new(forwardTableSize)); - } - - ipTableSize = 0; - if (GetIpAddrTable(nullptr, &ipTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) - { - ipTable = std::unique_ptr((PMIB_IPADDRTABLE) operator new(ipTableSize)); - } - - // find the interface IP used for the default route and use that - result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); - // can return ERROR_MORE_DATA on XP even after the first call - while (result == NO_ERROR || result == ERROR_MORE_DATA) - { - for (DWORD i = 0; i < forwardTable->dwNumEntries; ++i) - { - if (forwardTable->table[i].dwForwardDest == 0) - { - ifIndex = forwardTable->table[i].dwForwardIfIndex; - break; - } - } - - if (result == NO_ERROR || ifIndex != NET_IFINDEX_UNSPECIFIED) - break; - - result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); - } - - if (ifIndex != NET_IFINDEX_UNSPECIFIED && - GetIpAddrTable(ipTable.get(), &ipTableSize, FALSE) == NO_ERROR) - { - for (DWORD i = 0; i < ipTable->dwNumEntries; ++i) - { - if (ipTable->table[i].dwIndex == ifIndex) - { - return_value = Common::swap32(ipTable->table[i].dwAddr); - break; - } - } - } -#endif - - // default placeholder, in case of failure - if (return_value == 0) - return_value = 192 << 24 | 168 << 16 | 1 << 8 | 150; - - return GetDefaultReply(return_value); + const DefaultInterface interface = GetSystemDefaultInterfaceOrFallback(); + return GetDefaultReply(Common::swap32(interface.inet)); } IPCCommandResult NetIPTop::HandleInetAToNRequest(const IOCtlRequest& request) From 3ce271991ba0c258eea1746b698f8ba472458c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 19 Apr 2018 19:02:15 +0200 Subject: [PATCH 3/5] IOS/SO: Implement GetSystemDefaultInterface for non-Windows This fixes gethostid on non-Windows platforms. Except on Android, where this is left unimplemented because Android does not support getifaddrs. --- Source/Core/Core/IOS/Network/IP/Top.cpp | 50 ++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index 2081b3a595..ce418cf7fe 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -22,6 +22,7 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/Network.h" +#include "Common/ScopeGuard.h" #include "Common/StringUtil.h" #include "Core/Core.h" @@ -37,16 +38,12 @@ #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -#elif defined(__linux__) or defined(__APPLE__) -#include -#include - -typedef struct pollfd pollfd_t; #else -#include +#include #include #include #include +#include #endif // WSAPoll doesn't support POLLPRI and POLLWRBAND flags @@ -212,6 +209,47 @@ static std::optional GetSystemDefaultInterface() return DefaultInterface{entry.dwAddr, entry.dwMask, entry.dwBCastAddr}; } } +#elif !defined(__ANDROID__) + // Assume that the address that is used to access the Internet corresponds + // to the default interface. + auto get_default_address = []() -> std::optional { + const int sock = socket(AF_INET, SOCK_DGRAM, 0); + Common::ScopeGuard sock_guard{[sock] { close(sock); }}; + + sockaddr_in addr{}; + socklen_t length = sizeof(addr); + addr.sin_family = AF_INET; + // The address is irrelevant -- no packet is actually sent. This just needs to be a public IP. + addr.sin_addr.s_addr = inet_addr(8, 8, 8, 8); + if (connect(sock, reinterpret_cast(&addr), sizeof(addr)) == -1) + return {}; + if (getsockname(sock, reinterpret_cast(&addr), &length) == -1) + return {}; + return addr.sin_addr; + }; + + auto get_addr = [](const sockaddr* addr) { + return reinterpret_cast(addr)->sin_addr.s_addr; + }; + + const auto default_interface_address = get_default_address(); + if (!default_interface_address) + return {}; + + ifaddrs* iflist; + if (getifaddrs(&iflist) != 0) + return {}; + Common::ScopeGuard iflist_guard{[iflist] { freeifaddrs(iflist); }}; + + for (const ifaddrs* iface = iflist; iface; iface = iface->ifa_next) + { + if (iface->ifa_addr && iface->ifa_addr->sa_family == AF_INET && + get_addr(iface->ifa_addr) == default_interface_address->s_addr) + { + return DefaultInterface{get_addr(iface->ifa_addr), get_addr(iface->ifa_netmask), + get_addr(iface->ifa_broadaddr)}; + } + } #endif return {}; } From 32d51f1699e90efd62c7f091690d6b0f2966e7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 19 Apr 2018 19:21:55 +0200 Subject: [PATCH 4/5] IOS/SO: Re-implement GetInterfaceOpt(0x4003) Use the newly added GetSystemDefaultInterfaceOrFallback() to return actual information for the default interface, not just dummy interface details. This also fixes GetInterfaceOpt(0x4003) and gethostid() returning inconsistent information. Prior to this change, GetInterfaceOpt(0x4003) would return 10.0.1.30 and gethostid would give a totally unrelated IP. --- Source/Core/Core/IOS/Network/IP/Top.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index ce418cf7fe..622cddbf67 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -884,11 +884,16 @@ IPCCommandResult NetIPTop::HandleGetInterfaceOptRequest(const IOCtlVRequest& req break; case 0x4003: // ip addr table + { + // XXX: this isn't exactly right; the buffer can be larger than 12 bytes, in which case + // SO can write 12 more bytes. Memory::Write_U32(0xC, request.io_vectors[1].address); - Memory::Write_U32(inet_addr(10, 0, 1, 30), request.io_vectors[0].address); - Memory::Write_U32(inet_addr(255, 255, 255, 0), request.io_vectors[0].address + 4); - Memory::Write_U32(inet_addr(10, 0, 255, 255), request.io_vectors[0].address + 8); + const DefaultInterface interface = GetSystemDefaultInterfaceOrFallback(); + Memory::Write_U32(Common::swap32(interface.inet), request.io_vectors[0].address); + Memory::Write_U32(Common::swap32(interface.netmask), request.io_vectors[0].address + 4); + Memory::Write_U32(Common::swap32(interface.broadcast), request.io_vectors[0].address + 8); break; + } case 0x4005: // hardcoded value Memory::Write_U32(0x20, request.io_vectors[0].address); From 393663a0a3c9d7f73c20803ceff781c330736252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 27 Apr 2018 23:35:17 +0200 Subject: [PATCH 5/5] IOS/SO: Use an enum for result codes --- Source/Core/Core/IOS/Network/IP/Top.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index 622cddbf67..81f6a1a5d7 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -59,6 +59,12 @@ namespace HLE { namespace Device { +enum SOResultCode : s32 +{ + SO_ERROR_INVALID_REQUEST = -51, + SO_ERROR_HOST_NOT_FOUND = -305, +}; + NetIPTop::NetIPTop(Kernel& ios, const std::string& device_name) : Device(ios, device_name) { #ifdef _WIN32 @@ -771,7 +777,7 @@ IPCCommandResult NetIPTop::HandleGetInterfaceOptRequest(const IOCtlVRequest& req if (param != 0xfffe) { WARN_LOG(IOS_NET, "GetInterfaceOpt: received invalid request with param0=%08x", param); - return GetDefaultReply(-51); + return GetDefaultReply(SO_ERROR_INVALID_REQUEST); } if (request.io_vectors[0].size >= 8) @@ -1022,8 +1028,7 @@ IPCCommandResult NetIPTop::HandleGetAddressInfoRequest(const IOCtlVRequest& requ } else { - // Host not found - ret = -305; + ret = SO_ERROR_HOST_NOT_FOUND; } request.Dump(GetDeviceName(), LogTypes::IOS_NET, LogTypes::LINFO);