From 895d8eb7fa87b8f23319acc4fd033f3c5000f54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 10:14:30 +0100 Subject: [PATCH 1/2] Try ANR2ME's sceNetInet implementation --- CMakeLists.txt | 2 - Core/Core.vcxproj | 2 - Core/Core.vcxproj.filters | 6 - Core/HLE/proAdhoc.h | 48 + Core/HLE/sceNet.cpp | 9 +- Core/HLE/sceNet.h | 4 - Core/HLE/sceNetInet.cpp | 3042 ++++++++++++++++----------- Core/HLE/sceNetInet.h | 696 ++++-- Core/HLE/sceNetResolver.cpp | 1 - Core/Net/InetSocket.cpp | 3 - Core/Net/InetSocket.h | 37 - UWP/CoreUWP/CoreUWP.vcxproj | 4 +- UWP/CoreUWP/CoreUWP.vcxproj.filters | 8 +- android/jni/Android.mk | 1 - libretro/Makefile.common | 1 - 15 files changed, 2395 insertions(+), 1469 deletions(-) delete mode 100644 Core/Net/InetSocket.cpp delete mode 100644 Core/Net/InetSocket.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c772d47e8cb9..9e50485cb319 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2388,8 +2388,6 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Net/InetCommon.h Core/Net/NetResolver.cpp Core/Net/NetResolver.h - Core/Net/InetSocket.cpp - Core/Net/InetSocket.h Core/MemFault.cpp Core/MemFault.h Core/MemMap.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 1c783385e53c..7cbfeb4c7c99 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -629,7 +629,6 @@ - @@ -1221,7 +1220,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index f0c86bbc6dc5..08996d93ac29 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1339,9 +1339,6 @@ Net - - Net - @@ -2157,9 +2154,6 @@ Net - - Net - diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 862126cc4608..b2abf3076ba3 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -69,6 +69,22 @@ #undef EALREADY #undef ETIMEDOUT #undef EOPNOTSUPP +#undef ENOTSOCK +#undef EPROTONOSUPPORT +#undef ESOCKTNOSUPPORT +#undef EPFNOSUPPORT +#undef EAFNOSUPPORT +#undef EINTR +#undef EACCES +#undef EFAULT +#undef EINVAL +#undef ENOSPC +#undef EHOSTDOWN +#undef EADDRINUSE +#undef EADDRNOTAVAIL +#undef ENETUNREACH +#undef EHOSTUNREACH +#undef ENETDOWN #define errno WSAGetLastError() #define ESHUTDOWN WSAESHUTDOWN #define ECONNABORTED WSAECONNABORTED @@ -83,6 +99,22 @@ #define EALREADY WSAEALREADY #define ETIMEDOUT WSAETIMEDOUT #define EOPNOTSUPP WSAEOPNOTSUPP +#define ENOTSOCK WSAENOTSOCK +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EINTR WSAEINTR +#define EACCES WSAEACCES +#define EFAULT WSAEFAULT +#define EINVAL WSAEINVAL +#define ENOSPC ERROR_INVALID_PARAMETER +#define EHOSTDOWN WSAEHOSTDOWN +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETUNREACH WSAENETUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#define ENETDOWN WSAENETDOWN inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEALREADY || errcode == WSAEINVAL); } // WSAEINVAL should be treated as WSAEALREADY during connect for backward-compatibility with Winsock 1.1 inline bool isDisconnected(int errcode) { return (errcode == WSAECONNRESET || errcode == WSAECONNABORTED || errcode == WSAESHUTDOWN); } #else @@ -1300,16 +1332,32 @@ int getActivePeerCount(const bool excludeTimedout = true); int getLocalIp(sockaddr_in * SocketAddress); uint32_t getLocalIp(int sock); +/* + * Check if an IP (big-endian/network order) is Private or Public IP + */ +bool isMulticastIP(uint32_t ip); + /* * Check if an IP (big-endian/network order) is Private or Public IP */ bool isPrivateIP(uint32_t ip); +/* + * Check if an IP (big-endian/network order) is APIPA(169.254.x.x) IP + */ +bool isAPIPA(uint32_t ip); + /* * Check if an IP (big-endian/network order) is Loopback IP */ bool isLoopbackIP(uint32_t ip); +/* + * Check if an IP (big-endian/network order) is a Broadcast IP + * Default subnet mask is 255.255.255.0 + */ +bool isBroadcastIP(uint32_t ip, const uint32_t subnetmask = 0x00ffffff); + /* * Get Number of bytes available in buffer to be Received * @param sock fd diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index d18f684f89c5..695db9356489 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -216,7 +216,7 @@ void __NetApctlInit() { static void __ResetInitNetLib() { netInited = false; - SceNetInet::Shutdown(); + netInetInited = false; // shouldn't this actually do something? memset(&netMallocStat, 0, sizeof(netMallocStat)); memset(¶meter, 0, sizeof(parameter)); @@ -275,7 +275,7 @@ void __NetShutdown() { Net_Term(); SceNetResolver::Shutdown(); - SceNetInet::Shutdown(); + __NetInetShutdown(); __NetApctlShutdown(); __ResetInitNetLib(); @@ -313,11 +313,9 @@ void __NetDoState(PointerWrap &p) { return; auto cur_netInited = netInited; - auto cur_netInetInited = SceNetInet::Inited(); + auto cur_netInetInited = netInetInited; auto cur_netApctlInited = netApctlInited; - bool netInetInited = cur_netInetInited; - Do(p, netInited); Do(p, netInetInited); Do(p, netApctlInited); @@ -370,6 +368,7 @@ void __NetDoState(PointerWrap &p) { if (p.mode == p.MODE_READ) { // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks netApctlInited = cur_netApctlInited; + netInetInited = cur_netInetInited; netInited = cur_netInited; // Discard leftover events diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index 3ae083f22fa5..d475bf5265d1 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -34,10 +34,6 @@ // On-Demand Nonblocking Flag // #define INET_MSG_DONTWAIT 0x80 -// Event Flags -#define INET_POLLRDNORM 0x0040 -#define INET_POLLWRNORM 0x0004 - // TODO: Determine how many handlers we can actually have const size_t MAX_APCTL_HANDLERS = 32; diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 100df863d453..f1691b70d6bd 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -1,1440 +1,2018 @@ -// Copyright (c) 2012- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ +#if defined(_WIN32) +#include "Common/CommonWindows.h" +#endif -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #if __linux__ || __APPLE__ || defined(__OpenBSD__) #include #include #include +#include #include #endif -#include "Common/Net/Resolve.h" +#include "Common/StringUtils.h" #include "Common/Data/Text/Parsers.h" - #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" -#include "Core/Config.h" +#include "Common/Serialize/SerializeMap.h" + #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceNet.h" +#include "Core/HLE/proAdhoc.h" // TODO: This is just for some defines +#include "Core/HLE/sceNetInet.h" +#include "Core/HLE/sceKernelMemory.h" #include "Core/MIPS/MIPS.h" +#include "Core/Config.h" +#include "Core/System.h" +#include "Core/ELF/ParamSFO.h" #include "Core/MemMapHelpers.h" +#include "Core/Util/PortManager.h" + +#include "sceKernel.h" +#include "sceKernelThread.h" +#include "sceKernelMutex.h" +#include "sceUtility.h" #include "Core/HLE/proAdhoc.h" +#include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" -#include "Core/HLE/sceNetInet.h" - -#include -#include -#include - #include "Core/HLE/sceNp.h" +#include "Core/HLE/sceNp2.h" #include "Core/Reporting.h" -// TODO: move Core/Net -#include "Core/Net/InetCommon.h" -#include "Core/Net/InetSocket.h" -#include "Core/Util/PortManager.h" - -#define SCENET Log::sceNet - -#if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE) -// Missing toolchain define -#define INADDR_NONE 0xFFFFFFFF -#elif PPSSPP_PLATFORM(WINDOWS) -#pragma comment(lib, "ws2_32.lib") -#define close closesocket -#define ERROR_WHEN_NONBLOCKING_CALL_OCCURS WSAEWOULDBLOCK -using netBufferType = char; -#else -#define ERROR_WHEN_NONBLOCKING_CALL_OCCURS EWOULDBLOCK -#include -using netBufferType = void; -#endif - -// TODO: socket domain -// TODO: socket level -// TODO: ignore reuseaddr (option) -// TODO: ignore port (option) -// TODO: ignore SO_NOSIGPIPE -// TODO: timeouts (PSP_NET_INET_SO_SNDTIMEO) - -/** - * @file sceNetInet.cpp - * - * @brief A shim for SceNetInet functions to operate over native POSIX sockets. These POSIX socket implementations are - * largely standarized and consistent between platforms with the exception of Windows which uses Winsock2 which itself - * is similar enough. - * - * Glossary: - * - Inet: Anything with is prefaced with Inet, regardless of case, is the PSP variant of the function / structure / etc. - * - * Standards: - * - C++-style implementations are preferred over C-style implementations when applicable (see SceFdSetOperations) for - * an example which implements fd_set and FD_* functions using C++-style code. - * - The last error is implemented within SceNetInet itself and is not a direct passthrough from the platforms errno - * implementation. - * - Invalid arguments (unmapped sockets, invalid options / flags etc) are mapped to EINVAL. - * - Invalid memory regions are mapped to EFAULT. - * - hleLogError should be used to return errors. - * - hleLogSuccess* should be used to return success, with optional messages. These message formattings have > 0 - * overhead so be mindful of their usage. - * - SceNetInet must have SetLastError called in all error cases except when SceNetInet itself is not initialized. - * - DEBUG_LOG should be used where it makes sense. - * - Comments must be left to indicate the intent of each section of each function. The comments should be short and - * concise while not mentioning any specific games or similar in particular. Mention the constraints that came from - * the game. - * - Explicit mappings should be used over implicit passthrough. Cases which are not known to work for PSP should be - * mapped to an EINVAL error unless it is demonstrated to work as expected. - * - It should not be possible for a game to crash the application via any shim; think what would happen if every - * parameter is randomized. - */ - -struct PspInetTimeval { - u32 tv_sec; /* Seconds. */ - u32 tv_usec; /* Microseconds. */ -}; - -class PspInetFdSetOperations { -public: - typedef long int fdMask; - static constexpr int gFdsBitsCount = 8 * static_cast(sizeof(fdMask)); +#include "Core/Instance.h" - struct FdSet { - fdMask mFdsBits[256 / gFdsBitsCount]; - }; +#ifndef MSG_NOSIGNAL +// Default value to 0x00 (do nothing) in systems where it's not supported. +#define MSG_NOSIGNAL 0x00 +#endif - static void Set(FdSet &sceFdSetBits, int socket) { - sceFdSetBits.mFdsBits[Position(socket)] |= ConvertToMask(socket); - } +int inetLastErrno = 0; // TODO: since errno can only be read once, we should keep track the value to be used on sceNetInetGetErrno +int inetLastSocket = -1; // A workaround to keep the most recent socket id for sceNetInetSelect, until we have a socket class wrapper - static bool IsSet(const FdSet &sceFdSetBits, int socket) { - return (sceFdSetBits.mFdsBits[Position(socket)] & ConvertToMask(socket)) != 0; - } +bool netInetInited = false; - static void Clear(FdSet &sceFdSetBits, int socket) { - sceFdSetBits.mFdsBits[Position(socket)] &= ~ConvertToMask(socket); - } - - static void Zero(FdSet &sceFdSetBits) { - memset(sceFdSetBits.mFdsBits, 0, sizeof(FdSet)); +int convertMsgFlagPSP2Host(int flag) { + switch (flag) { + case PSP_NET_INET_MSG_OOB: + return MSG_OOB; + case PSP_NET_INET_MSG_PEEK: + return MSG_PEEK; + case PSP_NET_INET_MSG_DONTROUTE: + return MSG_DONTROUTE; +#if defined(MSG_EOR) + case PSP_NET_INET_MSG_EOR: + return MSG_EOR; +#endif + case PSP_NET_INET_MSG_TRUNC: + return MSG_TRUNC; + case PSP_NET_INET_MSG_CTRUNC: + return MSG_CTRUNC; + case PSP_NET_INET_MSG_WAITALL: + return MSG_WAITALL; +#if defined(MSG_DONTWAIT) + case PSP_NET_INET_MSG_DONTWAIT: + return MSG_DONTWAIT; +#endif +#if defined(MSG_BCAST) + case PSP_NET_INET_MSG_BCAST: + return MSG_BCAST; +#endif +#if defined(MSG_MCAST) + case PSP_NET_INET_MSG_MCAST: + return MSG_MCAST; +#endif } + return hleLogError(Log::sceNet, flag, "Unknown MSG flag"); +} -private: - static int Position(const int socket) { - return socket / gFdsBitsCount; +int convertMsgFlagHost2PSP(int flag) { + switch (flag) { + case MSG_OOB: + return PSP_NET_INET_MSG_OOB; + case MSG_PEEK: + return PSP_NET_INET_MSG_PEEK; + case MSG_DONTROUTE: + return PSP_NET_INET_MSG_DONTROUTE; +#if defined(MSG_EOR) + case MSG_EOR: + return PSP_NET_INET_MSG_EOR; +#endif + case MSG_TRUNC: + return PSP_NET_INET_MSG_TRUNC; + case MSG_CTRUNC: + return PSP_NET_INET_MSG_CTRUNC; + case MSG_WAITALL: + return PSP_NET_INET_MSG_WAITALL; +#if defined(MSG_DONTWAIT) + case MSG_DONTWAIT: + return PSP_NET_INET_MSG_DONTWAIT; +#endif +#if defined(MSG_BCAST) + case MSG_BCAST: + return PSP_NET_INET_MSG_BCAST; +#endif +#if defined(MSG_MCAST) + case MSG_MCAST: + return PSP_NET_INET_MSG_MCAST; +#endif } + return hleLogError(Log::sceNet, flag, "Unknown MSG flag"); +} - static int ConvertToMask(const int socket) { - return static_cast(1UL << (socket % gFdsBitsCount)); +int convertMSGFlagsPSP2Host(int flags) { + // Only takes compatible one + int flgs = 0; + if (flags & PSP_NET_INET_MSG_OOB) { + flgs |= MSG_OOB; } -}; - -static bool inetSockaddrToNativeSocketAddr(sockaddr_in &dest, u32 sockAddrInternetPtr, size_t addressLength) { - const auto inetSockaddrIn = Memory::GetTypedPointerRange(sockAddrInternetPtr, (u32)addressLength); - if (inetSockaddrIn == nullptr || addressLength == 0) { - return false; + if (flags & PSP_NET_INET_MSG_PEEK) { + flgs |= MSG_PEEK; } - - // Clear dest of any existing data and copy relevant fields from inet sockaddr_in - memset(&dest, 0, sizeof(dest)); - dest.sin_family = inetSockaddrIn->sin_family; - dest.sin_port = inetSockaddrIn->sin_port; - dest.sin_addr.s_addr = inetSockaddrIn->sin_addr; - DEBUG_LOG(Log::sceNet, "sceSockaddrToNativeSocketAddr: Family %i, port %i, addr %s, len %i", dest.sin_family, ntohs(dest.sin_port), ip2str(dest.sin_addr, false).c_str(), inetSockaddrIn->sin_len); - return true; -} - -static bool writeSockAddrInToInetSockAddr(u32 destAddrPtr, u32 destAddrLenPtr, sockaddr_in src) { - const auto sceNetSocklenPtr = reinterpret_cast(Memory::GetPointerWrite(destAddrLenPtr)); - u32 sceNetSocklen = 0; - if (sceNetSocklenPtr != nullptr) { - sceNetSocklen = *sceNetSocklenPtr; + if (flags & PSP_NET_INET_MSG_DONTROUTE) { + flgs |= MSG_DONTROUTE; } - const auto sceNetSockaddrIn = Memory::GetTypedPointerWriteRange(destAddrPtr, sceNetSocklen); - if (sceNetSockaddrIn == nullptr) { - return false; +#if defined(MSG_EOR) + if (flags & PSP_NET_INET_MSG_EOR) { + flgs |= MSG_EOR; } - DEBUG_LOG(Log::sceNet, "writeSockAddrInToSceSockAddr size: %d vs %d", (int)sizeof(SceNetInetSockaddrIn), sceNetSocklen); - if (sceNetSocklenPtr) { - *sceNetSocklenPtr = std::min(sceNetSocklen, sizeof(SceNetInetSockaddr)); +#endif + if (flags & PSP_NET_INET_MSG_TRUNC) { + flgs |= MSG_TRUNC; } - if (sceNetSocklen >= 1) { - sceNetSockaddrIn->sin_len = sceNetSocklen; + if (flags & PSP_NET_INET_MSG_CTRUNC) { + flgs |= MSG_CTRUNC; } - if (sceNetSocklen >= 2) { - sceNetSockaddrIn->sin_family = src.sin_family; + if (flags & PSP_NET_INET_MSG_WAITALL) { + flgs |= MSG_WAITALL; } - if (sceNetSocklen >= 4) { - sceNetSockaddrIn->sin_port = src.sin_port; +#if defined(MSG_DONTWAIT) + if (flags & PSP_NET_INET_MSG_DONTWAIT) { + flgs |= MSG_DONTWAIT; } - if (sceNetSocklen >= 8) { - sceNetSockaddrIn->sin_addr = src.sin_addr.s_addr; +#endif +#if defined(MSG_BCAST) + if (flags & PSP_NET_INET_MSG_BCAST) { + flgs |= MSG_BCAST; } - return true; -} - -static int setBlockingMode(int nativeSocketId, bool nonblocking) { -#if PPSSPP_PLATFORM(WINDOWS) - unsigned long val = nonblocking ? 1 : 0; - return ioctlsocket(nativeSocketId, FIONBIO, &val); -#else - // Change to Non-Blocking Mode - if (nonblocking) { - return fcntl(nativeSocketId, F_SETFL, O_NONBLOCK); - } else { - const int flags = fcntl(nativeSocketId, F_GETFL); - - // Remove Non-Blocking Flag - return fcntl(nativeSocketId, F_SETFL, flags & ~O_NONBLOCK); +#endif +#if defined(MSG_MCAST) + if (flags & PSP_NET_INET_MSG_MCAST) { + flgs |= MSG_MCAST; } #endif -} - -static int sceNetInetInit() { - ERROR_LOG(Log::sceNet, "UNTESTED sceNetInetInit()"); - return SceNetInet::Init() ? 0 : ERROR_NET_INET_ALREADY_INITIALIZED; -} -int sceNetInetTerm() { - ERROR_LOG(Log::sceNet, "UNTESTED sceNetInetTerm()"); - SceNetInet::Shutdown(); - return hleLogSuccessI(Log::sceNet, 0); + return flgs; } -static int sceNetInetSocket(int inetAddressFamily, int inetType, int inetProtocol) { - WARN_LOG_ONCE(sceNetInetSocket, Log::sceNet, "UNTESTED sceNetInetSocket(%i, %i, %i)", inetAddressFamily, inetType, inetProtocol); - auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); +int convertMSGFlagsHost2PSP(int flags) { + // Only takes compatible one + int flgs = 0; + if (flags & MSG_OOB) { + flgs |= PSP_NET_INET_MSG_OOB; } - - // Translate address family, type, and protocol. There is some complexity around the type in particular where - // flags are able to be encoded in the most significant bits. - - int nativeAddressFamily; - if (!SceNetInet::TranslateInetAddressFamilyToNative(nativeAddressFamily, inetAddressFamily)) { - sceNetInet->SetLastError(EAFNOSUPPORT); - return hleLogError(Log::sceNet, -1, "%s: Unable to translate inet address family %i", __func__, inetAddressFamily); + if (flags & MSG_PEEK) { + flgs |= PSP_NET_INET_MSG_PEEK; } - - int nativeType; - bool nonBlocking; - if (!SceNetInet::TranslateInetSocketTypeToNative(nativeType, nonBlocking, inetType)) { - sceNetInet->SetLastError(EINVAL); - return hleLogError(Log::sceNet, -1, "%s: Unable to translate inet type %08x", __func__, inetType); + if (flags & MSG_DONTROUTE) { + flgs |= PSP_NET_INET_MSG_DONTROUTE; } - - int nativeProtocol; - if (!SceNetInet::TranslateInetProtocolToNative(nativeProtocol, inetProtocol)) { - sceNetInet->SetLastError(EPROTONOSUPPORT); - return hleLogError(Log::sceNet, -1, "%s: Unable to translate inet protocol %i", __func__, inetProtocol); +#if defined(MSG_EOR) + if (flags & MSG_EOR) { + flgs |= PSP_NET_INET_MSG_EOR; } - - // Attempt to open socket - const int nativeSocketId = socket(nativeAddressFamily, nativeType, nativeProtocol); - if (nativeSocketId < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, -1, "%s: Unable to open socket(%i, %i, %i) with error %i: %s", __func__, nativeAddressFamily, nativeType, nativeProtocol, error, strerror(error)); +#endif + if (flags & MSG_TRUNC) { + flgs |= PSP_NET_INET_MSG_TRUNC; } - - // Map opened socket to an inet socket which is 1-indexed - const auto inetSocket = sceNetInet->CreateAndAssociateInetSocket(nativeSocketId, nativeProtocol, nonBlocking); - - // Set non-blocking mode since the translation function does not translate non-blocking mode due to platform incompatibilities - if (nonBlocking) { - setBlockingMode(nativeSocketId, true); + if (flags & MSG_CTRUNC) { + flgs |= PSP_NET_INET_MSG_CTRUNC; } - - // Close opened socket if such a socket exists - if (!inetSocket) { - close(nativeSocketId); - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, ERROR_NET_INET_INVALID_ARG, "%s: Unable to create new InetSocket for native socket id %i, closing", __func__, nativeSocketId); + if (flags & MSG_WAITALL) { + flgs |= PSP_NET_INET_MSG_WAITALL; } - return inetSocket->GetInetSocketId(); -} - -static int sceNetInetGetsockopt(int socket, int inetSocketLevel, int inetOptname, u32 optvalPtr, u32 optlenPtr) { - WARN_LOG(Log::sceNet, "UNTESTED sceNetInetGetsockopt(%i, %i, %i, %08x, %08x)", socket, inetSocketLevel, inetOptname, optvalPtr, optlenPtr); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); +#if defined(MSG_DONTWAIT) + if (flags & MSG_DONTWAIT) { + flgs |= PSP_NET_INET_MSG_DONTWAIT; } - - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); +#endif +#if defined(MSG_BCAST) + if (flags & MSG_BCAST) { + flgs |= PSP_NET_INET_MSG_BCAST; } - - const auto nativeSocketId = inetSocket->GetNativeSocketId(); - - int nativeSocketLevel; - if (!SceNetInet::TranslateInetSocketLevelToNative(nativeSocketLevel, inetSocketLevel)) { - sceNetInet->SetLastError(EINVAL); - return hleLogError(Log::sceNet, -1, "[%i] %s: Unknown socket level %04x", nativeSocketId, __func__, inetSocketLevel); +#endif +#if defined(MSG_MCAST) + if (flags & MSG_MCAST) { + flgs |= PSP_NET_INET_MSG_MCAST; } +#endif - int nativeOptname; - if (!SceNetInet::TranslateInetOptnameToNativeOptname(nativeOptname, inetOptname)) { - sceNetInet->SetLastError(ENOPROTOOPT); - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Unknown optname %04x", inetOptname); - } + return flgs; +} - if (nativeOptname != inetOptname) { - DEBUG_LOG(Log::sceNet, "sceNetInetGetsockopt: Translated optname %04x into %04x", inetOptname, nativeOptname); +int convertSocketDomainPSP2Host(int domain) { + switch (domain) { + case PSP_NET_INET_AF_UNSPEC: + return AF_UNSPEC; + case PSP_NET_INET_AF_LOCAL: + return AF_UNIX; + case PSP_NET_INET_AF_INET: + return AF_INET; } + return hleLogError(Log::sceNet, domain, "Unknown Socket Domain"); +} - socklen_t *optlen = reinterpret_cast(Memory::GetPointerWrite(optlenPtr)); - if (!optlen) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Invalid pointer %08x", nativeSocketId, __func__, optlenPtr); +int convertSocketDomainHost2PSP(int domain) { + switch (domain) { + case AF_UNSPEC: + return PSP_NET_INET_AF_UNSPEC; + case AF_UNIX: + return PSP_NET_INET_AF_LOCAL; + case AF_INET: + return PSP_NET_INET_AF_INET; } + return hleLogError(Log::sceNet, domain, "Unknown Socket Domain"); +} - const auto optval = Memory::GetTypedPointerWriteRange(optvalPtr, *optlen); - if (optval == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Invalid pointer range %08x (size %i)", nativeSocketId, __func__, optvalPtr, *optlen); +std::string inetSocketDomain2str(int domain) { + switch (domain) { + case PSP_NET_INET_AF_UNSPEC: + return "AF_UNSPEC"; + case PSP_NET_INET_AF_UNIX: + return "AF_UNIX"; + case PSP_NET_INET_AF_INET: + return "AF_INET"; } + return "AF_" + StringFromFormat("%08x", domain); +} - switch (nativeOptname) { - // No direct equivalents - case INET_SO_NONBLOCK: { - if (*optlen != sizeof(u32)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Invalid optlen %i for INET_SO_NONBLOCK", nativeSocketId, __func__, *optlen); - } - Memory::Write_U32(optvalPtr, inetSocket->IsNonBlocking() ? 1 : 0); - return hleLogSuccessI(Log::sceNet, 0); - } - // Direct 1:1 mappings - default: { - // TODO: implement non-blocking getsockopt - const int ret = getsockopt(nativeSocketId, nativeSocketLevel, nativeOptname, optval, optlen); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "[%i] %s: returned error %i: %s", nativeSocketId, __func__, error, strerror(error)); - } - return hleLogSuccessI(Log::sceNet, ret); - } - } +int convertSocketTypePSP2Host(int type) { + // FIXME: Masked with 0x0F since there might be additional flags mixed in socket type that need to be converted too + switch (type & PSP_NET_INET_SOCK_TYPE_MASK) { + case PSP_NET_INET_SOCK_STREAM: + return SOCK_STREAM; + case PSP_NET_INET_SOCK_DGRAM: + return SOCK_DGRAM; + case PSP_NET_INET_SOCK_RAW: + // FIXME: SOCK_RAW have some restrictions on newer Windows? + return SOCK_RAW; + case PSP_NET_INET_SOCK_RDM: + return SOCK_RDM; + case PSP_NET_INET_SOCK_SEQPACKET: + return SOCK_SEQPACKET; + case PSP_NET_INET_SOCK_CONN_DGRAM: // PSP_NET_INET_SOCK_DCCP? + return SOCK_DGRAM; // SOCK_RAW? + case PSP_NET_INET_SOCK_PACKET: + return SOCK_STREAM; // SOCK_RAW? + } + + return hleLogError(Log::sceNet, type, "Unknown Socket Type") & PSP_NET_INET_SOCK_TYPE_MASK; } -static int sceNetInetSetsockopt(int socket, int inetSocketLevel, int inetOptname, u32 optvalPtr, int optlen) { - WARN_LOG_ONCE(sceNetInetSetsockopt, Log::sceNet, "UNTESTED sceNetInetSetsockopt(%i, %i, %i, %08x, %i)", socket, inetSocketLevel, inetOptname, optvalPtr, optlen); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); - } - - const auto nativeSocketId = inetSocket->GetNativeSocketId(); - - // Add some exceptions from ANR2ME's implementation - if (inetSocketLevel == PSP_NET_INET_SOL_SOCKET) { - switch (inetOptname) { - case INET_SO_NONBLOCK: - return hleLogSuccessInfoI(Log::sceNet, 0, "Ignoring nonblocking sockopt"); - case INET_SO_REUSEADDR: - return hleLogSuccessInfoI(Log::sceNet, 0, "Ignoring reuseaddr"); - case INET_SO_REUSEPORT: - return hleLogSuccessInfoI(Log::sceNet, 0, "Ignoring reuseport"); - case 0x1022: - return hleLogSuccessInfoI(Log::sceNet, 0, "Ignoring nosigpipe?"); - case INET_SO_RCVBUF: - case INET_SO_SNDBUF: - // It seems UNO game will try to set socket buffer size with a very large size and ended getting error (-1), so we should also limit the buffer size to replicate PSP behavior - // TODO: For SOCK_STREAM max buffer size is 8 Mb on BSD, while max SOCK_DGRAM is 65535 minus the IP & UDP Header size - if (Memory::Read_U32(optvalPtr) > 8 * 1024 * 1024) { - sceNetInet->SetLastError(ENOBUFS); // FIXME: return ENOBUFS for SOCK_STREAM, and EINVAL for SOCK_DGRAM - return hleLogError(Log::sceNet, -1, "buffer size too large?"); - } - break; - default: - // keep going - break; - } +int convertSocketTypeHost2PSP(int type) { + // FIXME: Masked with 0x0F since there might be additional flags mixed in socket type that need to be converted too + switch (type & PSP_NET_INET_SOCK_TYPE_MASK) { + case SOCK_STREAM: + return PSP_NET_INET_SOCK_STREAM; + case SOCK_DGRAM: + return PSP_NET_INET_SOCK_DGRAM; + case SOCK_RAW: + return PSP_NET_INET_SOCK_RAW; + case SOCK_RDM: + return PSP_NET_INET_SOCK_RDM; + case SOCK_SEQPACKET: + return PSP_NET_INET_SOCK_SEQPACKET; +#if defined(CONN_DGRAM) + case CONN_DGRAM: // SOCK_DCCP + return PSP_NET_INET_SOCK_CONN_DGRAM; // PSP_NET_INET_SOCK_DCCP +#endif +#if defined(SOCK_PACKET) + case SOCK_PACKET: + return PSP_NET_INET_SOCK_PACKET; +#endif } - int nativeSocketLevel; - if (!SceNetInet::TranslateInetSocketLevelToNative(nativeSocketLevel, inetSocketLevel)) { - sceNetInet->SetLastError(EINVAL); - return hleLogError(Log::sceNet, -1, "[%i] %s: Unknown socket level %04x", nativeSocketId, __func__, inetSocketLevel); - } + return hleLogError(Log::sceNet, type, "Unknown Socket Type") & PSP_NET_INET_SOCK_TYPE_MASK; +} - int nativeOptname; - if (!SceNetInet::TranslateInetOptnameToNativeOptname(nativeOptname, inetOptname)) { - sceNetInet->SetLastError(ENOPROTOOPT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Unknown optname %04x", nativeSocketId, __func__, inetOptname); - } +std::string inetSocketType2str(int type) { + switch (type & PSP_NET_INET_SOCK_TYPE_MASK) { + case PSP_NET_INET_SOCK_STREAM: + return "SOCK_STREAM"; + case PSP_NET_INET_SOCK_DGRAM: + return "SOCK_DGRAM"; + case PSP_NET_INET_SOCK_RAW: + return "SOCK_RAW"; + case PSP_NET_INET_SOCK_RDM: + return "SOCK_RDM"; + case PSP_NET_INET_SOCK_SEQPACKET: + return "SOCK_SEQPACKET"; + case PSP_NET_INET_SOCK_DCCP: + return "SOCK_DCCP/SOCK_CONN_DGRAM?"; + case PSP_NET_INET_SOCK_PACKET: + return "SOCK_PACKET?"; + } + return "SOCK_" + StringFromFormat("%08x", type); +} - if (nativeOptname != inetOptname) { - DEBUG_LOG(Log::sceNet, "sceNetInetSetsockopt: Translated optname %04x into %04x", inetOptname, nativeOptname); +int convertSocketProtoPSP2Host(int protocol) { + switch (protocol) { + case PSP_NET_INET_IPPROTO_UNSPEC: + return PSP_NET_INET_IPPROTO_UNSPEC; // 0 only valid if there is only 1 protocol available for a particular domain/family and type? + case PSP_NET_INET_IPPROTO_ICMP: + return IPPROTO_ICMP; + case PSP_NET_INET_IPPROTO_IGMP: + return IPPROTO_IGMP; + case PSP_NET_INET_IPPROTO_TCP: + return IPPROTO_TCP; + case PSP_NET_INET_IPPROTO_EGP: + return IPPROTO_EGP; + case PSP_NET_INET_IPPROTO_PUP: + return IPPROTO_PUP; + case PSP_NET_INET_IPPROTO_UDP: + return IPPROTO_UDP; + case PSP_NET_INET_IPPROTO_IDP: + return IPPROTO_IDP; + case PSP_NET_INET_IPPROTO_RAW: + return IPPROTO_RAW; } + return hleLogError(Log::sceNet, protocol, "Unknown Socket Protocol"); +} - // If optlens of != sizeof(u32) are created, split out the handling into separate functions for readability - if (optlen != sizeof(u32)) { - sceNetInet->SetLastError(EINVAL); - return hleLogError(Log::sceNet, -1, "[%i]: %s: Unhandled optlen %i for optname %04x", nativeSocketId, __func__, optlen, inetOptname); - } +int convertSocketProtoHost2PSP(int protocol) { + switch (protocol) { + case PSP_NET_INET_IPPROTO_UNSPEC: + return PSP_NET_INET_IPPROTO_UNSPEC; // 0 only valid if there is only 1 protocol available for a particular domain/family and type? + case IPPROTO_ICMP: + return PSP_NET_INET_IPPROTO_ICMP; + case IPPROTO_IGMP: + return PSP_NET_INET_IPPROTO_IGMP; + case IPPROTO_TCP: + return PSP_NET_INET_IPPROTO_TCP; + case IPPROTO_EGP: + return PSP_NET_INET_IPPROTO_EGP; + case IPPROTO_PUP: + return PSP_NET_INET_IPPROTO_PUP; + case IPPROTO_UDP: + return PSP_NET_INET_IPPROTO_UDP; + case IPPROTO_IDP: + return PSP_NET_INET_IPPROTO_IDP; + case IPPROTO_RAW: + return PSP_NET_INET_IPPROTO_RAW; + } + return hleLogError(Log::sceNet, protocol, "Unknown Socket Protocol"); +} - if (!Memory::IsValidAddress(optvalPtr)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i]: %s: Invalid address %08x for optval", nativeSocketId, __func__, optvalPtr); +std::string inetSocketProto2str(int protocol) { + switch (protocol) { + case PSP_NET_INET_IPPROTO_UNSPEC: + return "IPPROTO_UNSPEC (DEFAULT?)"; // defaulted to IPPROTO_TCP for SOCK_STREAM and IPPROTO_UDP for SOCK_DGRAM + case PSP_NET_INET_IPPROTO_ICMP: + return "IPPROTO_ICMP"; + case PSP_NET_INET_IPPROTO_IGMP: + return "IPPROTO_IGMP"; + case PSP_NET_INET_IPPROTO_TCP: + return "IPPROTO_TCP"; + case PSP_NET_INET_IPPROTO_EGP: + return "IPPROTO_EGP"; + case PSP_NET_INET_IPPROTO_PUP: + return "IPPROTO_PUP"; + case PSP_NET_INET_IPPROTO_UDP: + return "IPPROTO_UDP"; + case PSP_NET_INET_IPPROTO_IDP: + return "IPPROTO_IDP"; + case PSP_NET_INET_IPPROTO_RAW: + return "IPPROTO_RAW"; } + return "IPPROTO_" + StringFromFormat("%08x", protocol); +} - auto optval = Memory::Read_U32(optvalPtr); - DEBUG_LOG(Log::sceNet, "[%i] setsockopt(%i, %i, %i, %i)", nativeSocketId, nativeSocketId, nativeSocketLevel, nativeOptname, optval); - - switch (nativeOptname) { - // Unmatched PSP functions - no direct equivalent - case INET_SO_NONBLOCK: { - const bool nonblocking = optval != 0; - inetSocket->SetNonBlocking(nonblocking); - INFO_LOG(Log::sceNet, "[%i] setsockopt_u32: Set non-blocking=%i", nativeSocketId, nonblocking); - if (setBlockingMode(nativeSocketId, nonblocking) != 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - ERROR_LOG(Log::sceNet, "[%i] %s: Failed to set to non-blocking with error %i: %s", nativeSocketId, __func__, error, strerror(error)); - } - return hleLogSuccessI(Log::sceNet, 0); - } - // Functions with identical structs to native functions - default: { - INFO_LOG(Log::sceNet, "UNTESTED sceNetInetSetsockopt(%i, %i, %i, %u, %i)", nativeSocketId, nativeSocketLevel, nativeOptname, optval, 4); - const int ret = setsockopt(nativeSocketId, nativeSocketLevel, nativeOptname, reinterpret_cast(&optval), sizeof(optval)); - INFO_LOG(Log::sceNet, "setsockopt_u32: setsockopt returned %i for %i", ret, nativeSocketId); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "[%i] %s: Failed to set optname %04x to %08x with error %i: %s", nativeSocketId, __func__, nativeOptname, optval, error, strerror(error)); - } - return hleLogSuccessI(Log::sceNet, ret); +int convertCMsgTypePSP2Host(int type, int level) { + if (level == PSP_NET_INET_IPPROTO_IP) { + switch (type) { +#if defined(IP_RECVDSTADDR) + case PSP_NET_INET_IP_RECVDSTADDR: + return IP_RECVDSTADDR; +#endif +#if defined(IP_RECVIF) + case PSP_NET_INET_IP_RECVIF: + return IP_RECVIF; +#endif } + } else if (level == PSP_NET_INET_SOL_SOCKET) { +#if defined(SCM_RIGHTS) + if (type == PSP_NET_INET_SCM_RIGHTS) + return SCM_RIGHTS; +#endif +#if defined(SCM_CREDS) + if (type == PSP_NET_INET_SCM_CREDS) + return SCM_CREDS; +#endif +#if defined(SCM_TIMESTAMP) + if (type == PSP_NET_INET_SCM_TIMESTAMP) + return SCM_TIMESTAMP; +#endif } + return hleLogError(Log::sceNet, type, "Unknown CMSG_TYPE (Level = %08x)", level); } -static int sceNetInetConnect(int socket, u32 sockAddrInternetPtr, int addressLength) { - WARN_LOG_ONCE(sceNetInetConnect, Log::sceNet, "UNTESTED sceNetInetConnect(%i, %08x, %i, %i)", socket, sockAddrInternetPtr, Memory::Read_U32(sockAddrInternetPtr), addressLength); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - int nativeSocketId; - if (!sceNetInet->GetNativeSocketIdForInetSocketId(nativeSocketId, socket)) { - sceNetInet->SetLastError(EINVAL); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); - } - - // Translate inet sockaddr to native sockaddr - sockaddr_in convertedSockaddr{}; - if (!inetSockaddrToNativeSocketAddr(convertedSockaddr, sockAddrInternetPtr, addressLength)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Error translating sceSockaddr to native sockaddr", nativeSocketId, __func__); - } - - DEBUG_LOG(Log::sceNet, "[%i] sceNetInetConnect: Connecting to %s on %i", nativeSocketId, ip2str(convertedSockaddr.sin_addr, false).c_str(), ntohs(convertedSockaddr.sin_port)); - - // Attempt to connect using translated sockaddr - changeBlockingMode(nativeSocketId, 0); - int ret = connect(nativeSocketId, reinterpret_cast(&convertedSockaddr), sizeof(convertedSockaddr)); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - changeBlockingMode(nativeSocketId, 1); - return hleLogError(Log::sceNet, ret, "[%i] %s: Error connecting %i: %s", nativeSocketId, __func__, error, strerror(error)); +int convertCMsgTypeHost2PSP(int type, int level) { + if (level == IPPROTO_IP) { + switch (type) { +#if defined(IP_RECVDSTADDR) + case IP_RECVDSTADDR: + return PSP_NET_INET_IP_RECVDSTADDR; +#endif +#if defined(IP_RECVIF) + case IP_RECVIF: + return PSP_NET_INET_IP_RECVIF; +#endif + } + } else if (level == SOL_SOCKET) { +#if defined(SCM_RIGHTS) + if (type == SCM_RIGHTS) + return PSP_NET_INET_SCM_RIGHTS; +#endif +#if defined(SCM_CREDS) + if (type == SCM_CREDS) + return PSP_NET_INET_SCM_CREDS; +#endif +#if defined(SCM_TIMESTAMP) + if (type == SCM_TIMESTAMP) + return PSP_NET_INET_SCM_TIMESTAMP; +#endif } - changeBlockingMode(nativeSocketId, 1); - return hleLogSuccessI(Log::sceNet, ret); + return hleLogError(Log::sceNet, type, "Unknown CMSG_TYPE (Level = %08x)", level); } -static int sceNetInetListen(int socket, int backlog) { - WARN_LOG_ONCE(sceNetInetListen, Log::sceNet, "UNTESTED %s(%i, %i)", __func__, socket, backlog); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - int nativeSocketId; - if (!sceNetInet->GetNativeSocketIdForInetSocketId(nativeSocketId, socket)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); - } - - // Map PSP_NET_INET_SOMAXCONN (128) to platform SOMAXCONN - if (backlog == PSP_NET_INET_SOMAXCONN) { - backlog = SOMAXCONN; - } - - // Attempt to listen using either backlog, or SOMAXCONN as per above - const int ret = listen(socket, backlog); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "[%i] %s: Error listening %i: %s", nativeSocketId, __func__, error, strerror(error)); +int convertSockoptLevelPSP2Host(int level) { + switch (level) { + case PSP_NET_INET_IPPROTO_IP: + return IPPROTO_IP; + case PSP_NET_INET_IPPROTO_TCP: + return IPPROTO_TCP; + case PSP_NET_INET_IPPROTO_UDP: + return IPPROTO_UDP; + case PSP_NET_INET_SOL_SOCKET: + return SOL_SOCKET; } - return hleLogSuccessI(Log::sceNet, ret); + return hleLogError(Log::sceNet, level, "Unknown SockOpt Level"); } -static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { - WARN_LOG_ONCE(sceNetInetListen, Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __func__, socket, addrPtr, addrLenPtr); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - int nativeSocketId; - if (!sceNetInet->GetNativeSocketIdForInetSocketId(nativeSocketId, socket)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); - } - - // Attempt to accept a connection which will provide us with a sockaddrIn containing remote connection details - sockaddr_in sockaddrIn{}; - socklen_t socklen; - const int ret = accept(nativeSocketId, reinterpret_cast(&sockaddrIn), &socklen); - if (ret < 0) { - // Ensure that ERROR_WHEN_NONBLOCKING_CALL_OCCURS is not mapped to an hleLogError - if (const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - error != ERROR_WHEN_NONBLOCKING_CALL_OCCURS) { - hleLogError(Log::sceNet, ret, "[%i] %s: Encountered error %i: %s", nativeSocketId, __func__, error, strerror(error)); - } - return hleLogSuccessI(Log::sceNet, ret); - } - - // Don't call writeSockAddrInToInetSockAddr when addrPtr is 0, otherwise do and send false to EFAULT - if (addrPtr != 0 && !writeSockAddrInToInetSockAddr(addrPtr, addrLenPtr, sockaddrIn)) { - sceNetInet->SetLastError(EFAULT); - hleLogError(Log::sceNet, ret, "[%i] %s: Encountered error trying to write to addrPtr, probably invalid memory range", nativeSocketId, __func__); - } - return hleLogSuccessI(Log::sceNet, ret); +int convertSockoptLevelHost2PSP(int level) { + switch (level) { + case IPPROTO_IP: + return PSP_NET_INET_IPPROTO_IP; + case IPPROTO_TCP: + return PSP_NET_INET_IPPROTO_TCP; + case IPPROTO_UDP: + return PSP_NET_INET_IPPROTO_UDP; + case SOL_SOCKET: + return PSP_NET_INET_SOL_SOCKET; + } + return hleLogError(Log::sceNet, level, "Unknown SockOpt Level"); } -int sceNetInetPoll(void *fds, u32 nfds, int timeout) { // timeout in miliseconds - DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetPoll(%p, %d, %i) at %08x", fds, nfds, timeout, currentMIPS->pc); - const auto fdarray = static_cast(fds); // SceNetInetPollfd/pollfd, sceNetInetPoll() have similarity to BSD poll() but pollfd have different size on 64bit -//#ifdef _WIN32 - //WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX - if (nfds > FD_SETSIZE) { - ERROR_LOG(Log::sceNet, "sceNetInetPoll: nfds=%i is greater than FD_SETSIZE=%i, unable to poll", nfds, FD_SETSIZE); - return -1; - } - fd_set readfds, writefds, exceptfds; - FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); - for (int i = 0; i < static_cast(nfds); i++) { - if (fdarray[i].events & (INET_POLLRDNORM)) - FD_SET(fdarray[i].fd, &readfds); // (POLLRDNORM | POLLIN) - if (fdarray[i].events & (INET_POLLWRNORM)) - FD_SET(fdarray[i].fd, &writefds); // (POLLWRNORM | POLLOUT) - //if (fdarray[i].events & (ADHOC_EV_ALERT)) // (POLLRDBAND | POLLPRI) // POLLERR - FD_SET(fdarray[i].fd, &exceptfds); - fdarray[i].revents = 0; - } - timeval tmout{}; - tmout.tv_sec = timeout / 1000; // seconds - tmout.tv_usec = (timeout % 1000) * 1000; // microseconds - const int ret = select(nfds, &readfds, &writefds, &exceptfds, &tmout); - if (ret < 0) - return -1; - int eventCount = 0; - for (int i = 0; i < static_cast(nfds); i++) { - if (FD_ISSET(fdarray[i].fd, &readfds)) - fdarray[i].revents |= INET_POLLRDNORM; //POLLIN - if (FD_ISSET(fdarray[i].fd, &writefds)) - fdarray[i].revents |= INET_POLLWRNORM; //POLLOUT - fdarray[i].revents &= fdarray[i].events; - if (FD_ISSET(fdarray[i].fd, &exceptfds)) - fdarray[i].revents |= ADHOC_EV_ALERT; // POLLPRI; // POLLERR; // can be raised on revents regardless of events bitmask? - if (fdarray[i].revents) - eventCount++; - } -//#else - /* - // Doesn't work properly yet - pollfd *fdtmp = (pollfd *)malloc(sizeof(pollfd) * nfds); - // Note: sizeof(pollfd) = 16bytes in 64bit and 8bytes in 32bit, while sizeof(SceNetInetPollfd) is always 8bytes - for (int i = 0; i < (s32)nfds; i++) { - fdtmp[i].fd = fdarray[i].fd; - fdtmp[i].events = 0; - if (fdarray[i].events & INET_POLLRDNORM) fdtmp[i].events |= (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI); - if (fdarray[i].events & INET_POLLWRNORM) fdtmp[i].events |= (POLLOUT | POLLWRNORM | POLLWRBAND); - fdtmp[i].revents = 0; - fdarray[i].revents = 0; - } - retval = poll(fdtmp, (nfds_t)nfds, timeout); //retval = WSAPoll(fdarray, nfds, timeout); - for (int i = 0; i < (s32)nfds; i++) { - if (fdtmp[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) fdarray[i].revents |= INET_POLLRDNORM; - if (fdtmp[i].revents & (POLLOUT | POLLWRNORM | POLLWRBAND)) fdarray[i].revents |= INET_POLLWRNORM; - fdarray[i].revents &= fdarray[i].events; - if (fdtmp[i].revents & POLLERR) fdarray[i].revents |= POLLERR; //INET_POLLERR // can be raised on revents regardless of events bitmask? +std::string inetSockoptLevel2str(int level) { + switch (level) { + case PSP_NET_INET_IPPROTO_IP: + return "IPPROTO_IP"; + case PSP_NET_INET_IPPROTO_TCP: + return "IPPROTO_TCP"; + case PSP_NET_INET_IPPROTO_UDP: + return "IPPROTO_UDP"; + case PSP_NET_INET_SOL_SOCKET: + return "SOL_SOCKET"; } - free(fdtmp); - */ -//#endif - return eventCount; + return "SOL_" + StringFromFormat("%08x", level); } -static int sceNetInetSelect(int maxfd, u32 readFdsPtr, u32 writeFdsPtr, u32 exceptFdsPtr, u32 timeoutPtr) { - DEBUG_LOG(Log::sceNet, "sceNetInetSelect(%i, %08x, %08x, %08x, %08x)", maxfd, readFdsPtr, writeFdsPtr, exceptFdsPtr, timeoutPtr); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - // Translate all input fd_sets to native fd_sets. None of these will be nullptr and so this needs to be checked later. - int recomputedMaxFd = 1; - fd_set readFds; - sceNetInet->TranslateInetFdSetToNativeFdSet(recomputedMaxFd, readFds, readFdsPtr); - fd_set writeFds; - sceNetInet->TranslateInetFdSetToNativeFdSet(recomputedMaxFd, writeFds, writeFdsPtr); - fd_set exceptFds; - sceNetInet->TranslateInetFdSetToNativeFdSet(recomputedMaxFd, exceptFds, exceptFdsPtr); - - // Convert timeval when applicable - timeval tv{}; - if (timeoutPtr != 0) { - const auto inetTimeval = Memory::GetTypedPointerRange(timeoutPtr, sizeof(PspInetTimeval)); - if (inetTimeval != nullptr) { - tv.tv_sec = inetTimeval->tv_sec; - tv.tv_usec = inetTimeval->tv_usec; - DEBUG_LOG(Log::sceNet, "%s: Timeout seconds=%lu, useconds=%lu", __func__, tv.tv_sec, tv.tv_usec); - } else { - WARN_LOG(Log::sceNet, "%s: Encountered invalid timeout value, continuing anyway", __func__); +int convertSockoptNamePSP2Host(int optname, int level) { + if (level == PSP_NET_INET_IPPROTO_TCP) { + switch (optname) { + case PSP_NET_INET_TCP_NODELAY: + return TCP_NODELAY; + case PSP_NET_INET_TCP_MAXSEG: + return TCP_MAXSEG; + } + } else if (level == PSP_NET_INET_IPPROTO_IP) { + switch (optname) { + case PSP_NET_INET_IP_OPTIONS: + return IP_OPTIONS; + case PSP_NET_INET_IP_HDRINCL: + return IP_HDRINCL; + case PSP_NET_INET_IP_TOS: + return IP_TOS; + case PSP_NET_INET_IP_TTL: + return IP_TTL; +#if defined(IP_RECVOPTS) + case PSP_NET_INET_IP_RECVOPTS: + return IP_RECVOPTS; +#endif +#if defined(IP_RECVRETOPTS) + case PSP_NET_INET_IP_RECVRETOPTS: + return IP_RECVRETOPTS; +#endif +#if defined(IP_RECVDSTADDR) + case PSP_NET_INET_IP_RECVDSTADDR: + return IP_RECVDSTADDR; +#endif +#if defined(IP_RETOPTS) + case PSP_NET_INET_IP_RETOPTS: + return IP_RETOPTS; +#endif + case PSP_NET_INET_IP_MULTICAST_IF: + return IP_MULTICAST_IF; + case PSP_NET_INET_IP_MULTICAST_TTL: + return IP_MULTICAST_TTL; + case PSP_NET_INET_IP_MULTICAST_LOOP: + return IP_MULTICAST_LOOP; + case PSP_NET_INET_IP_ADD_MEMBERSHIP: + return IP_ADD_MEMBERSHIP; + case PSP_NET_INET_IP_DROP_MEMBERSHIP: + return IP_DROP_MEMBERSHIP; +#if defined(IP_PORTRANGE) + case PSP_NET_INET_IP_PORTRANGE: + return IP_PORTRANGE; +#endif +#if defined(IP_RECVIF) + case PSP_NET_INET_IP_RECVIF: + return IP_RECVIF; +#endif +#if defined(IP_ERRORMTU) + case PSP_NET_INET_IP_ERRORMTU: + return IP_ERRORMTU; +#endif +#if defined(IP_IPSEC_POLICY) + case PSP_NET_INET_IP_IPSEC_POLICY: + return IP_IPSEC_POLICY; +#endif + } + } else if (level == PSP_NET_INET_SOL_SOCKET) { + switch (optname) { + case PSP_NET_INET_SO_DEBUG: + return SO_DEBUG; + case PSP_NET_INET_SO_ACCEPTCONN: + return SO_ACCEPTCONN; + case PSP_NET_INET_SO_REUSEADDR: + return SO_REUSEADDR; + case PSP_NET_INET_SO_KEEPALIVE: + return SO_KEEPALIVE; + case PSP_NET_INET_SO_DONTROUTE: + return SO_DONTROUTE; + case PSP_NET_INET_SO_BROADCAST: + return SO_BROADCAST; +#if defined(SO_USELOOPBACK) + case PSP_NET_INET_SO_USELOOPBACK: + return SO_USELOOPBACK; +#endif + case PSP_NET_INET_SO_LINGER: + return SO_LINGER; + case PSP_NET_INET_SO_OOBINLINE: + return SO_OOBINLINE; +#if defined(SO_REUSEPORT) + case PSP_NET_INET_SO_REUSEPORT: + return SO_REUSEPORT; +#endif +#if defined(SO_TIMESTAMP) + case PSP_NET_INET_SO_TIMESTAMP: + return SO_TIMESTAMP; +#endif +#if defined(SO_ONESBCAST) + case PSP_NET_INET_SO_ONESBCAST: + return SO_ONESBCAST; +#endif + case PSP_NET_INET_SO_SNDBUF: + return SO_SNDBUF; + case PSP_NET_INET_SO_RCVBUF: + return SO_RCVBUF; + case PSP_NET_INET_SO_SNDLOWAT: + return SO_SNDLOWAT; + case PSP_NET_INET_SO_RCVLOWAT: + return SO_RCVLOWAT; + case PSP_NET_INET_SO_SNDTIMEO: + return SO_SNDTIMEO; + case PSP_NET_INET_SO_RCVTIMEO: + return SO_RCVTIMEO; + case PSP_NET_INET_SO_ERROR: + return SO_ERROR; + case PSP_NET_INET_SO_TYPE: + return SO_TYPE; +#if defined(SO_NBIO) + case PSP_NET_INET_SO_NBIO: + return SO_NBIO; +#endif +#if defined(SO_BIO) + case PSP_NET_INET_SO_BIO: + return SO_BIO; +#endif } } - - // Since the fd_set structs are allocated on the stack (and always so), only pass in their pointers if the input pointer is non-null - const int ret = select(recomputedMaxFd, readFdsPtr != 0 ? &readFds : nullptr, writeFdsPtr != 0 ? &writeFds : nullptr, exceptFdsPtr != 0 ? &exceptFds : nullptr, timeoutPtr != 0 ? &tv : nullptr); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - ERROR_LOG(Log::sceNet, "%s: Received error from select() %i: %s", __func__, error, strerror(error)); - } - - DEBUG_LOG(Log::sceNet, "%s: select() returned %i", __func__, ret); - return hleDelayResult(ret, "TODO: unhack", 500); + return hleLogError(Log::sceNet, optname, "Unknown PSP's SockOpt Name (Level = %08x)", level); } -static int sceNetInetClose(int socket) { - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogWarning(Log::sceNet, -1, "%s: Attempting to close socket %i which does not exist", __func__, socket); - } - - const int ret = close(inetSocket->GetNativeSocketId()); - if (!sceNetInet->EraseNativeSocket(socket)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "%s: Unable to clear mapping of inetSocketId->nativeSocketId, was there contention?", __func__); +int convertSockoptNameHost2PSP(int optname, int level) { + if (level == IPPROTO_TCP) { + switch (optname) { + case TCP_NODELAY: + return PSP_NET_INET_TCP_NODELAY; + case TCP_MAXSEG: + return PSP_NET_INET_TCP_MAXSEG; + } + } else if (level == IPPROTO_IP) { + switch (optname) { + case IP_OPTIONS: + return PSP_NET_INET_IP_OPTIONS; + case IP_HDRINCL: + return PSP_NET_INET_IP_HDRINCL; + case IP_TOS: + return PSP_NET_INET_IP_TOS; + case IP_TTL: + return PSP_NET_INET_IP_TTL; +#if defined(IP_RECVOPTS) + case IP_RECVOPTS: + return PSP_NET_INET_IP_RECVOPTS; +#endif +#if defined(IP_RECVRETOPTS) && (IP_RECVRETOPTS != IP_RETOPTS) + case IP_RECVRETOPTS: + return PSP_NET_INET_IP_RECVRETOPTS; +#endif +#if defined(IP_RECVDSTADDR) + case IP_RECVDSTADDR: + return PSP_NET_INET_IP_RECVDSTADDR; +#endif +#if defined(IP_RETOPTS) + case IP_RETOPTS: + return PSP_NET_INET_IP_RETOPTS; +#endif + case IP_MULTICAST_IF: + return PSP_NET_INET_IP_MULTICAST_IF; + case IP_MULTICAST_TTL: + return PSP_NET_INET_IP_MULTICAST_TTL; + case IP_MULTICAST_LOOP: + return PSP_NET_INET_IP_MULTICAST_LOOP; + case IP_ADD_MEMBERSHIP: + return PSP_NET_INET_IP_ADD_MEMBERSHIP; + case IP_DROP_MEMBERSHIP: + return PSP_NET_INET_IP_DROP_MEMBERSHIP; +#if defined(IP_PORTRANGE) + case IP_PORTRANGE: + return PSP_NET_INET_IP_PORTRANGE; +#endif +#if defined(IP_RECVIF) + case PSP_NET_INET_IP_RECVIF: + return IP_RECVIF; +#endif +#if defined(IP_ERRORMTU) + case IP_ERRORMTU: + return PSP_NET_INET_IP_ERRORMTU; +#endif +#if defined(IP_IPSEC_POLICY) + case IP_IPSEC_POLICY: + return PSP_NET_INET_IP_IPSEC_POLICY; +#endif + } + } else if (level == SOL_SOCKET) { + switch (optname) { + case SO_DEBUG: + return PSP_NET_INET_SO_DEBUG; + case SO_ACCEPTCONN: + return PSP_NET_INET_SO_ACCEPTCONN; + case SO_REUSEADDR: + return PSP_NET_INET_SO_REUSEADDR; + case SO_KEEPALIVE: + return PSP_NET_INET_SO_KEEPALIVE; + case SO_DONTROUTE: + return PSP_NET_INET_SO_DONTROUTE; + case SO_BROADCAST: + return PSP_NET_INET_SO_BROADCAST; +#if defined(SO_USELOOPBACK) + case SO_USELOOPBACK: + return PSP_NET_INET_SO_USELOOPBACK; +#endif + case SO_LINGER: + return PSP_NET_INET_SO_LINGER; + case SO_OOBINLINE: + return PSP_NET_INET_SO_OOBINLINE; +#if defined(SO_REUSEPORT) + case SO_REUSEPORT: + return PSP_NET_INET_SO_REUSEPORT; +#endif +#if defined(SO_TIMESTAMP) + case SO_TIMESTAMP: + return PSP_NET_INET_SO_TIMESTAMP; +#endif +#if defined(SO_ONESBCAST) + case SO_ONESBCAST: + return PSP_NET_INET_SO_ONESBCAST; +#endif + case SO_SNDBUF: + return PSP_NET_INET_SO_SNDBUF; + case SO_RCVBUF: + return PSP_NET_INET_SO_RCVBUF; + case SO_SNDLOWAT: + return PSP_NET_INET_SO_SNDLOWAT; + case SO_RCVLOWAT: + return PSP_NET_INET_SO_RCVLOWAT; + case SO_SNDTIMEO: + return PSP_NET_INET_SO_SNDTIMEO; + case SO_RCVTIMEO: + return PSP_NET_INET_SO_RCVTIMEO; + case SO_ERROR: + return PSP_NET_INET_SO_ERROR; + case SO_TYPE: + return PSP_NET_INET_SO_TYPE; +#if defined(SO_NBIO) + case SO_NBIO: + return PSP_NET_INET_SO_NBIO; +#endif +#if defined(SO_BIO) + case SO_BIO: + return PSP_NET_INET_SO_BIO; +#endif + } } - return hleLogSuccessI(Log::sceNet, ret); + return hleLogError(Log::sceNet, optname, "Unknown Host's SockOpt Name (Level = %08x)", level); } -static u32 sceNetInetInetAddr(const char *hostname) { - ERROR_LOG(Log::sceNet, "UNTESTED sceNetInetInetAddr(%s)", hostname); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); +std::string inetSockoptName2str(int optname, int level) { + if (level == PSP_NET_INET_IPPROTO_TCP) { + switch (optname) { + case PSP_NET_INET_TCP_NODELAY: + return "TCP_NODELAY"; + case PSP_NET_INET_TCP_MAXSEG: + return "TCP_MAXSEG"; + } + } else if (level == PSP_NET_INET_IPPROTO_IP) { + switch (optname) { + case PSP_NET_INET_IP_OPTIONS: + return "IP_OPTIONS"; + case PSP_NET_INET_IP_HDRINCL: + return "IP_HDRINCL"; + case PSP_NET_INET_IP_TOS: + return "IP_TOS"; + case PSP_NET_INET_IP_TTL: + return "IP_TTL"; + case PSP_NET_INET_IP_RECVOPTS: + return "IP_RECVOPTS"; + case PSP_NET_INET_IP_RECVRETOPTS: + return "IP_RECVRETOPTS"; + case PSP_NET_INET_IP_RECVDSTADDR: + return "IP_RECVDSTADDR"; + case PSP_NET_INET_IP_RETOPTS: + return "IP_RETOPTS"; + case PSP_NET_INET_IP_MULTICAST_IF: + return "IP_MULTICAST_IF"; + case PSP_NET_INET_IP_MULTICAST_TTL: + return "IP_MULTICAST_TTL"; + case PSP_NET_INET_IP_MULTICAST_LOOP: + return "IP_MULTICAST_LOOP"; + case PSP_NET_INET_IP_ADD_MEMBERSHIP: + return "IP_ADD_MEMBERSHIP"; + case PSP_NET_INET_IP_DROP_MEMBERSHIP: + return "IP_DROP_MEMBERSHIP"; + case PSP_NET_INET_IP_PORTRANGE: + return "IP_PORTRANGE"; + case PSP_NET_INET_IP_RECVIF: + return "IP_RECVIF"; + case PSP_NET_INET_IP_ERRORMTU: + return "IP_ERRORMTU"; + case PSP_NET_INET_IP_IPSEC_POLICY: + return "IP_IPSEC_POLICY"; + } + } else if (level == PSP_NET_INET_SOL_SOCKET) { + switch (optname) { + case PSP_NET_INET_SO_DEBUG: + return "SO_DEBUG"; + case PSP_NET_INET_SO_ACCEPTCONN: + return "SO_ACCEPTCONN"; + case PSP_NET_INET_SO_REUSEADDR: + return "SO_REUSEADDR"; + case PSP_NET_INET_SO_KEEPALIVE: + return "SO_KEEPALIVE"; + case PSP_NET_INET_SO_DONTROUTE: + return "SO_DONTROUTE"; + case PSP_NET_INET_SO_BROADCAST: + return "SO_BROADCAST"; + case PSP_NET_INET_SO_USELOOPBACK: + return "SO_USELOOPBACK"; + case PSP_NET_INET_SO_LINGER: + return "SO_LINGER"; + case PSP_NET_INET_SO_OOBINLINE: + return "SO_OOBINLINE"; + case PSP_NET_INET_SO_REUSEPORT: + return "SO_REUSEPORT"; + case PSP_NET_INET_SO_TIMESTAMP: + return "SO_TIMESTAMP"; + case PSP_NET_INET_SO_ONESBCAST: + return "SO_ONESBCAST"; + case PSP_NET_INET_SO_SNDBUF: + return "SO_SNDBUF"; + case PSP_NET_INET_SO_RCVBUF: + return "SO_RCVBUF"; + case PSP_NET_INET_SO_SNDLOWAT: + return "SO_SNDLOWAT"; + case PSP_NET_INET_SO_RCVLOWAT: + return "SO_RCVLOWAT"; + case PSP_NET_INET_SO_SNDTIMEO: + return "SO_SNDTIMEO"; + case PSP_NET_INET_SO_RCVTIMEO: + return "SO_RCVTIMEO"; + case PSP_NET_INET_SO_ERROR: + return "SO_ERROR"; + case PSP_NET_INET_SO_TYPE: + return "SO_TYPE"; + case PSP_NET_INET_SO_NBIO: + return "SO_NBIO"; // SO_NONBLOCK + case PSP_NET_INET_SO_BIO: + return "SO_BIO"; + } } + return "SO_" + StringFromFormat("%08x (Level = %08x)", optname, level); +} - in_addr inAddr{}; -#if PPSSPP_PLATFORM(WINDOWS) - const int ret = inet_pton(AF_INET, hostname, &inAddr); -#else - const int ret = inet_aton(hostname, &inAddr); +int convertInetErrnoHost2PSP(int error) { + switch (error) { + case EINTR: + return ERROR_INET_EINTR; + case EBADF: + return ERROR_INET_EBADF; + case EACCES: + return ERROR_INET_EACCES; + case EFAULT: + return ERROR_INET_EFAULT; + case EINVAL: + return ERROR_INET_EINVAL; + case ENOSPC: + return ERROR_INET_ENOSPC; + case EPIPE: + return ERROR_INET_EPIPE; + case ENOMSG: + return ERROR_INET_ENOMSG; + case ENOLINK: + return ERROR_INET_ENOLINK; + case EPROTO: + return ERROR_INET_EPROTO; + case EBADMSG: + return ERROR_INET_EBADMSG; + case EOPNOTSUPP: + return ERROR_INET_EOPNOTSUPP; + case EPFNOSUPPORT: + return ERROR_INET_EPFNOSUPPORT; + case ECONNRESET: + return ERROR_INET_ECONNRESET; + case ENOBUFS: + return ERROR_INET_ENOBUFS; + case EAFNOSUPPORT: + return ERROR_INET_EAFNOSUPPORT; + case EPROTOTYPE: + return ERROR_INET_EPROTOTYPE; + case ENOTSOCK: + return ERROR_INET_ENOTSOCK; + case ENOPROTOOPT: + return ERROR_INET_ENOPROTOOPT; + case ESHUTDOWN: + return ERROR_INET_ESHUTDOWN; + case ECONNREFUSED: + return ERROR_INET_ECONNREFUSED; + case EADDRINUSE: + return ERROR_INET_EADDRINUSE; + case ECONNABORTED: + return ERROR_INET_ECONNABORTED; + case ENETUNREACH: + return ERROR_INET_ENETUNREACH; + case ENETDOWN: + return ERROR_INET_ENETDOWN; + case ETIMEDOUT: + return ERROR_INET_ETIMEDOUT; + case EHOSTDOWN: + return ERROR_INET_EHOSTDOWN; + case EHOSTUNREACH: + return ERROR_INET_EHOSTUNREACH; + case EALREADY: + return ERROR_INET_EALREADY; + case EMSGSIZE: + return ERROR_INET_EMSGSIZE; + case EPROTONOSUPPORT: + return ERROR_INET_EPROTONOSUPPORT; + case ESOCKTNOSUPPORT: + return ERROR_INET_ESOCKTNOSUPPORT; + case EADDRNOTAVAIL: + return ERROR_INET_EADDRNOTAVAIL; + case ENETRESET: + return ERROR_INET_ENETRESET; + case EISCONN: + return ERROR_INET_EISCONN; + case ENOTCONN: + return ERROR_INET_ENOTCONN; + case EAGAIN: + return ERROR_INET_EAGAIN; +#if !defined(_WIN32) + case EINPROGRESS: + return ERROR_INET_EINPROGRESS; #endif - if (ret != 0) { - sceNetInet->SetLastErrorToMatchPlatform(); - return inAddr.s_addr; } - - // TODO: Should this return ret or inAddr.sAddr? Conflicting info between the two PRs! - - return hleLogSuccessI(Log::sceNet, ret); + if (error != 0) + return hleLogError(Log::sceNet, error, "Unknown Error Number (%d)", error); + return error; } -static int sceNetInetInetAton(const char *hostname, u32 addrPtr) { - ERROR_LOG(Log::sceNet, "UNTESTED %s(%s, %08x)", __func__, hostname, addrPtr); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - if (hostname == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "%s: Invalid hostname: %08x", __func__, hostname); - } - - if (!Memory::IsValidAddress(addrPtr)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "%s: Invalid addrPtr: %08x", __func__, addrPtr); - } - - // Convert the input hostname into an inaddr - in_addr inAddr{}; -#if PPSSPP_PLATFORM(WINDOWS) - // Use inet_pton to accomplish the same behavior on Winsock2 which is missing inet_aton - const int ret = inet_pton(AF_INET, hostname, &inAddr); -#else - const int ret = inet_aton(hostname, &inAddr); +// FIXME: Some of this might be wrong +int convertInetErrno2PSPError(int error) { + switch (error) { + case ERROR_INET_EINTR: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_BUSY; + case ERROR_INET_EACCES: + return SCE_KERNEL_ERROR_ERRNO_READ_ONLY; + case ERROR_INET_EFAULT: + return SCE_KERNEL_ERROR_ERRNO_ADDR_OUT_OF_MAIN_MEM; + case ERROR_INET_EINVAL: + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; + case ERROR_INET_ENOSPC: + return SCE_KERNEL_ERROR_ERRNO_NO_MEMORY; + case ERROR_INET_EPIPE: + return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; + case ERROR_INET_ENOMSG: + return SCE_KERNEL_ERROR_ERRNO_NO_MEDIA; + case ERROR_INET_ENOLINK: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + case ERROR_INET_EPROTO: + return SCE_KERNEL_ERROR_ERRNO_FILE_PROTOCOL; + case ERROR_INET_EBADMSG: + return SCE_KERNEL_ERROR_ERRNO_INVALID_MEDIUM; + case ERROR_INET_EOPNOTSUPP: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_EPFNOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_ECONNRESET: + return SCE_KERNEL_ERROR_ERRNO_CONNECTION_RESET; + case ERROR_INET_ENOBUFS: + return SCE_KERNEL_ERROR_ERRNO_NO_FREE_BUF_SPACE; + case ERROR_INET_EAFNOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_EPROTOTYPE: + return SCE_KERNEL_ERROR_ERRNO_FILE_PROTOCOL; + case ERROR_INET_ENOTSOCK: + return SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_DESCRIPTOR; + case ERROR_INET_ENOPROTOOPT: + return SCE_KERNEL_ERROR_ERRNO_FILE_PROTOCOL; + case ERROR_INET_ESHUTDOWN: + return SCE_KERNEL_ERROR_ERRNO_CLOSED; + case ERROR_INET_ECONNREFUSED: + return SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS; + case ERROR_INET_EADDRINUSE: + return SCE_KERNEL_ERROR_ERRNO_FILE_ADDR_IN_USE; + case ERROR_INET_ECONNABORTED: + return SCE_KERNEL_ERROR_ERRNO_CONNECTION_ABORTED; + case ERROR_INET_ENETUNREACH: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + case ERROR_INET_ENETDOWN: + return SCE_KERNEL_ERROR_ERRNO_CLOSED; + case ERROR_INET_ETIMEDOUT: + return SCE_KERNEL_ERROR_ERRNO_FILE_TIMEOUT; + case ERROR_INET_EHOSTDOWN: + return SCE_KERNEL_ERROR_ERRNO_CLOSED; + case ERROR_INET_EHOSTUNREACH: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + case ERROR_INET_EALREADY: + return SCE_KERNEL_ERROR_ERRNO_ALREADY; + case ERROR_INET_EMSGSIZE: + return SCE_KERNEL_ERROR_ERRNO_FILE_IS_TOO_BIG; + case ERROR_INET_EPROTONOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_ESOCKTNOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_EADDRNOTAVAIL: + return SCE_KERNEL_ERROR_ERRNO_ADDRESS_NOT_AVAILABLE; + case ERROR_INET_ENETRESET: + return SCE_KERNEL_ERROR_ERRNO_CONNECTION_RESET; + case ERROR_INET_EISCONN: + return SCE_KERNEL_ERROR_ERRNO_ALREADY; // SCE_KERNEL_ERROR_ERRNO_IS_ALREADY_CONNECTED; // UNO only check for 0x80010077 and 0x80010078 + case ERROR_INET_ENOTCONN: + return SCE_KERNEL_ERROR_ERRNO_NOT_CONNECTED; + case ERROR_INET_EAGAIN: + return SCE_KERNEL_ERROR_ERRNO_RESOURCE_UNAVAILABLE; // SCE_ERROR_ERRNO_EAGAIN; +#if !defined(_WIN32) + case ERROR_INET_EINPROGRESS: + return SCE_KERNEL_ERROR_ERRNO_IN_PROGRESS; #endif - - if (ret == 0) { - // inet_aton does not set errno when an error occurs, so neither should we - return hleLogError(Log::sceNet, ret, "%s: Invalid hostname %s", __func__, hostname); } - - // Write back to addrPtr if ret is != 0 - Memory::Write_U32(inAddr.s_addr, addrPtr); - return hleLogSuccessI(Log::sceNet, ret); + if (error != 0) + return hleLogError(Log::sceNet, error, "Unknown PSP Error Number (%d)", error); + return error; } -static u32 sceNetInetInetNtop(int inetAddressFamily, u32 srcPtr, u32 dstBufPtr, u32 dstBufSize) { - WARN_LOG_ONCE(sceNetInetInetNtop, Log::sceNet, "UNTESTED %s(%i, %08x, %08x, %i)", __func__, inetAddressFamily, srcPtr, dstBufPtr, dstBufSize); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - const auto srcSockaddrIn = Memory::GetTypedPointerWriteRange(srcPtr, sizeof(SceNetInetSockaddrIn)); - if (srcSockaddrIn == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, 0, "%s: Invalid memory range for srcPtr %08x", __func__, srcPtr); - } - - const auto dstBuf = Memory::GetTypedPointerWriteRange(dstBufPtr, dstBufSize); - if (dstBuf == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, 0, "%s: Invalid memory range for dstBufPtr %08x, size %i", __func__, dstBufPtr, dstBufSize); - } - - if (!dstBufSize) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, 0, "%s: dstBufSize must be > 0", __func__); - } - - int nativeAddressFamily; - if (!SceNetInet::TranslateInetAddressFamilyToNative(nativeAddressFamily, inetAddressFamily)) { - sceNetInet->SetLastError(EAFNOSUPPORT); - return hleLogError(Log::sceNet, 0, "%s: Unknown address family %04x", __func__, inetAddressFamily); +void __NetInetShutdown() { + if (!netInetInited) { + return; } - if (inet_ntop(nativeAddressFamily, reinterpret_cast(srcSockaddrIn), dstBuf, dstBufSize) == nullptr) { - // Allow partial output in case it's desired for some reason - } - return hleLogSuccessX(Log::sceNet, dstBufPtr); + netInetInited = false; + // TODO: Shut down any open sockets here. } -static int sceNetInetInetPton(int inetAddressFamily, const char *hostname, u32 dstPtr) { - WARN_LOG_ONCE(sceNetInetInetPton, Log::sceNet, "UNTESTED %s(%i, %s, %08x)", __func__, inetAddressFamily, hostname, dstPtr); - - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - if (hostname == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, 0, "%s: Invalid memory range for hostname %08x", __func__, hostname); - } - - // IPv4, the only supported address family on PSP, will always be 32 bits - const auto dst = Memory::GetTypedPointerWriteRange(dstPtr, sizeof(u32)); - if (dst == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, 0, "%s: Invalid memory range for dstPtr %08x, size %i", __func__, dstPtr, sizeof(u32)); - } +static int sceNetInetInit() { + WARN_LOG(Log::sceNet, "UNIMPL sceNetInetInit()"); + if (netInetInited) + return ERROR_NET_INET_ALREADY_INITIALIZED; + netInetInited = true; + return 0; +} - // Translate inet address family to native - int nativeAddressFamily; - if (!SceNetInet::TranslateInetAddressFamilyToNative(nativeAddressFamily, inetAddressFamily)) { - sceNetInet->SetLastError(EAFNOSUPPORT); - return hleLogError(Log::sceNet, 0, "%s: Unknown address family %04x", __func__, inetAddressFamily); - } +static int sceNetInetTerm() { + WARN_LOG(Log::sceNet, "UNIMPL sceNetInetTerm()"); + __NetInetShutdown(); + return 0; +} - const int ret = inet_pton(inetAddressFamily, hostname, dst); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "%s: inet_pton returned %i: %s", __func__, sceNetInet->GetLastError(), strerror(error)); - } - return hleLogSuccessI(Log::sceNet, ret); +static int sceNetInetGetErrno() { + if (inetLastErrno == 0) + inetLastErrno = errno; + int error = convertInetErrnoHost2PSP(inetLastErrno); + inetLastErrno = 0; + return hleLogSuccessI(Log::sceNet, error, " at %08x", currentMIPS->pc); } -static int sceNetInetGetpeername(int socket, u32 addrPtr, u32 addrLenPtr) { - ERROR_LOG(Log::sceNet, "UNTESTED sceNetInetGetsockname(%i, %08x, %08x)", socket, addrPtr, addrLenPtr); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } +static int sceNetInetGetPspError() { + if (inetLastErrno == 0) + inetLastErrno = errno; + int error = convertInetErrno2PSPError(convertInetErrnoHost2PSP(inetLastErrno)); + inetLastErrno = 0; + return hleLogSuccessX(Log::sceNet, error, " at %08x", currentMIPS->pc); +} - int nativeSocketId; - if (!sceNetInet->GetNativeSocketIdForInetSocketId(nativeSocketId, socket)) { - ERROR_LOG(Log::sceNet, "%s: Requested socket %i which does not exist", __func__, socket); - return -1; +static int sceNetInetInetPton(int af, const char* hostname, u32 inAddrPtr) { + WARN_LOG(Log::sceNet, "UNTESTED sceNetInetInetPton(%i, %s, %08x)", af, safe_string(hostname), inAddrPtr); + if (!Memory::IsValidAddress(inAddrPtr)) { + return hleLogError(Log::sceNet, 0, "invalid arg"); //-1 } - // Write PSP sockaddr to native sockaddr in preparation of getpeername - sockaddr_in sockaddrIn{}; - socklen_t socklen = sizeof(sockaddr_in); - if (!inetSockaddrToNativeSocketAddr(sockaddrIn, addrPtr, addrLenPtr)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i]: %s: Encountered invalid addrPtr %08x and/or invalid addrLenPtr %08x", nativeSocketId, addrPtr, addrLenPtr); - } + int retval = inet_pton(convertSocketDomainPSP2Host(af), hostname, (void*)Memory::GetPointer(inAddrPtr)); + return hleLogSuccessI(Log::sceNet, retval); +} - const int ret = getpeername(nativeSocketId, reinterpret_cast(&sockaddrIn), &socklen); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "[%i] %s: Failed to execute getpeername %i: %s", nativeSocketId, __func__, error, strerror(error)); +static int sceNetInetInetAton(const char* hostname, u32 inAddrPtr) { + WARN_LOG(Log::sceNet, "UNTESTED sceNetInetInetAton(%s, %08x)", safe_string(hostname), inAddrPtr); + if (!Memory::IsValidAddress(inAddrPtr)) { + return hleLogError(Log::sceNet, 0, "invalid arg"); //-1 } - // Write output of getpeername to the input addrPtr - if (!writeSockAddrInToInetSockAddr(addrPtr, addrLenPtr, sockaddrIn)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Failed to write results of getpeername to SceNetInetSockaddrIn", nativeSocketId, __func__); - } - return hleLogSuccessI(Log::sceNet, ret); + int retval = inet_pton(AF_INET, hostname, (void*)Memory::GetPointer(inAddrPtr)); + // inet_aton() returns nonzero if the address is valid, zero if not. + return hleLogSuccessI(Log::sceNet, retval); } -static int sceNetInetGetsockname(int socket, u32 addrPtr, u32 addrLenPtr) { - ERROR_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __func__, socket, addrPtr, addrLenPtr); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); +// TODO: Need to find out whether it's possible to get partial output or not, since Coded Arms Contagion is using a small bufsize(4) +static u32 sceNetInetInetNtop(int af, u32 srcInAddrPtr, u32 dstBufPtr, u32 bufsize) { + WARN_LOG(Log::sceNet, "UNTESTED sceNetInetInetNtop(%i, %08x, %08x, %d)", af, srcInAddrPtr, dstBufPtr, bufsize); + if (!Memory::IsValidAddress(srcInAddrPtr)) { + return hleLogError(Log::sceNet, 0, "invalid arg"); } - - int nativeSocketId; - if (!sceNetInet->GetNativeSocketIdForInetSocketId(nativeSocketId, socket)) { - sceNetInet->SetLastError(EINVAL); - return hleLogError(Log::sceNet, -1, "%s: Requested socket %i which does not exist", __func__, socket); + if (!Memory::IsValidAddress(dstBufPtr) || bufsize < 1/*8*/) { // usually 8 or 16, but Coded Arms Contagion is using bufsize = 4 + inetLastErrno = ENOSPC; + return hleLogError(Log::sceNet, 0, "invalid arg"); } - // Set sockaddrIn to the result of getsockname - sockaddr_in sockaddrIn{}; - socklen_t socklen = sizeof(sockaddr_in); - const int ret = getsockname(nativeSocketId, reinterpret_cast(&sockaddrIn), &socklen); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "[%i] %s: Failed to execute getsockname %i: %s", nativeSocketId, __func__, error, strerror(error)); + if (inet_ntop(convertSocketDomainPSP2Host(af), Memory::GetCharPointer(srcInAddrPtr), (char*)Memory::GetCharPointer(dstBufPtr), bufsize) == NULL) { + //return hleLogDebug(Log::sceNet, 0, "invalid arg?"); // Temporarily commented out in case it's allowed to have partial output } - - // Write output of getsockname to the input addrPtr - if (!writeSockAddrInToInetSockAddr(addrPtr, addrLenPtr, sockaddrIn)) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Failed to write results of getsockname to SceNetInetSockaddrIn", nativeSocketId, __func__); - } - return hleLogSuccessI(Log::sceNet, ret); + return hleLogSuccessX(Log::sceNet, dstBufPtr, "%s", safe_string(Memory::GetCharPointer(dstBufPtr))); } -static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, int flags) { - WARN_LOG_ONCE(sceNetInetRecv, Log::sceNet, "UNTESTED sceNetInetRecv(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to close socket %i which does not exist", __func__, socket); - } +static u32_le sceNetInetInetAddr(const char* hostname) { + WARN_LOG(Log::sceNet, "UNTESTED sceNetInetInetAddr(%s)", safe_string(hostname)); + if (hostname == nullptr || hostname[0] == '\0') + return hleLogError(Log::sceNet, INADDR_NONE, "invalid arg"); - const auto nativeSocketId = inetSocket->GetNativeSocketId(); - const auto dstBuf = Memory::GetTypedPointerWriteRange(bufPtr, bufLen); - if (dstBuf == nullptr) { - return hleLogError(Log::sceNet, -1, "[%i] %s: Invalid pointer %08x (size %i)", nativeSocketId, __func__, bufPtr, bufLen); - } + u32 retval = INADDR_NONE; // inet_addr(hostname); // deprecated? + inet_pton(AF_INET, hostname, &retval); // Alternative to the deprecated inet_addr - const int nativeFlags = SceNetInet::TranslateInetFlagsToNativeFlags(flags, inetSocket->IsNonBlocking()); - const int ret = recv(nativeSocketId, dstBuf, bufLen, nativeFlags); - DEBUG_LOG(Log::sceNet, "[%i] %s: Called recv with buf size %i which returned %i", nativeSocketId, __func__, bufLen, ret); - if (ret < 0) { - if (const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - error != ERROR_WHEN_NONBLOCKING_CALL_OCCURS) { - ERROR_LOG(Log::sceNet, "[%i]: %s: recv() encountered error %i: %s", socket, __func__, error, strerror(error)); - } - } - return hleLogSuccessI(Log::sceNet, ret); + return hleLogSuccessX(Log::sceNet, retval); } -static int sceNetInetRecvfrom(int socket, u32 bufPtr, u32 bufLen, int flags, u32 fromAddr, u32 fromLenAddr) { - WARN_LOG_ONCE(sceNetInetRecvFrom, Log::sceNet, "UNTESTED sceNetInetRecvfrom(%i, %08x, %i, %08x, %08x, %08x)", socket, bufPtr, bufLen, flags, fromAddr, fromLenAddr); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); +static int sceNetInetGetpeername(int sock, u32 namePtr, u32 namelenPtr) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, sock, namePtr, namelenPtr); + if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) { + inetLastErrno = EFAULT; + return hleLogError(Log::sceNet, -1, "invalid arg"); + } + + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); + int* namelen = (int*)Memory::GetPointer(namelenPtr); + SockAddrIN4 saddr{}; + // TODO: Should've created convertSockaddrPSP2Host (and Host2PSP too) function as it's being used pretty often, thus fixing a bug on it will be tedious when scattered all over the places + saddr.addr.sa_family = name->sa_family; + int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); + memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); + int retval = getpeername(sock, (sockaddr*)&saddr, (socklen_t*)&len); + DEBUG_LOG(Log::sceNet, "Getpeername: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + *namelen = len; + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + } else { + memcpy(name->sa_data, saddr.addr.sa_data, len - (sizeof(name->sa_len) + sizeof(name->sa_family))); + name->sa_len = len; + name->sa_family = saddr.addr.sa_family; } + return 0; +} - const auto nativeSocketId = inetSocket->GetNativeSocketId(); - const auto dstBuf = Memory::GetTypedPointerWriteRange(bufPtr, bufLen); - if (dstBuf == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Invalid pointer range: %08x (size %i)", nativeSocketId, __func__, bufPtr, bufLen); +static int sceNetInetGetsockname(int sock, u32 namePtr, u32 namelenPtr) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, sock, namePtr, namelenPtr); + if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) { + inetLastErrno = EFAULT; + return hleLogError(Log::sceNet, -1, "invalid arg"); + } + + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); + int* namelen = (int*)Memory::GetPointer(namelenPtr); + SockAddrIN4 saddr{}; + saddr.addr.sa_family = name->sa_family; + int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); + memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); + int retval = getsockname(sock, (sockaddr*)&saddr, (socklen_t*)&len); + DEBUG_LOG(Log::sceNet, "Getsockname: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + *namelen = len; + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + } else { + memcpy(name->sa_data, saddr.addr.sa_data, len - (sizeof(name->sa_len) + sizeof(name->sa_family))); + name->sa_len = len; + name->sa_family = saddr.addr.sa_family; } + return 0; +} - // Translate PSP flags to native flags and prepare sockaddrIn to receive peer address - const int nativeFlags = SceNetInet::TranslateInetFlagsToNativeFlags(flags, inetSocket->IsNonBlocking()); - sockaddr_in sockaddrIn{}; - socklen_t socklen = sizeof(sockaddr_in); - Memory::Memset(bufPtr, 0, bufLen, __func__); - - const int ret = recvfrom(nativeSocketId, dstBuf, bufLen, nativeFlags, reinterpret_cast(&sockaddrIn), &socklen); - if (ret < 0) { - if (const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - error != 0 && error != ERROR_WHEN_NONBLOCKING_CALL_OCCURS) { - WARN_LOG(Log::sceNet, "[%i] %s: Received error %i: %s", nativeSocketId, __func__, error, strerror(error)); +// FIXME: nfds is number of fd(s) as in posix poll, or was it maximum fd value as in posix select? Star Wars Battlefront Renegade seems to set the nfds to 64, while Coded Arms Contagion is using 256 +int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr, u32 timeoutPtr) { + DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSelect(%i, %08x, %08x, %08x, %08x) at %08x", nfds, readfdsPtr, writefdsPtr, exceptfdsPtr, timeoutPtr, currentMIPS->pc); + int retval = -1; + SceNetInetFdSet* readfds = (SceNetInetFdSet*)Memory::GetCharPointer(readfdsPtr); + SceNetInetFdSet* writefds = (SceNetInetFdSet*)Memory::GetCharPointer(writefdsPtr); + SceNetInetFdSet* exceptfds = (SceNetInetFdSet*)Memory::GetCharPointer(exceptfdsPtr); + SceNetInetTimeval* timeout = (SceNetInetTimeval*)Memory::GetCharPointer(timeoutPtr); + // TODO: Use poll instead of select since Windows' FD_SETSIZE is only 64 while PSP is 256, and select can only work for fd value less than FD_SETSIZE on some system + fd_set rdfds{}, wrfds{}, exfds{}; + FD_ZERO(&rdfds); FD_ZERO(&wrfds); FD_ZERO(&exfds); + int maxfd = nfds; // (nfds > PSP_NET_INET_FD_SETSIZE) ? nfds : PSP_NET_INET_FD_SETSIZE; + int rdcnt = 0, wrcnt = 0, excnt = 0; + for (int i = maxfd - 1; i >= 0 /*&& i >= maxfd - 64*/; i--) { + bool windows_workaround = false; +#if PPSSPP_PLATFORM(WINDOWS) + //windows_workaround = (i == nfds - 1); +#endif + if (readfds != NULL && (NetInetFD_ISSET(i, readfds) || windows_workaround)) { + VERBOSE_LOG(Log::sceNet, "Input Read FD #%i", i); + if (rdcnt < FD_SETSIZE) { + FD_SET(i, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 + rdcnt++; + } + } + if (writefds != NULL && (NetInetFD_ISSET(i, writefds) || windows_workaround)) { + VERBOSE_LOG(Log::sceNet, "Input Write FD #%i", i); + if (wrcnt < FD_SETSIZE) { + FD_SET(i, &wrfds); + wrcnt++; + } + } + if (exceptfds != NULL && (NetInetFD_ISSET(i, exceptfds) || windows_workaround)) { + VERBOSE_LOG(Log::sceNet, "Input Except FD #%i", i); + if (excnt < FD_SETSIZE) { + FD_SET(i, &exfds); + excnt++; + } } - return hleDelayResult(ret, "TODO: unhack", 500); } - - // If ret was successful, write peer sockaddr to input fromAddr - if (ret > 0) { - if (!writeSockAddrInToInetSockAddr(fromAddr, fromLenAddr, sockaddrIn)) { - ERROR_LOG(Log::sceNet, "[%i] %s: Error writing native sockaddr to sceSockaddr", nativeSocketId, __func__); + // Workaround for games that set ndfs to 64 instead of socket id + 1 + if (inetLastSocket >= 0) { + if (readfds != NULL && rdcnt == 0) { + FD_SET(inetLastSocket, &rdfds); + rdcnt++; + } + if (writefds != NULL && wrcnt == 0) { + FD_SET(inetLastSocket, &wrfds); + wrcnt++; + } + if (exceptfds != NULL && excnt == 0) { + FD_SET(inetLastSocket, &exfds); + excnt++; } - INFO_LOG(Log::sceNet, "[%i] %s: Got %i bytes from recvfrom", nativeSocketId, __func__, ret); } - return hleDelayResult(ret, "TODO: unhack", 500); + + timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL + if (timeout != NULL) { + tmout.tv_sec = timeout->tv_sec; + tmout.tv_usec = timeout->tv_usec; + } + VERBOSE_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, tmout.tv_sec, tmout.tv_usec); + // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing + retval = select(nfds, (readfds == NULL) ? NULL : &rdfds, (writefds == NULL) ? NULL : &wrfds, (exceptfds == NULL) ? NULL : &exfds, /*(timeout == NULL) ? NULL :*/ &tmout); + if (readfds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(readfds); // Clear it only when not needing a workaround + if (writefds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(writefds); // Clear it only when not needing a workaround + if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); + for (int i = maxfd - 1; i >= 0 /*&& i >= maxfd - 64*/; i--) { + if (readfds != NULL && FD_ISSET(i, &rdfds)) + NetInetFD_SET(i, readfds); + if (writefds != NULL && FD_ISSET(i, &wrfds)) + NetInetFD_SET(i, writefds); + if (exceptfds != NULL && FD_ISSET(i, &exfds)) + NetInetFD_SET(i, exceptfds); + } + // Workaround for games that set ndfs to 64 instead of socket id + 1 + if (inetLastSocket >= 0) { + if (readfds != NULL && rdcnt == 1 && FD_ISSET(inetLastSocket, &rdfds)) + NetInetFD_SET(inetLastSocket, readfds); + if (writefds != NULL && wrcnt == 1 && FD_ISSET(inetLastSocket, &wrfds)) + NetInetFD_SET(inetLastSocket, writefds); + if (exceptfds != NULL && excnt == 1 && FD_ISSET(inetLastSocket, &exfds)) + NetInetFD_SET(inetLastSocket, exceptfds); + } + + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == 0) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else if (inetLastErrno < 0) + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return hleLogDebug(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) + } + return hleLogSuccessI(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) } -static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, int flags) { - WARN_LOG_ONCE(sceNetInetSend, Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x)", __func__, socket, bufPtr, bufLen, flags); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } +int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in miliseconds just like posix poll? or in microseconds as other PSP timeout? + DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetPoll(%08x, %d, %i) at %08x", fdsPtr, nfds, timeout, currentMIPS->pc); + int retval = -1; + int maxfd = 0; + SceNetInetPollfd *fdarray = (SceNetInetPollfd*)Memory::GetPointer(fdsPtr); // SceNetInetPollfd/pollfd, sceNetInetPoll() have similarity to BSD poll() but pollfd have different size on 64bit - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); - } + if (nfds > FD_SETSIZE) + nfds = FD_SETSIZE; - const auto buf = Memory::GetTypedPointerRange(bufPtr, bufLen); - if (buf == nullptr) { - sceNetInet->SetLastError(EFAULT); - return hleLogError(Log::sceNet, -1, "[%i] %s: Invalid pointer range: %08x (size %i)", socket, __func__, bufPtr, bufLen); + fd_set readfds{}, writefds{}, exceptfds{}; + FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); + for (int i = 0; i < (s32)nfds; i++) { + if (fdarray[i].fd < 0) { + inetLastErrno = EINVAL; + return hleLogError(Log::sceNet, -1, "invalid socket id"); + } + if (fdarray[i].fd > maxfd) maxfd = fdarray[i].fd; + FD_SET(fdarray[i].fd, &readfds); + FD_SET(fdarray[i].fd, &writefds); + FD_SET(fdarray[i].fd, &exceptfds); + fdarray[i].revents = 0; } - - // Translate PSP flags to native flags and send - const int nativeFlags = SceNetInet::TranslateInetFlagsToNativeFlags(flags, inetSocket->IsNonBlocking()); - const int ret = send(inetSocket->GetNativeSocketId(), buf, bufLen, nativeFlags); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - return hleLogError(Log::sceNet, ret, "[%i]: %s: send() encountered error %i: %s", socket, __func__, error, strerror(error)); + timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL + if (timeout >= 0) { + tmout.tv_sec = timeout / 1000000; // seconds + tmout.tv_usec = (timeout % 1000000); // microseconds } - return hleLogSuccessI(Log::sceNet, ret); -} - -static int sceNetInetSendto(int socket, u32 bufPtr, u32 bufLen, int flags, u32 toAddr, u32 toLen) { - ERROR_LOG_ONCE(sceNetInetSendto, Log::sceNet, "UNTESTED sceNetInetSendto(%i, %08x, %i, %08x, %08x, %i)", socket, bufPtr, bufLen, flags, toAddr, toLen); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + // TODO: Simulate blocking behaviour when timeout is non-zero to prevent PPSSPP from freezing + retval = select(maxfd + 1, &readfds, &writefds, &exceptfds, /*(timeout<0)? NULL:*/&tmout); + if (retval < 0) { + inetLastErrno = EINTR; + return hleLogError(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented } - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); + retval = 0; + for (int i = 0; i < (s32)nfds; i++) { + if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(fdarray[i].fd, &readfds)) + fdarray[i].revents |= (INET_POLLRDNORM | INET_POLLIN); //POLLIN_SET + if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(fdarray[i].fd, &writefds)) + fdarray[i].revents |= (INET_POLLWRNORM | INET_POLLOUT); //POLLOUT_SET + fdarray[i].revents &= fdarray[i].events; + if (FD_ISSET(fdarray[i].fd, &exceptfds)) + fdarray[i].revents |= (INET_POLLRDBAND | INET_POLLPRI | INET_POLLERR); //POLLEX_SET; // Can be raised on revents regardless of events bitmask? + if (fdarray[i].revents) + retval++; + VERBOSE_LOG(Log::sceNet, "Poll Socket#%d Fd: %d, events: %04x, revents: %04x, availToRecv: %d", i, fdarray[i].fd, fdarray[i].events, fdarray[i].revents, getAvailToRecv(fdarray[i].fd)); } + //hleEatMicro(1000); + return hleLogSuccessI(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 1000)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented +} - const int nativeSocketId = inetSocket->GetNativeSocketId(); - const auto srcBuf = Memory::GetTypedPointerRange(bufPtr, bufLen); - if (srcBuf == nullptr) { - ERROR_LOG(Log::sceNet, "[%i] %s: Invalid pointer range: %08x (size %i)", nativeSocketId, __func__, bufPtr, bufLen); - return -1; - } +static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { + DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetRecv(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + int retval = recv(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return hleDelayResult(retval, "workaround until blocking-socket", 500); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented + } + + std::string datahex; + DataToHexString(10, 0, Memory::GetPointer(bufPtr), retval, &datahex); + VERBOSE_LOG(Log::sceNet, "Data Dump (%d bytes):\n%s", retval, datahex.c_str()); + + return hleLogSuccessInfoI(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented +} - // Translate PSP flags to native flags and convert toAddr to native addr - const int nativeFlags = SceNetInet::TranslateInetFlagsToNativeFlags(flags, inetSocket->IsNonBlocking()); - sockaddr_in convertedSockAddr{}; - if (!inetSockaddrToNativeSocketAddr(convertedSockAddr, toAddr, toLen)) { - ERROR_LOG(Log::sceNet, "[%i] %s: Unable to translate sceSockAddr to native sockaddr", nativeSocketId, __func__); - return -1; - } +static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { + DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); - DEBUG_LOG(Log::sceNet, "[%i] %s: Writing %i bytes to %s on port %i", nativeSocketId, __func__, bufLen, ip2str(convertedSockAddr.sin_addr, false).c_str(), ntohs(convertedSockAddr.sin_port)); + std::string datahex; + DataToHexString(10, 0, Memory::GetPointer(bufPtr), bufLen, &datahex); + VERBOSE_LOG(Log::sceNet, "Data Dump (%d bytes):\n%s", bufLen, datahex.c_str()); - const int ret = sendto(nativeSocketId, srcBuf, bufLen, nativeFlags, reinterpret_cast(&convertedSockAddr), sizeof(sockaddr_in)); - DEBUG_LOG(Log::sceNet, "[%i] %s: sendto returned %i", nativeSocketId, __func__, ret); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + int retval = send(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); - if (ret < 0) { - const auto error = sceNetInet->SetLastErrorToMatchPlatform(); - WARN_LOG(Log::sceNet, "[%i] %s: Got error %i=%s", nativeSocketId, __func__, error, strerror(error)); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return retval; } - return hleLogSuccessI(Log::sceNet, ret); + return hleLogSuccessInfoI(Log::sceNet, retval); } -static int sceNetInetGetErrno() { - ERROR_LOG_ONCE(sceNetInetGetErrno, Log::sceNet, "UNTESTED sceNetInetGetErrno()"); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); - } - - const auto nativeError = sceNetInet->GetLastError(); - if (nativeError != ERROR_WHEN_NONBLOCKING_CALL_OCCURS && nativeError != 0) { - INFO_LOG(Log::sceNet, "Requested %s %i=%s", __func__, nativeError, strerror(nativeError)); - } - - return SceNetInet::TranslateNativeErrorToInetError(nativeError); +static int sceNetInetSocket(int domain, int type, int protocol) { + WARN_LOG(Log::sceNet, "UNTESTED sceNetInetSocket(%i, %i, %i) at %08x", domain, type, protocol, currentMIPS->pc); + DEBUG_LOG(Log::sceNet, "Socket: Domain = %s, Type = %s, Protocol = %s", inetSocketDomain2str(domain).c_str(), inetSocketType2str(type).c_str(), inetSocketProto2str(protocol).c_str()); + int retval = socket(convertSocketDomainPSP2Host(domain), convertSocketTypePSP2Host(type), convertSocketProtoPSP2Host(protocol)); + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + } + + //InetSocket* sock = new InetSocket(domain, type, protocol); + //retval = pspSockets.Create(sock); + + // Ignore SIGPIPE when supported (ie. BSD/MacOS) + setSockNoSIGPIPE(retval, 1); + // TODO: We should always use non-blocking mode and simulate blocking mode + changeBlockingMode(retval, 1); + // Enable Port Re-use, required for multiple-instance + setSockReuseAddrPort(retval); + // Disable Connection Reset error on UDP to avoid strange behavior + setUDPConnReset(retval, false); + + inetLastSocket = retval; + return hleLogSuccessI(Log::sceNet, retval); } -static int sceNetInetBind(int socket, u32 addrPtr, u32 addrLen) { - WARN_LOG_ONCE(sceNetInetSend, Log::sceNet, "UNTESTED sceNetInetBind(%i, %08x, %08x)", socket, addrPtr, addrLen); - const auto sceNetInet = SceNetInet::Get(); - if (!sceNetInet) { - return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); +static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i, %i, %08x, %i) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlen, currentMIPS->pc); + u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); + DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s, OptValue = %d", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str(), *optval); + timeval tval{}; + // InetSocket* sock = pspSockets.Get(socket, error); + // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) + if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { + //memcpy(&sock->nonblocking, (int*)optval, std::min(sizeof(sock->nonblocking), optlen)); + return hleLogSuccessI(Log::sceNet, 0); + } + // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), + // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP + /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { + //memcpy(&sock->so_broadcast, (int*)optval, std::min(sizeof(sock->so_broadcast), optlen)); + return hleLogSuccessI(Log::sceNet, 0); + }*/ + // TODO: Ignoring SO_REUSEADDR flag to prevent disrupting multiple-instance feature + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEADDR) { + //memcpy(&sock->reuseaddr, (int*)optval, std::min(sizeof(sock->reuseaddr), optlen)); + return hleLogSuccessI(Log::sceNet, 0); + } + // TODO: Ignoring SO_REUSEPORT flag to prevent disrupting multiple-instance feature (not sure if PSP has SO_REUSEPORT or not tho, defined as 15 on Android) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEPORT) { // 15 + //memcpy(&sock->reuseport, (int*)optval, std::min(sizeof(sock->reuseport), optlen)); + return hleLogSuccessI(Log::sceNet, 0); + } + // TODO: Ignoring SO_NOSIGPIPE flag to prevent crashing PPSSPP (not sure if PSP has NOSIGPIPE or not tho, defined as 0x1022 on Darwin) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == 0x1022) { // PSP_NET_INET_SO_NOSIGPIPE ? + //memcpy(&sock->nosigpipe, (int*)optval, std::min(sizeof(sock->nosigpipe), optlen)); + return hleLogSuccessI(Log::sceNet, 0); + } + // It seems UNO game will try to set socket buffer size with a very large size and ended getting error (-1), so we should also limit the buffer size to replicate PSP behavior + else if (level == PSP_NET_INET_SOL_SOCKET && (optname == PSP_NET_INET_SO_RCVBUF || optname == PSP_NET_INET_SO_SNDBUF)) { // PSP_NET_INET_SO_NOSIGPIPE ? + // TODO: For SOCK_STREAM max buffer size is 8 Mb on BSD, while max SOCK_DGRAM is 65535 minus the IP & UDP Header size + if (*optval > 8 * 1024 * 1024) { + inetLastErrno = ENOBUFS; // FIXME: return ENOBUFS for SOCK_STREAM, and EINVAL for SOCK_DGRAM + return hleLogError(Log::sceNet, -1, "buffer size too large?"); + } } - - const auto inetSocket = sceNetInet->GetInetSocket(socket); - if (!inetSocket) { - sceNetInet->SetLastError(EBADF); - return hleLogError(Log::sceNet, -1, "%s: Attempting to operate on unmapped socket %i", __func__, socket); + int retval = 0; + // PSP timeout are a single 32bit value (micro seconds) + if (level == PSP_NET_INET_SOL_SOCKET && optval && (optname == PSP_NET_INET_SO_RCVTIMEO || optname == PSP_NET_INET_SO_SNDTIMEO)) { + tval.tv_sec = *optval / 1000000; // seconds + tval.tv_usec = (*optval % 1000000); // microseconds + retval = setsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, sizeof(tval)); + } else { + retval = setsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); } - - const int nativeSocketId = inetSocket->GetNativeSocketId(); - - // Convert PSP bind addr to native bind addr - sockaddr_in convertedSockaddr{}; - if (!inetSockaddrToNativeSocketAddr(convertedSockaddr, addrPtr, addrLen)) { - ERROR_LOG(Log::sceNet, "[%i] Error translating sceSockaddr to native sockaddr", nativeSocketId); - return -1; + if (retval < 0) { + inetLastErrno = errno; + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); } - socklen_t socklen = sizeof(convertedSockaddr); + return hleLogSuccessI(Log::sceNet, retval); +} - // Get default outbound sockaddr when INADDR_ANY or INADDR_BROADCAST are used - if (const auto addr = convertedSockaddr.sin_addr.s_addr; addr == INADDR_ANY || addr == INADDR_BROADCAST) { - if (!getDefaultOutboundSockaddr(convertedSockaddr, socklen)) { - WARN_LOG(Log::sceNet, "Failed to get default bound address"); - return -1; +static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPtr, u32 optlenPtr) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i, %i, %08x, %08x) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlenPtr, currentMIPS->pc); + u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); + socklen_t* optlen = (socklen_t*)Memory::GetPointer(optlenPtr); + DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str()); + timeval tval{}; + // InetSocket* sock = pspSockets.Get(socket, error); + // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) + if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { + //*optlen = std::min(sizeof(sock->nonblocking), *optlen); + //memcpy((int*)optval, &sock->nonblocking, *optlen); + //if (sock->nonblocking && *optlen>0) *optval = 0x80; // on true, returning 0x80 when retrieved using getsockopt? + return hleLogSuccessI(Log::sceNet, 0); + } + // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), + // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP + /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { + //*optlen = std::min(sizeof(sock->so_broadcast), *optlen); + //memcpy((int*)optval, &sock->so_broadcast, *optlen); + //if (sock->so_broadcast && *optlen>0) *optval = 0x80; // on true, returning 0x80 when retrieved using getsockopt? + return hleLogSuccessI(Log::sceNet, 0); + }*/ + // TODO: Ignoring SO_REUSEADDR flag to prevent disrupting multiple-instance feature + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEADDR) { + //*optlen = std::min(sizeof(sock->reuseaddr), *optlen); + //memcpy((int*)optval, &sock->reuseaddr, *optlen); + return hleLogSuccessI(Log::sceNet, 0); + } + // TODO: Ignoring SO_REUSEPORT flag to prevent disrupting multiple-instance feature (not sure if PSP has SO_REUSEPORT or not tho, defined as 15 on Android) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEPORT) { // 15 + //*optlen = std::min(sizeof(sock->reuseport), *optlen); + //memcpy((int*)optval, &sock->reuseport, *optlen); + return hleLogSuccessI(Log::sceNet, 0); + } + // TODO: Ignoring SO_NOSIGPIPE flag to prevent crashing PPSSPP (not sure if PSP has NOSIGPIPE or not tho, defined as 0x1022 on Darwin) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == 0x1022) { // PSP_NET_INET_SO_NOSIGPIPE ? + //*optlen = std::min(sizeof(sock->nosigpipe), *optlen); + //memcpy((int*)optval, &sock->nosigpipe, *optlen); + return hleLogSuccessI(Log::sceNet, 0); + } + int retval = 0; + // PSP timeout are a single 32bit value (micro seconds) + if (level == PSP_NET_INET_SOL_SOCKET && optval && (optname == PSP_NET_INET_SO_RCVTIMEO || optname == PSP_NET_INET_SO_SNDTIMEO)) { + socklen_t tvlen = sizeof(tval); + retval = getsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, &tvlen); + if (retval != SOCKET_ERROR) { + u64_le val = (tval.tv_sec * 1000000LL) + tval.tv_usec; + memcpy(optval, &val, std::min(static_cast(sizeof(val)), std::min(static_cast(sizeof(*optval)), *optlen))); } + } else { + retval = getsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); } - - // TODO: check whether setting to blocking and then non-blocking is valid - setBlockingMode(nativeSocketId, false); - INFO_LOG(Log::sceNet, "[%i] Binding to family %i, port %i, addr %s", nativeSocketId, convertedSockaddr.sin_family, ntohs(convertedSockaddr.sin_port), ip2str(convertedSockaddr.sin_addr, false).c_str()); - const int ret = bind(nativeSocketId, reinterpret_cast(&convertedSockaddr), socklen); - INFO_LOG(Log::sceNet, "Bind returned %i for fd=%i", ret, nativeSocketId); - setBlockingMode(nativeSocketId, inetSocket->IsNonBlocking()); - - // Set UPnP - const auto port = ntohs(convertedSockaddr.sin_port); - switch (inetSocket->GetProtocol()) { - case IPPROTO_UDP: { - UPnP_Add(IP_PROTOCOL_UDP, port, port); - break; - } - case IPPROTO_IP: - case IPPROTO_TCP: { - UPnP_Add(IP_PROTOCOL_TCP, port, port); - break; - } - // TODO: Unknown IP protocol 000f when attempting to set up UPnP port forwarding - default: { - WARN_LOG(Log::sceNet, "[%i]: Unknown IP protocol %04x when attempting to set up UPnP port forwarding", nativeSocketId, inetSocket->GetProtocol()); - break; - } + if (retval < 0) { + inetLastErrno = errno; + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); } - return hleLogSuccessI(Log::sceNet, ret); + DEBUG_LOG(Log::sceNet, "SockOpt: OptValue = %d", *optval); + return hleLogSuccessI(Log::sceNet, retval); } -// TODO: fix retmasks -const HLEFunction sceNetInet[] = { - {0X17943399, &WrapI_V, "sceNetInetInit", 'i', "" }, - {0X4CFE4E56, nullptr, "sceNetInetShutdown", '?', "" }, - {0XA9ED66B9, &WrapI_V, "sceNetInetTerm", 'i', "" }, - {0X8B7B220F, &WrapI_III, "sceNetInetSocket", 'i', "iii" }, - {0X4A114C7C, &WrapI_IIIUU, "sceNetInetGetsockopt", 'i', "iiixx"}, - {0X2FE71FE7, &WrapI_IIIUI, "sceNetInetSetsockopt", 'i', "iiixi"}, - {0X410B34AA, &WrapI_IUI, "sceNetInetConnect", 'i', "ixi" }, - {0X805502DD, nullptr, "sceNetInetCloseWithRST", '?', "" }, - {0XD10A1A7A, &WrapI_II, "sceNetInetListen", '?', "" }, - {0XDB094E1B, &WrapI_IUU, "sceNetInetAccept", '?', "" }, - {0XFAABB1DD, &WrapI_VUI, "sceNetInetPoll", 'i', "pxi" }, - {0X5BE8D595, &WrapI_IUUUU, "sceNetInetSelect", 'i', "ixxxx"}, - {0X8D7284EA, &WrapI_I, "sceNetInetClose", '?', "" }, - {0XCDA85C99, &WrapI_IUUI, "sceNetInetRecv", 'i', "ixxi" }, - {0XC91142E4, &WrapI_IUUIUU, "sceNetInetRecvfrom", 'i', "ixxxxx"}, - {0XEECE61D2, nullptr, "sceNetInetRecvmsg", '?', "" }, - {0X7AA671BC, &WrapI_IUUI, "sceNetInetSend", 'i', "ixxx" }, - {0X05038FC7, &WrapI_IUUIUU, "sceNetInetSendto", 'i', "ixxxxx"}, - {0X774E36F4, nullptr, "sceNetInetSendmsg", '?', "" }, - {0XFBABE411, &WrapI_V, "sceNetInetGetErrno", 'i', "" }, - {0X1A33F9AE, &WrapI_IUU, "sceNetInetBind", 'i', "" }, - {0XB75D5B0A, &WrapU_C, "sceNetInetInetAddr", 'u', "p" }, - {0X1BDF5D13, &WrapI_CU, "sceNetInetInetAton", 'i', "sx" }, - {0XD0792666, &WrapU_IUUU, "sceNetInetInetNtop", '?', "" }, - {0XE30B8C19, &WrapI_ICU, "sceNetInetInetPton", '?', "" }, - {0X8CA3A97E, nullptr, "sceNetInetGetPspError", '?', "" }, - {0XE247B6D6, &WrapI_IUU, "sceNetInetGetpeername", '?', "" }, - {0X162E6FD5, &WrapI_IUU, "sceNetInetGetsockname", '?', "" }, - {0X80A21ABD, nullptr, "sceNetInetSocketAbort", '?', "" }, - {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, - {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, -};; - -std::shared_ptr SceNetInet::gInstance; - -std::shared_mutex SceNetInet::gLock; - -std::unordered_map SceNetInet::gInetAddressFamilyToNativeAddressFamily = { - { PSP_NET_INET_AF_UNSPEC, AF_UNSPEC }, - { PSP_NET_INET_AF_LOCAL, AF_UNIX }, - { PSP_NET_INET_AF_INET, AF_INET }, -}; - -std::unordered_map SceNetInet::gInetSocketTypeToNativeSocketType = { - { PSP_NET_INET_SOCK_STREAM, SOCK_STREAM }, - { PSP_NET_INET_SOCK_DGRAM, SOCK_DGRAM }, - { PSP_NET_INET_SOCK_RAW, SOCK_RAW }, - { PSP_NET_INET_SOCK_RDM, SOCK_RDM }, - { PSP_NET_INET_SOCK_SEQPACKET, SOCK_SEQPACKET }, -}; - -std::unordered_map SceNetInet::gInetProtocolToNativeProtocol = { - { PSP_NET_INET_IPPROTO_IP, IPPROTO_IP }, - { PSP_NET_INET_IPPROTO_ICMP, IPPROTO_ICMP }, - { PSP_NET_INET_IPPROTO_IGMP, IPPROTO_IGMP }, - { PSP_NET_INET_IPPROTO_TCP, IPPROTO_TCP }, - { PSP_NET_INET_IPPROTO_EGP, IPPROTO_EGP }, - { PSP_NET_INET_IPPROTO_PUP, IPPROTO_PUP }, - { PSP_NET_INET_IPPROTO_UDP, IPPROTO_UDP }, - { PSP_NET_INET_IPPROTO_IDP, IPPROTO_IDP }, - { PSP_NET_INET_IPPROTO_RAW, IPPROTO_RAW }, -}; +static int sceNetInetBind(int socket, u32 namePtr, int namelen) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, namePtr, namelen, currentMIPS->pc); + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); + SockAddrIN4 saddr{}; + // TODO: Should've created convertSockaddrPSP2Host (and Host2PSP too) function as it's being used pretty often, thus fixing a bug on it will be tedious when scattered all over the places + saddr.addr.sa_family = name->sa_family; + int len = std::min(namelen > 0 ? namelen : 0, static_cast(sizeof(saddr))); + memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); + if (isLocalServer) { + getLocalIp(&saddr.in); + } + // FIXME: On non-Windows broadcast to INADDR_BROADCAST(255.255.255.255) might not be received by the sender itself when binded to specific IP (ie. 192.168.0.2) or INADDR_BROADCAST. + // Meanwhile, it might be received by itself when binded to subnet (ie. 192.168.0.255) or INADDR_ANY(0.0.0.0). + if (saddr.in.sin_addr.s_addr == INADDR_ANY || saddr.in.sin_addr.s_addr == INADDR_BROADCAST) { + // Replace INADDR_ANY with a specific IP in order not to send data through the wrong interface (especially during broadcast) + // Get Local IP Address + sockaddr_in sockAddr{}; + getLocalIp(&sockAddr); + DEBUG_LOG(Log::sceNet, "Bind: Address Replacement = %s => %s", ip2str(saddr.in.sin_addr).c_str(), ip2str(sockAddr.sin_addr).c_str()); + saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; + } + // TODO: Make use Port Offset only for PPSSPP to PPSSPP communications (ie. IP addresses available in the group/friendlist), otherwise should be considered as Online Service thus should use the port as is. + //saddr.in.sin_port = htons(ntohs(saddr.in.sin_port) + portOffset); + DEBUG_LOG(Log::sceNet, "Bind: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + changeBlockingMode(socket, 0); + int retval = bind(socket, (struct sockaddr*)&saddr, len); + if (retval < 0) { + inetLastErrno = errno; + changeBlockingMode(socket, 1); + return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + } + changeBlockingMode(socket, 1); + // Update binded port number if it was 0 (any port) + memcpy(name->sa_data, saddr.addr.sa_data, sizeof(name->sa_data)); + // Enable Port-forwarding + // TODO: Check the socket type/protocol for SOCK_STREAM/SOCK_DGRAM or IPPROTO_TCP/IPPROTO_UDP instead of forwarding both protocol + // InetSocket* sock = pspSockets.Get(socket, error); + // UPnP_Add((sock->type == SOCK_STREAM)? IP_PROTOCOL_TCP: IP_PROTOCOL_UDP, port, port); + unsigned short port = ntohs(saddr.in.sin_port); + UPnP_Add(IP_PROTOCOL_UDP, port, port); + UPnP_Add(IP_PROTOCOL_TCP, port, port); + + // Workaround: Send a dummy 0 size message to AdhocServer IP to make sure the socket actually bound to an address when binded with INADDR_ANY before using getsockname, seems to fix sending DGRAM from incorrect port issue on Android + /*saddr.in.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr; + saddr.in.sin_port = 0; + sendto(socket, dummyPeekBuf64k, 0, MSG_NOSIGNAL, (struct sockaddr*)&saddr, sizeof(saddr)); + */ -// Windows compat workarounds (ugly! may not work!) -#if PPSSPP_PLATFORM(WINDOWS) -#define SO_REUSEPORT (SO_BROADCAST|SO_REUSEADDR) -#define SO_TIMESTAMP 0 -#define MSG_DONTWAIT 0 -#endif - -// TODO: commented out optnames -std::unordered_map SceNetInet::gInetSocketOptnameToNativeOptname = { - { INET_SO_ACCEPTCONN, SO_ACCEPTCONN }, - { INET_SO_REUSEADDR, SO_REUSEADDR }, - { INET_SO_KEEPALIVE, SO_KEEPALIVE }, - { INET_SO_DONTROUTE, SO_DONTROUTE }, - { INET_SO_BROADCAST, SO_BROADCAST }, - // { INET_SO_USELOOPBACK, INET_SO_USELOOPBACK }, - { INET_SO_LINGER, SO_LINGER }, - { INET_SO_OOBINLINE, SO_OOBINLINE }, - { INET_SO_REUSEPORT, SO_REUSEPORT }, - { INET_SO_TIMESTAMP, SO_TIMESTAMP }, - // { INET_SO_ONESBCAST, INET_SO_ONESBCAST }, - { INET_SO_SNDBUF, SO_SNDBUF }, - { INET_SO_RCVBUF, SO_RCVBUF }, - { INET_SO_SNDLOWAT, SO_SNDLOWAT }, - { INET_SO_RCVLOWAT, SO_RCVLOWAT }, - { INET_SO_SNDTIMEO, SO_SNDTIMEO }, - { INET_SO_RCVTIMEO, SO_RCVTIMEO }, - { INET_SO_ERROR, SO_ERROR }, - { INET_SO_TYPE, SO_TYPE }, - // { INET_SO_OVERFLOWED, INET_SO_OVERFLOWED }, // or is this nonblock? SO_NBIO - { INET_SO_DEBUG, SO_DEBUG } -}; + return hleLogSuccessI(Log::sceNet, retval); +} -std::unordered_map SceNetInet::gInetMessageFlagToNativeMessageFlag = { - { INET_MSG_OOB, MSG_OOB }, - { INET_MSG_PEEK, MSG_PEEK }, - { INET_MSG_DONTROUTE, MSG_DONTROUTE }, -#if defined(MSG_EOR) - { INET_MSG_EOR, MSG_EOR }, -#endif - { INET_MSG_TRUNC, MSG_TRUNC }, - { INET_MSG_CTRUNC, MSG_CTRUNC }, - { INET_MSG_WAITALL, MSG_WAITALL }, - { INET_MSG_DONTWAIT, MSG_DONTWAIT }, -#if defined(MSG_BCAST) - { INET_MSG_BCAST, MSG_BCAST }, -#endif -#if defined(MSG_MCAST) - { INET_MSG_MCAST, MSG_MCAST }, -#endif -}; +static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, sockAddrPtr, sockAddrLen, currentMIPS->pc); + SceNetInetSockaddr* dst = (SceNetInetSockaddr*)Memory::GetPointer(sockAddrPtr); + SockAddrIN4 saddr{}; + int dstlen = std::min(sockAddrLen > 0 ? sockAddrLen : 0, static_cast(sizeof(saddr))); + saddr.addr.sa_family = dst->sa_family; + memcpy(saddr.addr.sa_data, dst->sa_data, sizeof(dst->sa_data)); + DEBUG_LOG(Log::sceNet, "Connect: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + // Workaround to avoid blocking for indefinitely + setSockTimeout(socket, SO_SNDTIMEO, 5000000); + setSockTimeout(socket, SO_RCVTIMEO, 5000000); + changeBlockingMode(socket, 0); // Use blocking mode as temporary fix for UNO, since we don't simulate blocking-mode yet + int retval = connect(socket, (struct sockaddr*)&saddr.addr, dstlen); + if (retval < 0) { + inetLastErrno = errno; + if (connectInProgress(inetLastErrno)) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + changeBlockingMode(socket, 1); + // TODO: Since we're temporarily forcing blocking-mode we'll need to change errno from ETIMEDOUT to EAGAIN + /*if (inetLastErrno == ETIMEDOUT) + inetLastErrno = EAGAIN; + */ + return hleLogDebug(Log::sceNet, retval); + } + changeBlockingMode(socket, 1); + + return hleLogSuccessI(Log::sceNet, retval); +} -std::unordered_map SceNetInet::gNativeErrorCodeToInetErrorCode = { - { EINPROGRESS, INET_EINPROGRESS } -}; +static int sceNetInetListen(int socket, int backlog) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, backlog, currentMIPS->pc); -bool SceNetInet::Init() { - auto lock = std::unique_lock(gLock); - if (gInstance) { - return false; + int retval = listen(socket, (backlog == PSP_NET_INET_SOMAXCONN ? SOMAXCONN : backlog)); + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); } - gInstance = std::make_shared(); - return true; + + return hleLogSuccessI(Log::sceNet, retval); } -bool SceNetInet::Shutdown() { - auto lock = std::unique_lock(gLock); - if (!gInstance) { - return false; - } - gInstance->CloseAllRemainingSockets(); - gInstance = nullptr; - return true; +static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, addrPtr, addrLenPtr, currentMIPS->pc); + SceNetInetSockaddr* src = (SceNetInetSockaddr*)Memory::GetCharPointer(addrPtr); + socklen_t* srclen = (socklen_t*)Memory::GetCharPointer(addrLenPtr); + SockAddrIN4 saddr{}; + if (srclen) + *srclen = std::min((*srclen) > 0 ? *srclen : 0, static_cast(sizeof(saddr))); + int retval = accept(socket, (struct sockaddr*)&saddr.addr, srclen); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return retval; + } + + if (src) { + src->sa_family = saddr.addr.sa_family; + memcpy(src->sa_data, saddr.addr.sa_data, sizeof(src->sa_data)); + src->sa_len = srclen ? *srclen : 0; + } + DEBUG_LOG(Log::sceNet, "Accept: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + return hleLogSuccessI(Log::sceNet, retval); } -bool SceNetInet::TranslateInetAddressFamilyToNative(int &destAddressFamily, int srcAddressFamily) { - const auto it = gInetAddressFamilyToNativeAddressFamily.find(static_cast(srcAddressFamily)); - if (it == gInetAddressFamilyToNativeAddressFamily.end()) { - return false; +static int sceNetInetShutdown(int socket, int how) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, how, currentMIPS->pc); + // Convert HOW from PSP to Host + int hostHow = how; + switch (how) { + case PSP_NET_INET_SHUT_RD: hostHow = SHUT_RD; break; + case PSP_NET_INET_SHUT_WR: hostHow = SHUT_WR; break; + case PSP_NET_INET_SHUT_RDWR: hostHow = SHUT_RDWR; break; } - destAddressFamily = it->second; - return true; + return hleLogSuccessI(Log::sceNet, shutdown(socket, hostHow)); } -bool SceNetInet::TranslateInetSocketLevelToNative(int &destSocketLevel, int srcSocketLevel) { - switch (srcSocketLevel) { - case PSP_NET_INET_IPPROTO_IP: - destSocketLevel = IPPROTO_IP; - return true; - case PSP_NET_INET_IPPROTO_TCP: - destSocketLevel = IPPROTO_TCP; - return true; - case PSP_NET_INET_IPPROTO_UDP: - destSocketLevel = IPPROTO_UDP; - return true; - case PSP_NET_INET_SOL_SOCKET: - destSocketLevel = SOL_SOCKET; - return true; - default: - return false; - } +static int sceNetInetSocketAbort(int socket) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i)", __FUNCTION__, socket); + // FIXME: either using shutdown/close or select? probably using select if blocking mode is being simulated with non-blocking + return hleLogSuccessI(Log::sceNet, shutdown(socket, SHUT_RDWR)); } -bool SceNetInet::TranslateInetSocketTypeToNative(int &destSocketType, bool &destNonBlocking, int srcSocketType) { - // First, take the base socket type - const int baseSocketType = static_cast(srcSocketType & PSP_NET_INET_SOCK_TYPE_MASK); - const auto it = gInetSocketTypeToNativeSocketType.find(static_cast(baseSocketType)); - if (it == gInetSocketTypeToNativeSocketType.end()) { - return false; - } - // Set base value for dest - destSocketType = it->second; - // Find any flags which are set, noting that this highly depends on the native platform and unknowns are ignored - const int srcFlags = srcSocketType & PSP_NET_INET_SOCK_FLAGS_MASK; -#if defined(SOCK_DCCP) - if ((srcFlags & PSP_NET_INET_SOCK_DCCP) != 0) { - destSocketType |= SOCK_DCCP; - } -#endif -#if defined(SOCK_PACKET) - if ((srcFlags & PSP_NET_INET_SOCK_PACKET) != 0) { - destSocketType |= SOCK_PACKET; - } -#endif -#if defined(SOCK_CLOEXEC) - if ((srcFlags & PSP_NET_INET_SOCK_CLOEXEC) != 0) { - destSocketType |= SOCK_CLOEXEC; - } -#endif - if ((srcFlags & PSP_NET_INET_SOCK_NONBLOCK) != 0) { - destNonBlocking = true; - } -#if defined(SOCK_NOSIGPIPE) - if ((srcFlags & PSP_NET_INET_SOCK_NOSIGPIPE) != 0) { - destSocketType |= SOCK_NOSIGPIPE; - } -#endif - return true; +static int sceNetInetClose(int socket) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); + return hleLogSuccessI(Log::sceNet, closesocket(socket)); } -bool SceNetInet::TranslateInetProtocolToNative(int &destProtocol, int srcProtocol) { - const auto it = gInetProtocolToNativeProtocol.find(static_cast(srcProtocol)); - if (it == gInetProtocolToNativeProtocol.end()) { - return false; - } - destProtocol = it->second; - return true; +static int sceNetInetCloseWithRST(int socket) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); + // Based on http://deepix.github.io/2016/10/21/tcprst.html + struct linger sl {}; + sl.l_onoff = 1; // non-zero value enables linger option in kernel + sl.l_linger = 0; // timeout interval in seconds + setsockopt(socket, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); + return hleLogSuccessI(Log::sceNet, closesocket(socket)); } -bool SceNetInet::TranslateInetOptnameToNativeOptname(int &destOptname, const int inetOptname) { - const auto it = gInetSocketOptnameToNativeOptname.find(static_cast(inetOptname)); - if (it == gInetSocketOptnameToNativeOptname.end()) { - return false; - } - destOptname = it->second; - return true; +static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 fromPtr, u32 fromlenPtr) { + DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %08x) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, fromPtr, fromlenPtr, currentMIPS->pc); + SceNetInetSockaddr* src = (SceNetInetSockaddr*)Memory::GetCharPointer(fromPtr); + socklen_t* srclen = (socklen_t*)Memory::GetCharPointer(fromlenPtr); + SockAddrIN4 saddr{}; + if (srclen) + *srclen = std::min((*srclen) > 0 ? *srclen : 0, static_cast(sizeof(saddr))); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + int retval = recvfrom(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, srclen); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return hleDelayResult(retval, "workaround until blocking-socket", 500); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) + } + + if (src) { + src->sa_family = saddr.addr.sa_family; + memcpy(src->sa_data, saddr.addr.sa_data, sizeof(src->sa_data)); + src->sa_len = srclen ? *srclen : 0; + } + DEBUG_LOG(Log::sceNet, "RecvFrom: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + // Discard if it came from APIPA address (ie. self-received broadcasts from 169.254.x.x when broadcasting to INADDR_BROADCAST on Windows) on Untold Legends The Warrior's Code / Twisted Metal Head On + /*if (isAPIPA(saddr.in.sin_addr.s_addr)) { + inetLastErrno = EAGAIN; + retval = -1; + DEBUG_LOG(Log::sceNet, "RecvFrom: Ignoring Address = %s", ip2str(saddr.in.sin_addr).c_str()); + hleLogDebug(Log::sceNet, retval, "faked errno = %d", inetLastErrno); + return hleDelayResult(retval, "workaround until blocking-socket", 500); + }*/ + + std::string datahex; + DataToHexString(0, 0, Memory::GetPointer(bufferPtr), retval, &datahex); + VERBOSE_LOG(Log::sceNet, "Data Dump (%d bytes):\n%s", retval, datahex.c_str()); + + return hleLogSuccessInfoI(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) } -int SceNetInet::TranslateInetFlagsToNativeFlags(const int messageFlags, const bool nonBlocking) { - int nativeFlags = 0; // The actual platform flags - int foundFlags = 0; // The inet equivalent of the native flags, used to verify that no remaining flags need to be set - for (const auto [inetFlag, nativeFlag] : gInetMessageFlagToNativeMessageFlag) { - if ((messageFlags & inetFlag) != 0) { - nativeFlags |= nativeFlag; - foundFlags |= inetFlag; +static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 toPtr, int tolen) { + DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %d) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, toPtr, tolen, currentMIPS->pc); + SceNetInetSockaddr* dst = (SceNetInetSockaddr*)Memory::GetCharPointer(toPtr); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + SockAddrIN4 saddr{}; + int dstlen = std::min(tolen > 0 ? tolen : 0, static_cast(sizeof(saddr))); + if (dst) { + saddr.addr.sa_family = dst->sa_family; + memcpy(saddr.addr.sa_data, dst->sa_data, sizeof(dst->sa_data)); + } + DEBUG_LOG(Log::sceNet, "SendTo: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + std::string datahex; + DataToHexString(0, 0, Memory::GetPointer(bufferPtr), len, &datahex); + VERBOSE_LOG(Log::sceNet, "Data Dump (%d bytes):\n%s", len, datahex.c_str()); + + int retval; + bool isBcast = isBroadcastIP(saddr.in.sin_addr.s_addr); + // Broadcast/Multicast, use real broadcast/multicast if there is no one in peerlist + if (isBcast && getActivePeerCount() > 0) { + // Acquire Peer Lock + peerlock.lock(); + SceNetAdhocctlPeerInfo* peer = friends; + for (; peer != NULL; peer = peer->next) { + // Does Skipping sending to timed out friends could cause desync when players moving group at the time MP game started? + if (peer->last_recv == 0) + continue; + + saddr.in.sin_addr.s_addr = peer->ip_addr; + retval = sendto(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); + if (retval < 0) { + DEBUG_LOG(Log::sceNet, "SendTo(BC): Socket error %d", errno); + } else { + DEBUG_LOG(Log::sceNet, "SendTo(BC): Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + } } - } + // Free Peer Lock + peerlock.unlock(); + retval = len; + } + // Unicast or real broadcast/multicast + else { + // FIXME: On non-Windows(including PSP too?) broadcast to INADDR_BROADCAST(255.255.255.255) might not be received by the sender itself when binded to specific IP (ie. 192.168.0.2) or INADDR_BROADCAST. + // Meanwhile, it might be received by itself when binded to subnet (ie. 192.168.0.255) or INADDR_ANY(0.0.0.0). + /*if (isBcast) { + // TODO: Replace Broadcast with Multicast to be more consistent across platform + // Replace Limited Broadcast(255.255.255.255) with Direct Broadcast(ie. 192.168.0.255) for accurate targetting when there are multiple interfaces, to avoid receiving it's own broadcasted data through IP 169.254.x.x on Windows (which is not recognized as it's own IP by the game) + // Get Local IP Address + sockaddr_in sockAddr{}; + getLocalIp(&sockAddr); + // Change the last number to 255 to indicate a common broadcast address (the accurate way should be: ip | (~subnetmask)) + ((u8*)&sockAddr.sin_addr.s_addr)[3] = 255; + saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; + DEBUG_LOG(Log::sceNet, "SendTo(BC): Address Replacement = %s", ip2str(saddr.in.sin_addr).c_str()); + }*/ + retval = sendto(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); + } + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return retval; + } + + return hleLogSuccessInfoI(Log::sceNet, retval); +} -#if !PPSSPP_PLATFORM(WINDOWS) - if (nonBlocking) { - nativeFlags |= MSG_DONTWAIT; +// Similar to POSIX's sendmsg or Winsock2's WSASendMsg? Are their packets compatible one another? +// Games using this: The Warrior's Code +static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { + DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + DEBUG_LOG_REPORT_ONCE(sceNetInetSendmsg, Log::sceNet, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + // Note: sendmsg is concatenating iovec buffers before sending it, and send/sendto is just a wrapper for sendmsg according to https://stackoverflow.com/questions/4258834/how-sendmsg-works + int retval = -1; + if (!Memory::IsValidAddress(msghdrPtr)) { + inetLastErrno = EFAULT; + return hleLogError(Log::sceNet, retval); + } + InetMsghdr* pspMsghdr = (InetMsghdr*)Memory::GetPointer(msghdrPtr); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + SockAddrIN4 saddr{}; +#if defined(_WIN32) + WSAMSG hdr; + WSACMSGHDR* chdr = NULL; + size_t iovecsize = sizeof(WSABUF); + WSABUF* iov = (WSABUF*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#else + msghdr hdr; + cmsghdr* chdr = nullptr; + size_t iovecsize = sizeof(iovec); + iovec* iov = (iovec*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#endif + if (iov == NULL) { + inetLastErrno = ENOBUFS; + return hleLogError(Log::sceNet, retval); + } + memset(iov, 0, pspMsghdr->msg_iovlen * iovecsize); + memset(&hdr, 0, sizeof(hdr)); + if (pspMsghdr->msg_name != 0) { + SceNetInetSockaddr* pspSaddr = (SceNetInetSockaddr*)Memory::GetPointer(pspMsghdr->msg_name); + saddr.addr.sa_family = pspSaddr->sa_family; + size_t datalen = std::min(pspMsghdr->msg_namelen - (sizeof(pspSaddr->sa_len) + sizeof(pspSaddr->sa_family)), sizeof(saddr.addr.sa_data)); + memcpy(saddr.addr.sa_data, pspSaddr->sa_data, datalen); + DEBUG_LOG(Log::sceNet, "SendMsg: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); +#if defined(_WIN32) + hdr.name = &saddr.addr; + hdr.namelen = static_cast(datalen + sizeof(saddr.addr.sa_family)); +#else + hdr.msg_name = &saddr.addr; + hdr.msg_namelen = static_cast(datalen + sizeof(saddr.addr.sa_family)); +#endif } +#if defined(_WIN32) + hdr.lpBuffers = iov; + hdr.dwBufferCount = pspMsghdr->msg_iovlen; +#else + hdr.msg_iov = iov; + hdr.msg_iovlen = pspMsghdr->msg_iovlen; +#endif + if (pspMsghdr->msg_iov != 0) { + SceNetIovec* pspIov = (SceNetIovec*)Memory::GetPointer(pspMsghdr->msg_iov); + for (int i = 0; i < pspMsghdr->msg_iovlen; i++) { + if (pspIov[i].iov_base != 0) { +#if defined(_WIN32) + iov[i].buf = (char*)Memory::GetPointer(pspIov[i].iov_base); + iov[i].len = pspIov[i].iov_len; +#else + iov[i].iov_base = (char*)Memory::GetPointer(pspIov[i].iov_base); + iov[i].iov_len = pspIov[i].iov_len; #endif - - // Check for any inet flags which were not successfully mapped into a native flag - if (const int missingFlags = messageFlags & ~foundFlags; missingFlags != 0) { - for (int i = 0; i < sizeof(int) * 8; i++) { - if (const int val = 1 << i; (missingFlags & val) != 0) { - DEBUG_LOG(Log::sceNet, "Encountered unsupported platform flag at bit %i (actual value %04x), undefined behavior may ensue.", i, val); } } } - return nativeFlags; -} - -int SceNetInet::TranslateNativeErrorToInetError(const int nativeError) { - if (const auto it = gNativeErrorCodeToInetErrorCode.find(nativeError); - it != gNativeErrorCodeToInetErrorCode.end()) { - return it->second; - } - return nativeError; -} - -int SceNetInet::GetLastError() { - auto lock = std::shared_lock(mLock); - return mLastError; -} - -void SceNetInet::SetLastError(const int error) { - auto lock = std::unique_lock(mLock); - mLastError = error; -} - -int SceNetInet::SetLastErrorToMatchPlatform() { - int error; -#if PPSSPP_PLATFORM(WINDOWS) - error = WSAGetLastError(); + // Control's Level (ie. host's SOL_SOCKET to/from psp's PSP_NET_INET_SOL_SOCKET) and Type (ie. SCM_RIGHTS) might need to be converted to be cross-platform + if (pspMsghdr->msg_control != 0) { +#if defined(_WIN32) + chdr = (WSACMSGHDR*)malloc(pspMsghdr->msg_controllen); #else - error = errno; + chdr = (cmsghdr*)malloc(pspMsghdr->msg_controllen); +#endif + if (chdr == NULL) { + inetLastErrno = ENOBUFS; + free(iov); + return hleLogError(Log::sceNet, retval); + } + InetCmsghdr* pspCmsghdr = (InetCmsghdr*)Memory::GetPointer(pspMsghdr->msg_control); + // TODO: Convert InetCmsghdr into platform-specific struct as they're affected by 32/64bit + memcpy(chdr, pspCmsghdr, pspMsghdr->msg_controllen); +#if defined(_WIN32) + hdr.Control.buf = (char*)chdr; // (char*)pspCmsghdr; + hdr.Control.len = pspMsghdr->msg_controllen; + // Note: Many existing implementations of CMSG_FIRSTHDR never look at msg_controllen and just return the value of cmsg_control. + if (pspMsghdr->msg_controllen >= sizeof(InetCmsghdr)) { + // TODO: Creates our own CMSG_* macros (32-bit version of it, similar to the one on PSP) to avoid alignment/size issue that can lead to memory corruption/out of bound issue. + for (WSACMSGHDR* cmsgptr = CMSG_FIRSTHDR(&hdr); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&hdr, cmsgptr)) { + cmsgptr->cmsg_type = convertCMsgTypePSP2Host(cmsgptr->cmsg_type, cmsgptr->cmsg_level); + cmsgptr->cmsg_level = convertSockoptLevelPSP2Host(cmsgptr->cmsg_level); + } + } +#else + hdr.msg_control = (char*)chdr; // (char*)pspCmsghdr; + hdr.msg_controllen = pspMsghdr->msg_controllen; + // Note: Many existing implementations of CMSG_FIRSTHDR never look at msg_controllen and just return the value of cmsg_control. + if (pspMsghdr->msg_controllen >= sizeof(InetCmsghdr)) { + for (cmsghdr* cmsgptr = CMSG_FIRSTHDR(&hdr); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&hdr, cmsgptr)) { + cmsgptr->cmsg_type = convertCMsgTypePSP2Host(cmsgptr->cmsg_type, cmsgptr->cmsg_level); + cmsgptr->cmsg_level = convertSockoptLevelPSP2Host(cmsgptr->cmsg_level); + } + } #endif - SetLastError(error); - return error; -} - -std::shared_ptr SceNetInet::CreateAndAssociateInetSocket(int nativeSocketId, int protocol, bool nonBlocking) { - auto lock = std::unique_lock(mLock); - - int inetSocketId = ++mCurrentInetSocketId; - if (const auto it = mInetSocketIdToNativeSocket.find(inetSocketId); it != mInetSocketIdToNativeSocket.end()) { - WARN_LOG(Log::sceNet, "%s: Attempted to re-associate socket from already-associated inetSocketId: %i", __func__, inetSocketId); - return nullptr; - } - auto inetSocket = std::make_shared(inetSocketId, nativeSocketId, protocol, nonBlocking); - inetSocket->SetNonBlocking(nonBlocking); - mInetSocketIdToNativeSocket.emplace(inetSocketId, inetSocket); - return inetSocket; -} - -std::shared_ptr SceNetInet::GetInetSocket(int inetSocketId) { - auto lock = std::shared_lock(mLock); - - const auto it = mInetSocketIdToNativeSocket.find(inetSocketId); - if (it == mInetSocketIdToNativeSocket.end()) { - WARN_LOG(Log::sceNet, "%s: Attempted to get unassociated socket from inetSocketId: %i", __func__, inetSocketId); - return nullptr; - } - return it->second; -} - -bool SceNetInet::GetNativeSocketIdForInetSocketId(int& nativeSocketId, int inetSocketId) { - const auto inetSocket = GetInetSocket(inetSocketId); - if (!inetSocket) { - return false; - } - nativeSocketId = inetSocket->GetNativeSocketId(); - return true; -} - -bool SceNetInet::EraseNativeSocket(int inetSocketId) { - auto lock = std::unique_lock(mLock); - - const auto it = mInetSocketIdToNativeSocket.find(inetSocketId); - if (it == mInetSocketIdToNativeSocket.end()) { - WARN_LOG(Log::sceNet, "%s: Attempted to delete unassociated socket from inetSocketId: %i", __func__, inetSocketId); - return false; - } - mInetSocketIdToNativeSocket.erase(it); - return true; -} - -bool SceNetInet::TranslateInetFdSetToNativeFdSet(int &maxFd, fd_set& destFdSet, u32 fdsPtr) const { - if (fdsPtr == 0) { - // Allow nullptr to be used without failing - return true; - } - - FD_ZERO(&destFdSet); - const auto sceFdSet = Memory::GetTypedPointerRange(fdsPtr, sizeof(PspInetFdSetOperations::FdSet)); - if (sceFdSet == nullptr) { - ERROR_LOG(Log::sceNet, "%s: Invalid fdsPtr %08x", __func__, fdsPtr); - return false; } - - int setSize = 0; - for (auto& it : mInetSocketIdToNativeSocket) { - const auto inetSocket = it.first; - const auto nativeSocketId = it.second->GetNativeSocketId(); - maxFd = std::max(nativeSocketId + 1, maxFd); - if (PspInetFdSetOperations::IsSet(*sceFdSet, inetSocket)) { - if (++setSize > FD_SETSIZE) { - ERROR_LOG(Log::sceNet, "%s: Encountered input FD_SET which is greater than max supported size %i", __func__, setSize); - return false; + // Flags (ie. PSP_NET_INET_MSG_OOB) might need to be converted to be cross-platform +#if defined(_WIN32) + hdr.dwFlags = convertMSGFlagsPSP2Host(pspMsghdr->msg_flags & ~PSP_NET_INET_MSG_DONTWAIT) | MSG_NOSIGNAL; +#else + hdr.msg_flags = convertMSGFlagsPSP2Host(pspMsghdr->msg_flags & ~PSP_NET_INET_MSG_DONTWAIT) | MSG_NOSIGNAL; +#endif + unsigned long sent = 0; + bool isBcast = isBroadcastIP(saddr.in.sin_addr.s_addr); + // Broadcast/Multicast, use real broadcast/multicast if there is no one in peerlist + if (isBcast && getActivePeerCount() > 0) { + // Acquire Peer Lock + peerlock.lock(); + SceNetAdhocctlPeerInfo* peer = friends; + for (; peer != NULL; peer = peer->next) { + // Does Skipping sending to timed out friends could cause desync when players moving group at the time MP game started? + if (peer->last_recv == 0) + continue; + + saddr.in.sin_addr.s_addr = peer->ip_addr; +#if defined(_WIN32) + int result = WSASendMsg(socket, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); + if (static_cast(sent) > retval) + retval = sent; +#else + size_t result = sendmsg(socket, &hdr, flgs | MSG_NOSIGNAL); + if (static_cast(result) > retval) + retval = result; +#endif + if (retval != SOCKET_ERROR) { + DEBUG_LOG(Log::sceNet, "SendMsg(BC): Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + } else { + DEBUG_LOG(Log::sceNet, "SendMsg(BC): Socket error %d", errno); } - DEBUG_LOG(Log::sceNet, "%s: Translating input %i into %i", __func__, inetSocket, nativeSocketId); - FD_SET(nativeSocketId, &destFdSet); } + // Free Peer Lock + peerlock.unlock(); + // TODO: Calculate number of bytes supposed to be sent + retval = std::max(retval, 0); // Broadcast always success? + } + // Unicast or real broadcast/multicast + else { + // FIXME: On non-Windows(including PSP too?) broadcast to INADDR_BROADCAST(255.255.255.255) might not be received by the sender itself when binded to specific IP (ie. 192.168.0.2) or INADDR_BROADCAST. + // Meanwhile, it might be received by itself when binded to subnet (ie. 192.168.0.255) or INADDR_ANY(0.0.0.0). + /*if (isBcast) { + // TODO: Replace Broadcast with Multicast to be more consistent across platform + // Replace Limited Broadcast(255.255.255.255) with Direct Broadcast(ie. 192.168.0.255) for accurate targetting when there are multiple interfaces, to avoid receiving it's own broadcasted data through IP 169.254.x.x on Windows (which is not recognized as it's own IP by the game) + // Get Local IP Address + sockaddr_in sockAddr{}; + getLocalIp(&sockAddr); + // Change the last number to 255 to indicate a common broadcast address (the accurate way should be: ip | (~subnetmask)) + ((u8*)&sockAddr.sin_addr.s_addr)[3] = 255; + saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; + DEBUG_LOG(Log::sceNet, "SendMsg(BC): Address Replacement = %s", ip2str(saddr.in.sin_addr).c_str()); + }*/ +#if defined(_WIN32) + int result = WSASendMsg(socket, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); + if (result != SOCKET_ERROR) + retval = sent; +#else + retval = sendmsg(socket, &hdr, flgs | MSG_NOSIGNAL); +#endif } - - DEBUG_LOG(Log::sceNet, "%s: Translated %i sockets", __func__, setSize); - return true; + free(chdr); + free(iov); + /* // Example with 1 Msg buffer and without CMsg + msghdr msg; + iovec iov[1]; + int buflen = pspMsghdr->msg_iovlen; + char* buf = (char*)malloc(buflen); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + iov[0].iov_base = buf; + iov[0].iov_len = buflen; + + retval = sendmsg(socket, &msg, flags); + free(buf); + */ + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); + else + hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return retval; + } + return hleLogSuccessInfoI(Log::sceNet, retval); // returns number of bytes sent? } -void SceNetInet::CloseAllRemainingSockets() const { - for (const auto &[first, second] : mInetSocketIdToNativeSocket) { - if (!second) { - continue; - } - close(second->GetNativeSocketId()); +// Similar to POSIX's recvmsg or Mswsock's WSARecvMsg? Are their packets compatible one another? +// Games using this: World of Poker +static int sceNetInetRecvmsg(int socket, u32 msghdrPtr, int flags) { + ERROR_LOG(Log::sceNet, "UNIMPL %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + DEBUG_LOG_REPORT_ONCE(sceNetInetRecvmsg, Log::sceNet, "UNIMPL %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + // Reference: http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch14lev1sec5.html + int retval = -1; + if (!Memory::IsValidAddress(msghdrPtr)) { + inetLastErrno = EFAULT; + return hleLogError(Log::sceNet, retval); + } + InetMsghdr* pspMsghdr = (InetMsghdr*)Memory::GetPointer(msghdrPtr); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + SockAddrIN4 saddr{}; +#if defined(_WIN32) + WSAMSG hdr; + WSACMSGHDR* chdr = NULL; + size_t iovecsize = sizeof(WSABUF); + WSABUF* iov = (WSABUF*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#else + msghdr hdr; + cmsghdr* chdr = nullptr; + size_t iovecsize = sizeof(iovec); + iovec* iov = (iovec*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#endif + if (iov == NULL) { + inetLastErrno = ENOBUFS; + return hleLogError(Log::sceNet, retval); } + memset(iov, 0, pspMsghdr->msg_iovlen * iovecsize); + memset(&hdr, 0, sizeof(hdr)); + // TODO: Do similar to the already working sceNetInetSendmsg but in reverse + //if (pspMsghdr->msg_name != 0) { ... } + + return hleLogError(Log::sceNet, retval); // returns number of bytes received? } +// TODO: fix retmasks +const HLEFunction sceNetInet[] = { + {0X17943399, &WrapI_V, "sceNetInetInit", 'i', "" }, + {0X4CFE4E56, &WrapI_II, "sceNetInetShutdown", 'i', "ii" }, + {0XA9ED66B9, &WrapI_V, "sceNetInetTerm", 'i', "" }, + {0X8B7B220F, &WrapI_III, "sceNetInetSocket", 'i', "iii" }, + {0X2FE71FE7, &WrapI_IIIUI, "sceNetInetSetsockopt", 'i', "iiixi" }, + {0X4A114C7C, &WrapI_IIIUU, "sceNetInetGetsockopt", 'i', "iiixx" }, + {0X410B34AA, &WrapI_IUI, "sceNetInetConnect", 'i', "ixi" }, + {0X805502DD, &WrapI_I, "sceNetInetCloseWithRST", 'i', "i" }, + {0XD10A1A7A, &WrapI_II, "sceNetInetListen", 'i', "ii" }, + {0XDB094E1B, &WrapI_IUU, "sceNetInetAccept", 'i', "ixx" }, + {0XFAABB1DD, &WrapI_UUI, "sceNetInetPoll", 'i', "xxi" }, + {0X5BE8D595, &WrapI_IUUUU, "sceNetInetSelect", 'i', "ixxxx" }, + {0X8D7284EA, &WrapI_I, "sceNetInetClose", 'i', "i" }, + {0XCDA85C99, &WrapI_IUUU, "sceNetInetRecv", 'i', "ixxx" }, + {0XC91142E4, &WrapI_IUIIUU, "sceNetInetRecvfrom", 'i', "ixiixx" }, + {0XEECE61D2, &WrapI_IUI, "sceNetInetRecvmsg", 'i', "ixi" }, + {0X7AA671BC, &WrapI_IUUU, "sceNetInetSend", 'i', "ixxx" }, + {0X05038FC7, &WrapI_IUIIUI, "sceNetInetSendto", 'i', "ixiixi" }, + {0X774E36F4, &WrapI_IUI, "sceNetInetSendmsg", 'i', "ixi" }, + {0XFBABE411, &WrapI_V, "sceNetInetGetErrno", 'i', "" }, + {0X1A33F9AE, &WrapI_IUI, "sceNetInetBind", 'i', "ixi" }, + {0XB75D5B0A, &WrapU_C, "sceNetInetInetAddr", 'x', "s" }, + {0X1BDF5D13, &WrapI_CU, "sceNetInetInetAton", 'i', "sx" }, + {0XD0792666, &WrapU_IUUU, "sceNetInetInetNtop", 'x', "ixxx" }, + {0XE30B8C19, &WrapI_ICU, "sceNetInetInetPton", 'i', "isx" }, + {0X8CA3A97E, &WrapI_V, "sceNetInetGetPspError", 'i', "" }, + {0XE247B6D6, &WrapI_IUU, "sceNetInetGetpeername", 'i', "ixx" }, + {0X162E6FD5, &WrapI_IUU, "sceNetInetGetsockname", 'i', "ixx" }, + {0X80A21ABD, &WrapI_I, "sceNetInetSocketAbort", 'i', "i" }, + {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, + {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, +}; + void Register_sceNetInet() { RegisterModule("sceNetInet", std::size(sceNetInet), sceNetInet); } diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h index 91b9f9323643..aa83a60c39d9 100644 --- a/Core/HLE/sceNetInet.h +++ b/Core/HLE/sceNetInet.h @@ -1,7 +1,6 @@ #pragma once #include "Core/HLE/HLE.h" -#include "Core/Net/InetSocket.h" #if PPSSPP_PLATFORM(WINDOWS) #include @@ -11,191 +10,558 @@ #include #include +// Using constants instead of numbers for readability reason, since PSP_THREAD_ATTR_KERNEL/USER is located in sceKernelThread.cpp instead of sceKernelThread.h +#ifndef PSP_THREAD_ATTR_KERNEL +#define PSP_THREAD_ATTR_KERNEL 0x00001000 +#endif +#ifndef PSP_THREAD_ATTR_USER +#define PSP_THREAD_ATTR_USER 0x80000000 +#endif + + +// Socket Types (based on https://github.com/justincormack/netbsd-src/blob/master/src/sys/sys/socket.h ) +#define PSP_NET_INET_SOCK_STREAM 1 // stream socket +#define PSP_NET_INET_SOCK_DGRAM 2 // datagram socket +#define PSP_NET_INET_SOCK_RAW 3 // raw-protocol interface // SOCK_RAW is similar to but not compatible with the obsolete AF_INET / SOCK_PACKET // SOCK_RAW have some restrictions on newer Windows https://docs.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2 +#define PSP_NET_INET_SOCK_RDM 4 // reliably-delivered message +#define PSP_NET_INET_SOCK_SEQPACKET 5 // sequenced packet stream +#define PSP_NET_INET_SOCK_CONN_DGRAM 6 // connection-orientated datagram +#define PSP_NET_INET_SOCK_DCCP PSP_NET_INET_SOCK_CONN_DGRAM // Datagram Congestion Control Protocol +#define PSP_NET_INET_SOCK_PACKET 10 // Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level // SOCK_PACKET is an obsolete socket type to receive raw packets directly from the device driver +#define PSP_NET_INET_SOCK_TYPE_MASK 0x000F // mask that covers the above +// Flags to be ORed into the type parameter of socket and socketpair and used for the flags parameter of paccept. +#define PSP_NET_INET_SOCK_CLOEXEC 0x10000000 // set close on exec on socket +#define PSP_NET_INET_SOCK_NONBLOCK 0x20000000 // set non blocking i/o socket +#define PSP_NET_INET_SOCK_NOSIGPIPE 0x40000000 // don't send sigpipe +#define PSP_NET_INET_SOCK_FLAGS_MASK 0xf0000000 // flags mask + +// Option flags per-socket (based on SOL_SOCKET value on PSP (0xffff) seems to be different with linux/android's auto-generated socket.h (1), but similar to posix/gnu/BSD like this https://github.com/eblot/newlib/blob/master/newlib/libc/sys/linux/sys/socket.h ?) +#define PSP_NET_INET_SO_DEBUG 0x0001 // turn on debugging info recording +#define PSP_NET_INET_SO_ACCEPTCONN 0x0002 // socket has had listen() +#define PSP_NET_INET_SO_REUSEADDR 0x0004 // allow local address reuse +#define PSP_NET_INET_SO_KEEPALIVE 0x0008 // keep connections alive +#define PSP_NET_INET_SO_DONTROUTE 0x0010 // just use interface addresses +#define PSP_NET_INET_SO_BROADCAST 0x0020 // permit sending of broadcast msgs +#define PSP_NET_INET_SO_USELOOPBACK 0x0040 // bypass hardware when possible +#define PSP_NET_INET_SO_LINGER 0x0080 // linger on close if data present +#define PSP_NET_INET_SO_OOBINLINE 0x0100 // leave received OOB data in line +#define PSP_NET_INET_SO_REUSEPORT 0x0200 // allow local address & port reuse +#define PSP_NET_INET_SO_TIMESTAMP 0x0400 // timestamp received dgram traffic +#define PSP_NET_INET_SO_ONESBCAST 0x0800 // permit sending to 255.255.255.255 + +// Additional options (not kept in so_options) +#define PSP_NET_INET_SO_SNDBUF 0x1001 // send buffer size (default value = 16384 bytes) +#define PSP_NET_INET_SO_RCVBUF 0x1002 // receive buffer size (default value = 16384 bytes for TCP/IP, 41600 bytes for UDP/IP) +#define PSP_NET_INET_SO_SNDLOWAT 0x1003 // send low-water mark +#define PSP_NET_INET_SO_RCVLOWAT 0x1004 // receive low-water mark +#define PSP_NET_INET_SO_SNDTIMEO 0x1005 // send timeout +#define PSP_NET_INET_SO_RCVTIMEO 0x1006 // receive timeout +#define PSP_NET_INET_SO_ERROR 0x1007 // get error status and clear +#define PSP_NET_INET_SO_TYPE 0x1008 // get socket type +#define PSP_NET_INET_SO_NBIO 0x1009 // SO_NONBLOCK ? // set to non-blocking I/O mode (on true, returning 0x80 when retrieved using getsockopt?) +#define PSP_NET_INET_SO_BIO 0x100a // set to blocking I/O mode (not using the optval just like SO_NBIO?) +//#define PSP_NET_INET_SO_NONBLOCK 0x100b // set to blocking or non-blocking I/O mode (using the optval) + +// User-settable options (used with setsockopt) +#define PSP_NET_INET_TCP_NODELAY 0x01 // don't delay send to coalesce packets +#define PSP_NET_INET_TCP_MAXSEG 0x02 // set maximum segment size + +// Options for use with [get/set]sockopt at the IP level +#define PSP_NET_INET_IP_OPTIONS 1 // (buf/ip_opts) set/get IP options +#define PSP_NET_INET_IP_HDRINCL 2 // (int) header is included with data +#define PSP_NET_INET_IP_TOS 3 // (int) IP type of service and preced. +#define PSP_NET_INET_IP_TTL 4 // (int) IP time to live +#define PSP_NET_INET_IP_RECVOPTS 5 // (bool) receive all IP opts w/dgram +#define PSP_NET_INET_IP_RECVRETOPTS 6 // (bool) receive IP opts for response +#define PSP_NET_INET_IP_RECVDSTADDR 7 // (bool) receive IP dst addr w/dgram +#define PSP_NET_INET_IP_RETOPTS 8 // (ip_opts) set/get IP options +#define PSP_NET_INET_IP_MULTICAST_IF 9 // (in_addr) set/get IP multicast i/f +#define PSP_NET_INET_IP_MULTICAST_TTL 10 // (u_char) set/get IP multicast ttl +#define PSP_NET_INET_IP_MULTICAST_LOOP 11 // (u_char) set/get IP multicast loopback +#define PSP_NET_INET_IP_ADD_MEMBERSHIP 12 // (ip_mreq) add an IP group membership +#define PSP_NET_INET_IP_DROP_MEMBERSHIP 13 // (ip_mreq) drop an IP group membership +#define PSP_NET_INET_IP_PORTRANGE 19 // (int) range to use for ephemeral port +#define PSP_NET_INET_IP_RECVIF 20 // (bool) receive reception if w/dgram +#define PSP_NET_INET_IP_ERRORMTU 21 // (int) get MTU of last xmit = EMSGSIZE + +#define PSP_NET_INET_IP_IPSEC_POLICY 22 // (struct) get/set security policy + +// Level number for [get/set]sockopt to apply to socket itself +#define PSP_NET_INET_SOL_SOCKET 0xffff // options for socket level + +// "Socket"-level control message types: +#define PSP_NET_INET_SCM_RIGHTS 0x01 // access rights (array of int) +#define PSP_NET_INET_SCM_CREDS 0x04 // credentials (struct sockcred) +#define PSP_NET_INET_SCM_TIMESTAMP 0x08 // timestamp (struct timeval) + +// Protocols +#define PSP_NET_INET_IPPROTO_IP 0 // dummy for IP +#define PSP_NET_INET_IPPROTO_HOPOPTS 0 // IP6 hop-by-hop options +#define PSP_NET_INET_IPPROTO_UNSPEC 0 // 0 will defaulted to the only existing protocol for that particular domain/family and type +#define PSP_NET_INET_IPPROTO_ICMP 1 // control message protocol +#define PSP_NET_INET_IPPROTO_IGMP 2 // group mgmt protocol +#define PSP_NET_INET_IPPROTO_GGP 3 // gateway^2 (deprecated) +#define PSP_NET_INET_IPPROTO_IPV4 4 // IP header +#define PSP_NET_INET_IPPROTO_IPIP 4 // IP inside IP +#define PSP_NET_INET_IPPROTO_TCP 6 // tcp +#define PSP_NET_INET_IPPROTO_EGP 8 // exterior gateway protocol +#define PSP_NET_INET_IPPROTO_PUP 12 // pup +#define PSP_NET_INET_IPPROTO_UDP 17 // user datagram protocol +#define PSP_NET_INET_IPPROTO_IDP 22 // xns idp +#define PSP_NET_INET_IPPROTO_TP 29 // tp-4 w/ class negotiation +#define PSP_NET_INET_IPPROTO_IPV6 41 // IP6 header +#define PSP_NET_INET_IPPROTO_ROUTING 43 // IP6 routing header +#define PSP_NET_INET_IPPROTO_FRAGMENT 44 // IP6 fragmentation header +#define PSP_NET_INET_IPPROTO_RSVP 46 // resource reservation +#define PSP_NET_INET_IPPROTO_GRE 47 // GRE encaps RFC 1701 +#define PSP_NET_INET_IPPROTO_ESP 50 // encap. security payload +#define PSP_NET_INET_IPPROTO_AH 51 // authentication header +#define PSP_NET_INET_IPPROTO_MOBILE 55 // IP Mobility RFC 2004 +#define PSP_NET_INET_IPPROTO_IPV6_ICMP 58 // IPv6 ICMP +#define PSP_NET_INET_IPPROTO_ICMPV6 58 // ICMP6 +#define PSP_NET_INET_IPPROTO_NONE 59 // IP6 no next header +#define PSP_NET_INET_IPPROTO_DSTOPTS 60 // IP6 destination option +#define PSP_NET_INET_IPPROTO_EON 80 // ISO cnlp +#define PSP_NET_INET_IPPROTO_ENCAP 98 // encapsulation header +#define PSP_NET_INET_IPPROTO_PIM 103 // Protocol indep. multicast +#define PSP_NET_INET_IPPROTO_IPCOMP 108 // IP Payload Comp. Protocol + +#define PSP_NET_INET_IPPROTO_RAW 255 // raw IP packet +#define PSP_NET_INET_IPPROTO_MAX 256 + +#define PSP_NET_INET_IPPROTO_DONE 257 // all job for this packet are done + +// Address families +#define PSP_NET_INET_AF_UNSPEC 0 // unspecified +#define PSP_NET_INET_AF_LOCAL 1 // local to host (pipes, portals) +#define PSP_NET_INET_AF_UNIX PSP_NET_INET_AF_LOCAL // backward compatibility +#define PSP_NET_INET_AF_INET 2 // internetwork: UDP, TCP, etc. +#define PSP_NET_INET_AF_IMPLINK 3 // arpanet imp addresses +#define PSP_NET_INET_AF_PUP 4 // pup protocols: e.g. BSP +#define PSP_NET_INET_AF_CHAOS 5 // mit CHAOS protocols +#define PSP_NET_INET_AF_NS 6 // XEROX NS protocols +#define PSP_NET_INET_AF_ISO 7 // ISO protocols +#define PSP_NET_INET_AF_OSI PSP_NET_INET_AF_ISO +#define PSP_NET_INET_AF_ECMA 8 // european computer manufacturers +#define PSP_NET_INET_AF_DATAKIT 9 // datakit protocols +#define PSP_NET_INET_AF_CCITT 10 // CCITT protocols, X.25 etc +#define PSP_NET_INET_AF_SNA 11 // IBM SNA +#define PSP_NET_INET_AF_DECnet 12 // DECnet +#define PSP_NET_INET_AF_DLI 13 // DEC Direct data link interface +#define PSP_NET_INET_AF_LAT 14 // LAT +#define PSP_NET_INET_AF_HYLINK 15 // NSC Hyperchannel +#define PSP_NET_INET_AF_APPLETALK 16 // Apple Talk +#define PSP_NET_INET_AF_ROUTE 17 // Internal Routing Protocol +#define PSP_NET_INET_AF_LINK 18 // Link layer interface + +#define PSP_NET_INET_AF_COIP 20 // connection-oriented IP, aka ST II +#define PSP_NET_INET_AF_CNT 21 // Computer Network Technology + +#define PSP_NET_INET_AF_IPX 23 // Novell Internet Protocol +#define PSP_NET_INET_AF_INET6 24 // IP version 6 + +#define PSP_NET_INET_AF_ISDN 26 // Integrated Services Digital Network +#define PSP_NET_INET_AF_E164 PSP_NET_INET_AF_ISDN // CCITT E.164 recommendation +#define PSP_NET_INET_AF_NATM 27 // native ATM access +#define PSP_NET_INET_AF_ARP 28 // (rev.) addr. res. prot. (RFC 826) + +#define PSP_NET_INET_AF_MAX 31 + +// Infrastructure ERRNO Values (similar to this https://github.com/eblot/newlib/blob/master/newlib/libc/include/sys/errno.h ?) +#define ERROR_INET_EINTR 4 // Interrupted system call +#define ERROR_INET_EBADF 9 //0x09 // Or was it 0x80010009 (SCE_ERROR_ERRNO_EBADF/SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_DESCRIPTOR) ? +#define ERROR_INET_EAGAIN 11 //0x0B // Or was it 0x8001000B (SCE_ERROR_ERRNO_EAGAIN) ? +#define ERROR_INET_EWOULDBLOCK EAGAIN // Operation would block +#define ERROR_INET_EACCES 13 // Permission denied +#define ERROR_INET_EFAULT 14 // Bad address +#define ERROR_INET_EINVAL 22 // Invalid argument +#define ERROR_INET_ENOSPC 28 // No space left on device +#define ERROR_INET_EPIPE 32 // Broken pipe +#define ERROR_INET_ENOMSG 35 // No message of desired type +#define ERROR_INET_ENOLINK 67 // The link has been severed +#define ERROR_INET_EPROTO 71 // Protocol error +#define ERROR_INET_EBADMSG 77 // Trying to read unreadable message +#define ERROR_INET_EOPNOTSUPP 95 // Operation not supported on transport endpoint +#define ERROR_INET_EPFNOSUPPORT 96 // Protocol family not supported +#define ERROR_INET_ECONNRESET 104 // Connection reset by peer +#define ERROR_INET_ENOBUFS 105 // No buffer space available +#define ERROR_INET_EAFNOSUPPORT 106 // EISCONN ? // Address family not supported by protocol family +#define ERROR_INET_EPROTOTYPE 107 // Protocol wrong type for socket +#define ERROR_INET_ENOTSOCK 108 // Socket operation on non-socket +#define ERROR_INET_ENOPROTOOPT 109 // Protocol not available +#define ERROR_INET_ESHUTDOWN 110 // Can't send after socket shutdown +#define ERROR_INET_ECONNREFUSED 111 // Connection refused +#define ERROR_INET_EADDRINUSE 112 // Address already in use +#define ERROR_INET_ECONNABORTED 113 // Connection aborted +#define ERROR_INET_ENETUNREACH 114 // Network is unreachable +#define ERROR_INET_ENETDOWN 115 // Network interface is not configured +#define ERROR_INET_ETIMEDOUT 116 // Connection timed out +#define ERROR_INET_EHOSTDOWN 117 // Host is down +#define ERROR_INET_EHOSTUNREACH 118 // Host is unreachable +#define ERROR_INET_EINPROGRESS 119 // Connection already in progress +#define ERROR_INET_EALREADY 120 // Socket already connected +#define ERROR_INET_EDESTADDRREQ 121 // Destination address required +#define ERROR_INET_EMSGSIZE 122 // Message too long +#define ERROR_INET_EPROTONOSUPPORT 123 // Unknown protocol +#define ERROR_INET_ESOCKTNOSUPPORT 124 // Socket type not supported (linux?) +#define ERROR_INET_EADDRNOTAVAIL 125 // Address not available +#define ERROR_INET_ENETRESET 126 +#define ERROR_INET_EISCONN 127 // Socket is already connected +#define ERROR_INET_ENOTCONN 128 // Socket is not connected +#define ERROR_INET_ETOOMANYREFS 129 +#define ERROR_INET_ENOTSUP 134 // Not supported + +// Maximum queue length specifiable by listen(2) +#define PSP_NET_INET_SOMAXCONN 128 + +// On-Demand Flags +#define PSP_NET_INET_MSG_OOB 0x1 // process out-of-band data +#define PSP_NET_INET_MSG_PEEK 0x2 // peek at incoming message +#define PSP_NET_INET_MSG_DONTROUTE 0x4 // send without using routing tables +#define PSP_NET_INET_MSG_EOR 0x8 // data completes record +#define PSP_NET_INET_MSG_TRUNC 0x10 // data discarded before delivery +#define PSP_NET_INET_MSG_CTRUNC 0x20 // control data lost before delivery +#define PSP_NET_INET_MSG_WAITALL 0x40 // wait for full request or error +#define PSP_NET_INET_MSG_DONTWAIT 0x80 // this message should be nonblocking +#define PSP_NET_INET_MSG_BCAST 0x100 // this message was rcvd using link-level brdcst +#define PSP_NET_INET_MSG_MCAST 0x200 // this message was rcvd using link-level mcast + +// Poll Event Flags (used on events) +#define INET_POLLIN 0x001 // There is data to read. +#define INET_POLLPRI 0x002 // There is urgent data to read. +#define INET_POLLOUT 0x004 // Writing now will not block. + +#define INET_POLLRDNORM 0x040 // Equivalent to POLLIN ? just like _XOPEN_SOURCE? (mapped to read fds_set) +#define INET_POLLWRNORM 0x100 //0x0004 ? // Equivalent to POLLOUT ? just like _XOPEN_SOURCE? (mapped to write fds_set) + +#define INET_POLLRDBAND 0x080 // Priority data may be read. (mapped to exception fds_set) +#define INET_POLLWRBAND 0x200 // Priority data may be written. (mapped to write fds_set?) + +#define INET_POLLERR 0x008 // Error condition. (can appear on revents regardless of events?) +#define INET_POLLHUP 0x010 // Hung up. (can appear on revents regardless of events?) +#define INET_POLLNVAL 0x020 // Invalid polling request. (can appear on revents regardless of events?) + +// Types of socket shutdown(2) +#define PSP_NET_INET_SHUT_RD 0 // Disallow further receives. +#define PSP_NET_INET_SHUT_WR 1 // Disallow further sends. +#define PSP_NET_INET_SHUT_RDWR 2 // Disallow further sends/receives. + +#ifndef SHUT_RD +#define SHUT_RD SD_RECEIVE //0x00 +#endif +#ifndef SHUT_WR +#define SHUT_WR SD_SEND //0x01 +#endif +#ifndef SHUT_RDWR +#define SHUT_RDWR SD_BOTH //0x02 +#endif + +enum { + // from pspsdk's pspnet.h which is similar to https://docs.vitasdk.org/net_2net_8h_source.html + SCE_NET_ERROR_EPERM = 0x80410101, + SCE_NET_ERROR_ENOENT = 0x80410102, + SCE_NET_ERROR_ESRCH = 0x80410103, + SCE_NET_ERROR_EINTR = 0x80410104, + SCE_NET_ERROR_EIO = 0x80410105, + SCE_NET_ERROR_ENXIO = 0x80410106, + SCE_NET_ERROR_E2BIG = 0x80410107, + + SCE_NET_ERROR_ENOEXEC = 0x80410108, + SCE_NET_ERROR_EBADF = 0x80410109, + SCE_NET_ERROR_ECHILD = 0x8041010A, + SCE_NET_ERROR_EDEADLK = 0x8041010B, + SCE_NET_ERROR_ENOMEM = 0x8041010C, + SCE_NET_ERROR_EACCES = 0x8041010D, + SCE_NET_ERROR_EFAULT = 0x8041010E, + SCE_NET_ERROR_ENOTBLK = 0x8041010F, + SCE_NET_ERROR_EBUSY = 0x80410110, + SCE_NET_ERROR_EEXIST = 0x80410111, + SCE_NET_ERROR_EXDEV = 0x80410112, + SCE_NET_ERROR_ENODEV = 0x80410113, + SCE_NET_ERROR_ENOTDIR = 0x80410114, + SCE_NET_ERROR_EISDIR = 0x80410115, + SCE_NET_ERROR_EINVAL = 0x80410116, + SCE_NET_ERROR_ENFILE = 0x80410117, + SCE_NET_ERROR_EMFILE = 0x80410118, + SCE_NET_ERROR_ENOTTY = 0x80410119, + SCE_NET_ERROR_ETXTBSY = 0x8041011A, + SCE_NET_ERROR_EFBIG = 0x8041011B, + SCE_NET_ERROR_ENOSPC = 0x8041011C, + SCE_NET_ERROR_ESPIPE = 0x8041011D, + SCE_NET_ERROR_EROFS = 0x8041011E, + SCE_NET_ERROR_EMLINK = 0x8041011F, + SCE_NET_ERROR_EPIPE = 0x80410120, + SCE_NET_ERROR_EDOM = 0x80410121, + SCE_NET_ERROR_ERANGE = 0x80410122, + SCE_NET_ERROR_EAGAIN = 0x80410123, + SCE_NET_ERROR_EWOULDBLOCK = 0x80410123, + SCE_NET_ERROR_EINPROGRESS = 0x80410124, + SCE_NET_ERROR_EALREADY = 0x80410125, + SCE_NET_ERROR_ENOTSOCK = 0x80410126, + SCE_NET_ERROR_EDESTADDRREQ = 0x80410127, + SCE_NET_ERROR_EMSGSIZE = 0x80410128, + SCE_NET_ERROR_EPROTOTYPE = 0x80410129, + SCE_NET_ERROR_ENOPROTOOPT = 0x8041012A, + SCE_NET_ERROR_EPROTONOSUPPORT = 0x8041012B, + SCE_NET_ERROR_ESOCKTNOSUPPORT = 0x8041012C, + SCE_NET_ERROR_EOPNOTSUPP = 0x8041012D, + SCE_NET_ERROR_EPFNOSUPPORT = 0x8041012E, + SCE_NET_ERROR_EAFNOSUPPORT = 0x8041012F, + SCE_NET_ERROR_EADDRINUSE = 0x80410130, + SCE_NET_ERROR_EADDRNOTAVAIL = 0x80410131, + SCE_NET_ERROR_ENETDOWN = 0x80410132, + SCE_NET_ERROR_ENETUNREACH = 0x80410133, + SCE_NET_ERROR_ENETRESET = 0x80410134, + SCE_NET_ERROR_ECONNABORTED = 0x80410135, + SCE_NET_ERROR_ECONNRESET = 0x80410136, + SCE_NET_ERROR_ENOBUFS = 0x80410137, + SCE_NET_ERROR_EISCONN = 0x80410138, + SCE_NET_ERROR_ENOTCONN = 0x80410139, + SCE_NET_ERROR_ESHUTDOWN = 0x8041013A, + SCE_NET_ERROR_ETOOMANYREFS = 0x8041013B, + SCE_NET_ERROR_ETIMEDOUT = 0x8041013C, + SCE_NET_ERROR_ECONNREFUSED = 0x8041013D, + SCE_NET_ERROR_ELOOP = 0x8041013E, + SCE_NET_ERROR_ENAMETOOLONG = 0x8041013F, + SCE_NET_ERROR_EHOSTDOWN = 0x80410140, + SCE_NET_ERROR_EHOSTUNREACH = 0x80410141, + SCE_NET_ERROR_ENOTEMPTY = 0x80410142, + SCE_NET_ERROR_EPROCLIM = 0x80410143, + SCE_NET_ERROR_EUSERS = 0x80410144, + SCE_NET_ERROR_EDQUOT = 0x80410145, + SCE_NET_ERROR_ESTALE = 0x80410146, + SCE_NET_ERROR_EREMOTE = 0x80410147, + SCE_NET_ERROR_EBADRPC = 0x80410148, + SCE_NET_ERROR_ERPCMISMATCH = 0x80410149, + SCE_NET_ERROR_EPROGUNAVAIL = 0x8041014A, + SCE_NET_ERROR_EPROGMISMATCH = 0x8041014B, + SCE_NET_ERROR_EPROCUNAVAIL = 0x8041014C, + SCE_NET_ERROR_ENOLCK = 0x8041014D, + SCE_NET_ERROR_ENOSYS = 0x8041014E, + SCE_NET_ERROR_EFTYPE = 0x8041014F, + SCE_NET_ERROR_EAUTH = 0x80410150, + SCE_NET_ERROR_ENEEDAUTH = 0x80410151, + SCE_NET_ERROR_EIDRM = 0x80410152, + SCE_NET_ERROR_ENOMS = 0x80410153, + SCE_NET_ERROR_EOVERFLOW = 0x80410154, + SCE_NET_ERROR_EILSEQ = 0x80410155, + SCE_NET_ERROR_ENOTSUP = 0x80410156, + SCE_NET_ERROR_ECANCELED = 0x80410157, + SCE_NET_ERROR_EBADMSG = 0x80410158, + SCE_NET_ERROR_ENODATA = 0x80410159, + SCE_NET_ERROR_ENOSR = 0x8041015A, + SCE_NET_ERROR_ENOSTR = 0x8041015B, + SCE_NET_ERROR_ETIME = 0x8041015C, + + SCE_NET_ERROR_EADHOC = 0x804101A0, + SCE_NET_ERROR_EDISABLEDIF = 0x804101A1, + SCE_NET_ERROR_ERESUME = 0x804101A2, + + SCE_NET_ERROR_ENOTINIT = 0x804101C8, + SCE_NET_ERROR_ENOLIBMEM = 0x804101C9, + SCE_NET_ERROR_ERESERVED202 = 0x804101CA, + SCE_NET_ERROR_ECALLBACK = 0x804101CB, + SCE_NET_ERROR_EINTERNAL = 0x804101CC, + SCE_NET_ERROR_ERETURN = 0x804101CD, + + SCE_NET_ERROR_RESOLVER_EINTERNAL = 0x804101DC, + SCE_NET_ERROR_RESOLVER_EBUSY = 0x804101DD, + SCE_NET_ERROR_RESOLVER_ENOSPACE = 0x804101DE, + SCE_NET_ERROR_RESOLVER_EPACKET = 0x804101DF, + SCE_NET_ERROR_RESOLVER_ERESERVED22 = 0x804101E0, + SCE_NET_ERROR_RESOLVER_ENODNS = 0x804101E1, + SCE_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101E2, + SCE_NET_ERROR_RESOLVER_ENOSUPPORT = 0x804101E3, + SCE_NET_ERROR_RESOLVER_EFORMAT = 0x804101E4, + SCE_NET_ERROR_RESOLVER_ESERVERFAILURE = 0x804101E5, + SCE_NET_ERROR_RESOLVER_ENOHOST = 0x804101E6, + SCE_NET_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x804101E7, + SCE_NET_ERROR_RESOLVER_ESERVERREFUSED = 0x804101E8, + SCE_NET_ERROR_RESOLVER_ENORECORD = 0x804101E9, + SCE_NET_ERROR_RESOLVER_EALIGNMENT = 0x804101EA, + + // pspnet_inet + ERROR_NET_INET_ALREADY_INITIALIZED = 0x80410201, + ERROR_NET_INET_SOCKET_BUSY = 0x80410202, + ERROR_NET_INET_CONFIG_INVALID_ARG = 0x80410203, + ERROR_NET_INET_GET_IFADDR = 0x80410204, + ERROR_NET_INET_SET_IFADDR = 0x80410205, + ERROR_NET_INET_DEL_IFADDR = 0x80410206, + ERROR_NET_INET_NO_DEFAULT_ROUTE = 0x80410207, + ERROR_NET_INET_GET_ROUTE = 0x80410208, + ERROR_NET_INET_SET_ROUTE = 0x80410209, + ERROR_NET_INET_FLUSH_ROUTE = 0x8041020a, + ERROR_NET_INET_INVALID_ARG = 0x8041020b, +}; + +// Similar to https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/fd_set.h +#define PSP_NET_INET_FD_SETSIZE 256 // PSP can support upto 256 fd(s) while the default FD_SETSIZE on Windows is only 64 +#define PSP_NET_INET_NFDBITS 32 // Default: 32 = sizeof(u32) * 8 (8-bits of each byte) = number of bits for each element in fds_bits +#define PSP_NET_INET_NFDBITS_SHIFT 5 // 2^5 = 32 +#define PSP_NET_INET_NFDBITS_MASK 0x1F // 0x1F = 5 bit mask (NFDBITS - 1) +#define PSP_NET_INET_FD_MASK 0xFF // Making sure FD value in the range of 0-255 to prevent out-of-bound + +#define NetInetFD_SET(n, p) \ + ((p)->fds_bits[((n) & PSP_NET_INET_FD_MASK)>>PSP_NET_INET_NFDBITS_SHIFT] |= (1 << ((n) & PSP_NET_INET_NFDBITS_MASK))) // (1 << ((n) % PSP_NET_INET_NFDBITS)) +#define NetInetFD_CLR(n, p) \ + ((p)->fds_bits[((n) & PSP_NET_INET_FD_MASK)>>PSP_NET_INET_NFDBITS_SHIFT] &= ~(1 << ((n) & PSP_NET_INET_NFDBITS_MASK))) +#define NetInetFD_ISSET(n, p) \ + ((p)->fds_bits[((n) & PSP_NET_INET_FD_MASK)>>PSP_NET_INET_NFDBITS_SHIFT] & (1 << ((n) & PSP_NET_INET_NFDBITS_MASK))) +#define NetInetFD_ZERO(p) \ + (void)memset((p), 0, sizeof(*(p))) + +// There might be padding (padded to 4 bytes) between each cmsghdr? Similar to http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch14lev1sec6.html#ch14lev1sec6 +struct InetCmsghdr { + s32_le cmsg_len; // length in bytes, including this structure, includes padding between cmsg_type and cmsg_data[] ? + s32_le cmsg_level; // originating protocol + s32_le cmsg_type; // protocol-specific type + // followed by unsigned char cmsg_data[], there might be 4-bytes padding between cmsg_type and cmsg_data[] ? +}; + +struct SceNetInetTimeval { + u32_le tv_sec; // seconds + u32_le tv_usec; // and microseconds +}; + +#ifdef _MSC_VER +#pragma pack(push,1) +#endif + +// FdSet +typedef struct SceNetInetFdSet { + u32_le fds_bits[(PSP_NET_INET_FD_SETSIZE + (PSP_NET_INET_NFDBITS - 1)) / PSP_NET_INET_NFDBITS]; // Default: 8 = ((PSP_NET_INET_FD_SETSIZE(256) + (PSP_NET_INET_NFDBITS-1)) / PSP_NET_INET_NFDBITS(32)) elements of 32-bit array to represents 256(FD_SETSIZE) bits of fd's bitmap +} PACK SceNetInetFdSet; + // Sockaddr typedef struct SceNetInetSockaddr { - uint8_t sa_len; - uint8_t sa_family; - uint8_t sa_data[14]; + uint8_t sa_len; // length of this struct or sa_data only? + uint8_t sa_family; + uint8_t sa_data[14]; // up to 14 bytes of data? } PACK SceNetInetSockaddr; // Sockaddr_in typedef struct SceNetInetSockaddrIn { - uint8_t sin_len; - uint8_t sin_family; - u16_le sin_port; //uint16_t - u32_le sin_addr; //uint32_t - uint8_t sin_zero[8]; + uint8_t sin_len; // length of this struct? + uint8_t sin_family; + u16_le sin_port; //uint16_t + u32_le sin_addr; //uint32_t + uint8_t sin_zero[8]; // zero-filled padding? } PACK SceNetInetSockaddrIn; +// Similar to iovec struct on 32-bit platform from BSD's uio.h/_iovec.h +typedef struct SceNetIovec { + u32_le iov_base; // Base address (pointer/void* of buffer) + u32_le iov_len; // Length +} PACK SceNetIovec; + +// Similar to msghdr struct on 32-bit platform from BSD's socket.h +typedef struct InetMsghdr { + u32_le msg_name; // optional address (pointer/void* to sockaddr_in/SceNetInetSockaddrIn/SceNetInetSockaddr struct?) + u32_le msg_namelen; // size of optional address + u32_le msg_iov; // pointer to iovec/SceNetIovec (ie. struct iovec*/PSPPointer), scatter/gather array (buffers are concatenated before sent?) + s32_le msg_iovlen; // # elements in msg_iov + u32_le msg_control; // pointer (ie. void*/PSPPointer) to ancillary data (multiple of cmsghdr/InetCmsghdr struct?), see below + u32_le msg_controllen; // ancillary data buffer len, includes padding between each cmsghdr/InetCmsghdr struct? + s32_le msg_flags; // flags on received message (ignored on sendmsg?) +} PACK InetMsgHdr; + +// Structure used for manipulating linger option +typedef struct SceNetInetLinger { + s32_le l_onoff; // option on/off + s32_le l_linger; // linger time in seconds +} PACK SceNetInetLinger; + // Polling Event Field typedef struct SceNetInetPollfd { //similar format to pollfd in 32bit (pollfd in 64bit have different size) - s32_le fd; - s16_le events; - s16_le revents; + s32_le fd; + s16_le events; + s16_le revents; } PACK SceNetInetPollfd; -enum { - // pspnet_inet - ERROR_NET_INET_ALREADY_INITIALIZED = 0x80410201, - ERROR_NET_INET_SOCKET_BUSY = 0x80410202, - ERROR_NET_INET_CONFIG_INVALID_ARG = 0x80410203, - ERROR_NET_INET_GET_IFADDR = 0x80410204, - ERROR_NET_INET_SET_IFADDR = 0x80410205, - ERROR_NET_INET_DEL_IFADDR = 0x80410206, - ERROR_NET_INET_NO_DEFAULT_ROUTE = 0x80410207, - ERROR_NET_INET_GET_ROUTE = 0x80410208, - ERROR_NET_INET_SET_ROUTE = 0x80410209, - ERROR_NET_INET_FLUSH_ROUTE = 0x8041020a, - ERROR_NET_INET_INVALID_ARG = 0x8041020b, -}; +// TCP & UDP Socket Union (Internal use only) +typedef struct InetSocket { + s32_le id; // posix socket id + s32_le domain; // AF_INET/PF_INET/etc + s32_le type; // SOCK_STREAM/SOCK_DGRAM/etc + s32_le protocol; // TCP/UDP/etc + s32_le nonblocking; // non-blocking flag (ie. FIONBIO) to keep track of the blocking mode since Windows doesn't have getter for this + s32_le so_broadcast; // broadcast flag (ie. SO_BROADCAST) to keep track of the broadcast flag, since we're using fake broadcast + s32_le tcp_state; // to keep track TCP connection state +} PACK InetSocket; -enum PspInetAddressFamily { - PSP_NET_INET_AF_UNSPEC = 0, - PSP_NET_INET_AF_LOCAL = 1, - PSP_NET_INET_AF_INET = 2, -}; +#ifdef _MSC_VER +#pragma pack(pop) +#endif -enum PspInetSocketLevel { - PSP_NET_INET_SOL_SOCKET = 0xFFFF, -}; +// ----------------- DNS Header ----------------------------- +// Based on https://web.archive.org/web/20201204080751/https://www.binarytides.com/dns-query-code-in-c-with-winsock/ +typedef struct +{ + unsigned short id; // identification number + unsigned char rd : 1; // recursion desired + unsigned char tc : 1; // truncated message + unsigned char aa : 1; // authoritive answer + unsigned char opcode : 4; // purpose of message + unsigned char qr : 1; // query/response flag + unsigned char rcode : 4; // response code + unsigned char cd : 1; // checking disabled + unsigned char ad : 1; // authenticated data + unsigned char z : 1; // its z! reserved + unsigned char ra : 1; // recursion available + unsigned short q_count; // number of question entries + unsigned short ans_count; // number of answer entries + unsigned short auth_count; // number of authority entries + unsigned short add_count; // number of resource entries +} DNS_HEADER; -enum PspInetSocketType { - PSP_NET_INET_SOCK_STREAM = 1, - PSP_NET_INET_SOCK_DGRAM = 2, - PSP_NET_INET_SOCK_RAW = 3, - PSP_NET_INET_SOCK_RDM = 4, - PSP_NET_INET_SOCK_SEQPACKET = 5, - PSP_NET_INET_SOCK_DCCP = 6, - PSP_NET_INET_SOCK_PACKET = 10, - PSP_NET_INET_SOCK_TYPE_MASK = 0xF, -}; +typedef struct +{ + unsigned short qtype; + unsigned short qclass; +} QUESTION; -enum PspInetSocketTypeFlag { - PSP_NET_INET_SOCK_CLOEXEC = 0x10000000, - PSP_NET_INET_SOCK_NONBLOCK = 0x20000000, - PSP_NET_INET_SOCK_NOSIGPIPE = 0x40000000, - PSP_NET_INET_SOCK_FLAGS_MASK = 0xF0000000, -}; +typedef struct +{ + unsigned short type; + unsigned short _class; + unsigned int ttl; + unsigned short data_len; +} R_DATA; -// TODO: revisit protocols, not all are necessary -enum PspInetProtocol { - PSP_NET_INET_IPPROTO_IP = 0, // dummy for IP - PSP_NET_INET_IPPROTO_UNSPEC = 0, // 0 will defaulted to the only existing protocol for that particular domain/family and type - PSP_NET_INET_IPPROTO_ICMP = 1, // control message protocol - PSP_NET_INET_IPPROTO_IGMP = 2, // group mgmt protocol - PSP_NET_INET_IPPROTO_TCP = 6, // tcp - PSP_NET_INET_IPPROTO_EGP = 8, // exterior gateway protocol - PSP_NET_INET_IPPROTO_PUP = 12, // pup - PSP_NET_INET_IPPROTO_UDP = 17, // user datagram protocol - PSP_NET_INET_IPPROTO_IDP = 22, // xns idp - PSP_NET_INET_IPPROTO_RAW = 255, // raw IP packet -}; +typedef struct +{ + unsigned char* name; + R_DATA* resource; + unsigned char* rdata; +} RES_RECORD; -// TODO: INET_ -enum PspInetSocketOptionName { - // TODO: also specify minimum socket size - INET_SO_DEBUG = 0x0001, // turn on debugging info recording - INET_SO_ACCEPTCONN = 0x0002, // socket has had listen() - INET_SO_REUSEADDR = 0x0004, // allow local address reuse - INET_SO_KEEPALIVE = 0x0008, // keep connections alive - INET_SO_DONTROUTE = 0x0010, // just use interface addresses - INET_SO_BROADCAST = 0x0020, // permit sending of broadcast msgs - INET_SO_USELOOPBACK = 0x0040, // bypass hardware when possible - INET_SO_LINGER = 0x0080, // linger on close if data present - INET_SO_OOBINLINE = 0x0100, // leave received OOB data in line - INET_SO_REUSEPORT = 0x0200, // allow local address & port reuse - INET_SO_TIMESTAMP = 0x0400, // timestamp received dgram traffic - INET_SO_ONESBCAST = 0x0800, // allow broadcast to 255.255.255.255 - INET_SO_SNDBUF = 0x1001, // send buffer size - INET_SO_RCVBUF = 0x1002, // receive buffer size - INET_SO_SNDLOWAT = 0x1003, // send low-water mark - INET_SO_RCVLOWAT = 0x1004, // receive low-water mark - INET_SO_SNDTIMEO = 0x1005, // send timeout - INET_SO_RCVTIMEO = 0x1006, // receive timeout - INET_SO_ERROR = 0x1007, // get error status and clear - INET_SO_TYPE = 0x1008, // get socket type - INET_SO_OVERFLOWED = 0x1009, // datagrams: return packets dropped - INET_SO_NONBLOCK = 0x1009, // non blocking I/O? -}; +typedef struct +{ + unsigned char* name; + QUESTION* ques; +} QUERY; +// --------------------------------------------------------- -enum PspInetLimit { - PSP_NET_INET_SOMAXCONN = 128, -}; +class PointerWrap; -enum PspInetMessageFlag { - INET_MSG_OOB = 1, - INET_MSG_PEEK = 1 << 1, - INET_MSG_DONTROUTE = 1 << 2, - INET_MSG_EOR = 1 << 3, - INET_MSG_TRUNC = 1 << 4, - INET_MSG_CTRUNC = 1 << 5, - INET_MSG_WAITALL = 1 << 6, - INET_MSG_DONTWAIT = 1 << 7, - INET_MSG_BCAST = 1 << 8, - INET_MSG_MCAST = 1 << 9 -}; +extern bool netInited; +extern bool netInetInited; +extern bool netApctlInited; +extern u32 netApctlState; +extern SceNetApctlInfoInternal netApctlInfo; +extern std::string defaultNetConfigName; +extern std::string defaultNetSSID; -enum InetErrorCode { - INET_EAGAIN = 0x0B, - INET_ETIMEDOUT = 0x74, - INET_EINPROGRESS = 119, - INET_EISCONN = 0x7F, -}; +void Register_sceNetInet(); -class SceNetInet { -public: - static bool Init(); - static bool Shutdown(); - static std::shared_ptr Get() { - return gInstance; - } - - static bool Inited() { - return gInstance.get() != nullptr; - } - - // TODO: document that errno should be set to EAFNOSUPPORT when this returns false - static bool TranslateInetAddressFamilyToNative(int &destAddressFamily, int srcAddressFamily); - static inline bool TranslateInetSocketLevelToNative(int &destSocketLevel, int srcSocketLevel); - // TODO: document that errno should be set to ESOMETHING when this returns false - static bool TranslateInetSocketTypeToNative(int &destSocketType, bool &destNonBlocking, int srcSocketType); - // TODO: document that errno should be set to EPROTONOSUPPORT when this returns false - static bool TranslateInetProtocolToNative(int &destProtocol, int srcProtocol); - // TODO: document that errno should be set to EPROTONOSUPPORT when this returns false - static bool TranslateInetOptnameToNativeOptname(int &destOptname, int inetOptname); - static int TranslateInetFlagsToNativeFlags(int messageFlags, bool nonBlocking); - static int TranslateNativeErrorToInetError(int nativeError); - - // TODO: document - - int GetLastError(); - void SetLastError(int error); - int SetLastErrorToMatchPlatform(); - std::shared_ptr CreateAndAssociateInetSocket(int nativeSocketId, int protocol, bool nonBlocking); - std::shared_ptr GetInetSocket(int inetSocketId); - bool GetNativeSocketIdForInetSocketId(int &nativeSocketId, int inetSocketId); - bool EraseNativeSocket(int inetSocketId); - bool TranslateInetFdSetToNativeFdSet(int& maxFd, fd_set &destFdSet, u32 fdsPtr) const; - -private: - void CloseAllRemainingSockets() const; - - static std::shared_ptr gInstance; - static std::shared_mutex gLock; - static std::unordered_map gInetAddressFamilyToNativeAddressFamily; - // TODO: document that this does not include flags - static std::unordered_map gInetSocketTypeToNativeSocketType; - static std::unordered_map gInetProtocolToNativeProtocol; - // TODO: Handle commented out options - static std::unordered_map gInetSocketOptnameToNativeOptname; - static std::unordered_map gInetMessageFlagToNativeMessageFlag; - - static std::unordered_map gNativeErrorCodeToInetErrorCode; - - int mLastError = 0; - std::unordered_map> mInetSocketIdToNativeSocket; - int mCurrentInetSocketId = 0; - std::shared_mutex mLock; -}; +void __NetInetShutdown(); + +int NetApctl_GetState(); + +int sceNetApctlConnect(int connIndex); +int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout); +int sceNetInetTerm(); +int sceNetApctlTerm(); +int sceNetApctlDisconnect(); void Register_sceNetInet(); diff --git a/Core/HLE/sceNetResolver.cpp b/Core/HLE/sceNetResolver.cpp index 970dafee5886..bd46f3bfbaa2 100644 --- a/Core/HLE/sceNetResolver.cpp +++ b/Core/HLE/sceNetResolver.cpp @@ -54,7 +54,6 @@ #include "Core/HLE/sceNp.h" #include "Core/Reporting.h" #include "Core/Instance.h" -#include "Core/Net/InetSocket.h" #if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE) // Missing toolchain define diff --git a/Core/Net/InetSocket.cpp b/Core/Net/InetSocket.cpp deleted file mode 100644 index 945ecd077ea2..000000000000 --- a/Core/Net/InetSocket.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// TODO: remove me if the functions remain inlined - -#include "InetSocket.h" diff --git a/Core/Net/InetSocket.h b/Core/Net/InetSocket.h deleted file mode 100644 index 25a36b2e87ef..000000000000 --- a/Core/Net/InetSocket.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -// TODO: document -class InetSocket { -public: - InetSocket(int sceSocketId, int nativeSocketId, int protocol, bool nonBlocking) : - mInetSocketId(sceSocketId), - mNativeSocketId(nativeSocketId), - mProtocol(protocol), - mNonBlocking(nonBlocking) {} - - int GetInetSocketId() const { - return mInetSocketId; - } - - int GetNativeSocketId() const { - return mNativeSocketId; - } - - bool IsNonBlocking() const { - return mNonBlocking; - } - - void SetNonBlocking(const bool nonBlocking) { - mNonBlocking = nonBlocking; - } - - // TODO: document that this is the native protocol - int GetProtocol() const { - return mProtocol; - } -private: - int mInetSocketId; - int mNativeSocketId; - int mProtocol; - bool mNonBlocking = false; -}; diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 4d6095137207..6852b5e246f3 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -318,7 +318,6 @@ - @@ -618,7 +617,6 @@ - @@ -1038,4 +1036,4 @@ - \ No newline at end of file + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index e6172e3d0f43..0547dd9dee76 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -1226,9 +1226,6 @@ Net - - Net - Net @@ -1942,9 +1939,6 @@ Net - - Net - Net @@ -1954,4 +1948,4 @@ Ext\gason - \ No newline at end of file + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 6d93211ad584..9c3e5284a5e2 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -511,7 +511,6 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/MIPS/IR/IRPassSimplify.cpp \ $(SRC)/Core/MIPS/IR/IRRegCache.cpp \ $(SRC)/Core/Net/InetCommon.cpp \ - $(SRC)/Core/Net/InetSocket.cpp \ $(SRC)/Core/Net/NetResolver.cpp \ $(SRC)/GPU/Math3D.cpp \ $(SRC)/GPU/GPU.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 008747f1622f..3c1c8f200f5e 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -815,7 +815,6 @@ SOURCES_CXX += \ $(COREDIR)/MemMap.cpp \ $(COREDIR)/MemMapFunctions.cpp \ $(COREDIR)/Net/InetCommon.cpp \ - $(COREDIR)/Net/InetSocket.cpp \ $(COREDIR)/Net/NetResolver.cpp \ $(COREDIR)/PSPLoaders.cpp \ $(COREDIR)/Replay.cpp \ From 8d16ea408e565ddf9c031d1e8b4a3240c4986aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 15:33:15 +0100 Subject: [PATCH 2/2] Buildfix --- Core/HLE/sceNet.h | 1 - Core/HLE/sceNetInet.cpp | 6 +++--- Core/HLE/sceNetInet.h | 3 --- ext/rcheevos | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index d475bf5265d1..980f9cb56a1b 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -263,5 +263,4 @@ int NetApctl_GetState(); int sceNetApctlConnect(int connIndex); int sceNetInetPoll(void *fds, u32 nfds, int timeout); -int sceNetInetTerm(); int sceNetApctlTerm(); diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index f1691b70d6bd..28912c6dd6a1 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -1207,7 +1207,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr tmout.tv_sec = timeout->tv_sec; tmout.tv_usec = timeout->tv_usec; } - VERBOSE_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, tmout.tv_sec, tmout.tv_usec); + VERBOSE_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing retval = select(nfds, (readfds == NULL) ? NULL : &rdfds, (writefds == NULL) ? NULL : &wrfds, (exceptfds == NULL) ? NULL : &exfds, /*(timeout == NULL) ? NULL :*/ &tmout); if (readfds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(readfds); // Clear it only when not needing a workaround @@ -1287,7 +1287,7 @@ int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in milisecond fdarray[i].revents |= (INET_POLLRDBAND | INET_POLLPRI | INET_POLLERR); //POLLEX_SET; // Can be raised on revents regardless of events bitmask? if (fdarray[i].revents) retval++; - VERBOSE_LOG(Log::sceNet, "Poll Socket#%d Fd: %d, events: %04x, revents: %04x, availToRecv: %d", i, fdarray[i].fd, fdarray[i].events, fdarray[i].revents, getAvailToRecv(fdarray[i].fd)); + VERBOSE_LOG(Log::sceNet, "Poll Socket#%d Fd: %d, events: %04x, revents: %04x, availToRecv: %d", i, fdarray[i].fd, fdarray[i].events, fdarray[i].revents, (int)getAvailToRecv(fdarray[i].fd)); } //hleEatMicro(1000); return hleLogSuccessI(Log::sceNet, hleDelayResult(retval, "workaround until blocking-socket", 1000)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented @@ -1435,7 +1435,7 @@ static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPt // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { - //*optlen = std::min(sizeof(sock->so_broadcast), *optlen); + // *optlen = std::min(sizeof(sock->so_broadcast), *optlen); //memcpy((int*)optval, &sock->so_broadcast, *optlen); //if (sock->so_broadcast && *optlen>0) *optval = 0x80; // on true, returning 0x80 when retrieved using getsockopt? return hleLogSuccessI(Log::sceNet, 0); diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h index aa83a60c39d9..e00ef9548c75 100644 --- a/Core/HLE/sceNetInet.h +++ b/Core/HLE/sceNetInet.h @@ -560,8 +560,5 @@ int NetApctl_GetState(); int sceNetApctlConnect(int connIndex); int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout); -int sceNetInetTerm(); int sceNetApctlTerm(); int sceNetApctlDisconnect(); - -void Register_sceNetInet(); diff --git a/ext/rcheevos b/ext/rcheevos index 3a91a58605c0..32917bdddf49 160000 --- a/ext/rcheevos +++ b/ext/rcheevos @@ -1 +1 @@ -Subproject commit 3a91a58605c0fb05833a228dbb674357b0e65a09 +Subproject commit 32917bdddf4982e62047862c6633e7671aaaf2cb