diff --git a/CMakeLists.txt b/CMakeLists.txt index fd67639ecd..c772d47e8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2251,6 +2251,12 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceNetAdhoc.h Core/HLE/sceNetAdhocMatching.cpp Core/HLE/sceNetAdhocMatching.h + Core/HLE/sceNetInet.cpp + Core/HLE/sceNetInet.h + Core/HLE/sceNetApctl.cpp + Core/HLE/sceNetApctl.h + Core/HLE/sceNetResolver.cpp + Core/HLE/sceNetResolver.h Core/HLE/proAdhoc.h Core/HLE/proAdhoc.cpp Core/HLE/proAdhocServer.h @@ -2378,6 +2384,12 @@ add_library(${CoreLibName} ${CoreLinkType} Core/MIPS/MIPSAsm.h Core/MIPS/MIPSTracer.cpp Core/MIPS/MIPSTracer.h + Core/Net/InetCommon.cpp + 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 @@ -2467,6 +2479,11 @@ include_directories(ext/libchdr/include) target_link_libraries(${CoreLibName} Common native chdr kirk cityhash sfmt19937 xbrz xxhash rcheevos minimp3 at3_standalone lua ${GlslangLibs} ${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS}) +# Winsock +if(WIN32) + target_link_libraries(${CoreLibName} ws2_32 winhttp) +endif() + if(NOT HTTPS_NOT_AVAILABLE) target_link_libraries(${CoreLibName} naett) if(WIN32) diff --git a/Core/Config.cpp b/Core/Config.cpp index 66f9ab7419..3fdc4c2169 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1244,6 +1244,10 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { mPostShaderSetting[key] = std::stof(value); } + const Section *hostOverrideSetting = iniFile.GetOrCreateSection("HostAliases"); + // TODO: relocate me before PR + mHostToAlias = hostOverrideSetting->ToMap(); + // Load post process shader names vPostShaderNames.clear(); for (const auto& it : postShaderChain->ToMap()) { @@ -1376,6 +1380,13 @@ bool Config::Save(const char *saveReason) { } } + // TODO: relocate me before PR + Section *hostOverrideSetting = iniFile.GetOrCreateSection("HostAliases"); + hostOverrideSetting->Clear(); + for (auto& it : mHostToAlias) { + hostOverrideSetting->Set(it.first.c_str(), it.second.c_str()); + } + Section *control = iniFile.GetOrCreateSection("Control"); control->Delete("DPadRadius"); diff --git a/Core/Config.h b/Core/Config.h index 6fa7fd23b6..a54a24665f 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -458,6 +458,7 @@ public: // Networking std::string proAdhocServer; bool bEnableWlan; + std::map mHostToAlias; // TODO: mPostShaderSetting bool bEnableAdhocServer; bool bEnableUPnP; bool bUPnPUseOriginalPort; @@ -482,6 +483,7 @@ public: int iFirmwareVersion; bool bBypassOSKWithKeyboard; + // Virtual reality bool bEnableVR; bool bEnable6DoF; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 8f0a6a16fd..1c783385e5 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -572,6 +572,9 @@ + + + @@ -625,6 +628,9 @@ + + + @@ -1185,6 +1191,9 @@ + + + @@ -1211,6 +1220,9 @@ + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 6e5e2f17a0..f0c86bbc6d 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -94,6 +94,9 @@ {067e3128-3aaf-4ed1-b19e-bab11606abe7} + + {f1b3f688-26eb-4121-9914-e9e19ab04bcd} + @@ -1321,6 +1324,24 @@ Dialog + + HLE\Libraries + + + HLE\Libraries + + + HLE\Libraries + + + Net + + + Net + + + Net + @@ -2121,6 +2142,24 @@ Dialog + + HLE\Libraries + + + HLE\Libraries + + + HLE\Libraries + + + Net + + + Net + + + Net + diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index d745818338..c1af60f5b4 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -438,11 +438,21 @@ template void WrapI_IIIUI() { RETURN(retval); } +template void WrapI_IIIUU() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); + RETURN(retval); +} + template void WrapI_IUUII() { int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); } +template void WrapI_IUUIII() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); + RETURN(retval); +} + template void WrapI_ICIUU() { int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); @@ -699,6 +709,11 @@ template void WrapI_IUUUUU() { RETURN(retval); } +template void WrapI_IUUIUU() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); + RETURN(retval); +} + template void WrapI_IUII() { int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); RETURN(retval); @@ -755,6 +770,11 @@ template void WrapI_IUUU() { RETURN(retval); } +template void WrapI_IUUI() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapI_IUU() { int retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); @@ -800,6 +820,11 @@ template void WrapI_ICI() { RETURN(retval); } +template void WrapI_ICU() { + int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2)); + RETURN(retval); +} + template void WrapI_IVVVVUI(){ u32 retval = func(PARAM(0), Memory::GetPointerWrite(PARAM(1)), Memory::GetPointerWrite(PARAM(2)), Memory::GetPointerWrite(PARAM(3)), Memory::GetPointerWrite(PARAM(4)), PARAM(5), PARAM(6) ); RETURN(retval); diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 4735322c40..b91c1e60fe 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -81,6 +81,8 @@ #include "sceSfmt19937.h" #include "sceG729.h" #include "KUBridge.h" +#include "sceNetInet.h" +#include "sceNetResolver.h" #define N(s) s @@ -313,6 +315,8 @@ void RegisterAllModules() { Register_mp4msv(); Register_InterruptManagerForKernel(); Register_sceSircs(); + Register_sceNetInet(); + Register_sceNetResolver(); // add new modules here. } diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 245418af66..fc353ec341 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -53,6 +53,7 @@ #include "Core/HLE/sceKernelMutex.h" #include "Core/HLE/sceUtility.h" +// TODO: move this to some common set #ifdef _WIN32 #undef errno #undef ESHUTDOWN diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 9423b02030..4c20186116 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -20,13 +20,20 @@ #include #include #include +#elif _WIN32 +#include +#pragma comment(lib, "ws2_32.lib") #endif +// TODO: fixme move Core/Net to Common/Net +#include "Common/Net/Resolve.h" +#include "Core/Net/InetCommon.h" #include "Common/Data/Text/Parsers.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" +#include "Core/Config.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceKernelMemory.h" @@ -44,6 +51,12 @@ #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNetAdhocMatching.h" #include "Core/HLE/sceNet.h" + +#include +#include + +#include "sceNetInet.h" +#include "sceNetResolver.h" #include "Core/HLE/sceNp.h" #include "Core/CoreTiming.h" #include "Core/Instance.h" @@ -54,7 +67,6 @@ #endif bool netInited; -bool netInetInited; u32 netDropRate = 0; u32 netDropDuration = 0; @@ -131,6 +143,25 @@ void InitLocalhostIP() { isLocalServer = (!strcasecmp(serverStr.c_str(), "localhost") || serverStr.find("127.") == 0); } +static bool __PlatformNetInit() { +#ifdef _WIN32 + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + // TODO: log + return false; + } +#endif + return true; +} + +static bool __PlatformNetShutdown() { +#ifdef _WIN32 + return WSACleanup() == 0; +#else + return true; +#endif +} + static void __ApctlState(u64 userdata, int cyclesLate) { SceUID threadID = userdata >> 32; int uid = (int)(userdata & 0xFFFFFFFF); @@ -176,7 +207,7 @@ void __NetApctlInit() { static void __ResetInitNetLib() { netInited = false; - netInetInited = false; + // netInetInited = false; memset(&netMallocStat, 0, sizeof(netMallocStat)); memset(¶meter, 0, sizeof(parameter)); @@ -211,7 +242,8 @@ void __NetInit() { SceNetEtherAddr mac; getLocalMac(&mac); INFO_LOG(Log::sceNet, "LocalHost IP will be %s [%s]", ip2str(g_localhostIP.in.sin_addr).c_str(), mac2str(&mac).c_str()); - + + __PlatformNetInit(); // TODO: May be we should initialize & cleanup somewhere else than here for PortManager to be used as general purpose for whatever port forwarding PPSSPP needed __UPnPInit(); @@ -233,12 +265,16 @@ void __NetShutdown() { // Network Cleanup Net_Term(); + SceNetResolver::Shutdown(); + SceNetInet::Shutdown(); __NetApctlShutdown(); __ResetInitNetLib(); // Since PortManager supposed to be general purpose for whatever port forwarding PPSSPP needed, may be we shouldn't clear & restore ports in here? it will be cleared and restored by PortManager's destructor when exiting PPSSPP anyway __UPnPShutdown(); + __PlatformNetShutdown(); + free(dummyPeekBuf64k); } @@ -268,11 +304,11 @@ void __NetDoState(PointerWrap &p) { return; auto cur_netInited = netInited; - auto cur_netInetInited = netInetInited; + // auto cur_netInetInited = netInetInited; auto cur_netApctlInited = netApctlInited; Do(p, netInited); - Do(p, netInetInited); + // Do(p, netInetInited); Do(p, netApctlInited); Do(p, apctlHandlers); Do(p, netMallocStat); @@ -317,11 +353,13 @@ 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; + // netInetInited = cur_netInetInited; netInited = cur_netInited; // Discard leftover events apctlEvents.clear(); + // Discard created resolvers + SceNetResolver::Shutdown(); } } @@ -795,21 +833,6 @@ static int sceNetGetMallocStat(u32 statPtr) { return 0; } -static int sceNetInetInit() { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetInit()"); - if (netInetInited) return ERROR_NET_INET_ALREADY_INITIALIZED; - netInetInited = true; - - return 0; -} - -int sceNetInetTerm() { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetTerm()"); - netInetInited = false; - - return 0; -} - void NetApctl_InitInfo() { memset(&netApctlInfo, 0, sizeof(netApctlInfo)); // Set dummy/fake values, these probably not suppose to have valid info before connected to an AP, right? @@ -823,7 +846,8 @@ void NetApctl_InitInfo() { if (netApctlInfo.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) netApctlInfo.channel = defaultWlanChannel; // Get Local IP Address sockaddr_in sockAddr; - getLocalIp(&sockAddr); // This will be valid IP, we probably not suppose to have a valid IP before connected to any AP, right? + socklen_t socklen = sizeof(sockaddr_in); + getDefaultOutboundSockaddr(sockAddr, socklen); // This will be valid IP, we probably not suppose to have a valid IP before connected to any AP, right? char ipstr[INET_ADDRSTRLEN] = "127.0.0.1"; // Patapon 3 seems to try to get current IP using ApctlGetInfo() right after ApctlInit(), what kind of IP should we use as default before ApctlConnect()? it shouldn't be a valid IP, right? inet_ntop(AF_INET, &sockAddr.sin_addr, ipstr, sizeof(ipstr)); truncate_cpy(netApctlInfo.ip, sizeof(netApctlInfo.ip), ipstr); @@ -985,6 +1009,7 @@ static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { if (!Memory::IsValidRange(pInfoAddr, 4)) return hleLogError(Log::sceNet, -1, "apctl invalid arg"); Memory::WriteUnchecked_U32(netApctlInfo.useProxy, pInfoAddr); + // Memory::WriteUnchecked_U32(1, pInfoAddr); NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_PROXY_URL: @@ -1084,107 +1109,6 @@ static int sceNetApctlDelHandler(u32 handlerID) { return NetApctl_DelHandler(handlerID); } -static int sceNetInetInetAton(const char *hostname, u32 addrPtr) { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetInetAton(%s, %08x)", hostname, addrPtr); - return -1; -} - -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); - int retval = -1; - SceNetInetPollfd *fdarray = (SceNetInetPollfd *)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) return -1; - 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].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 - retval = select(nfds, &readfds, &writefds, &exceptfds, &tmout); - if (retval < 0) return -1; - retval = 0; - for (int i = 0; i < (s32)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) retval++; - } -//#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? - } - free(fdtmp); - */ -//#endif - return retval; -} - -static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetRecv(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); - return -1; -} - -static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetSend(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); - return -1; -} - -static int sceNetInetGetErrno() { - ERROR_LOG(Log::sceNet, "UNTESTED sceNetInetGetErrno()"); - int error = errno; - switch (error) { - case ETIMEDOUT: - return INET_ETIMEDOUT; - case EISCONN: - return INET_EISCONN; - case EINPROGRESS: - return INET_EINPROGRESS; - //case EAGAIN: - // return INET_EAGAIN; - } - return error; //-1; -} - -static int sceNetInetSocket(int domain, int type, int protocol) { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetSocket(%i, %i, %i)", domain, type, protocol); - return -1; -} - -static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetSetsockopt(%i, %i, %i, %08x, %i)", socket, level, optname, optvalPtr, optlen); - return -1; -} - -static int sceNetInetConnect(int socket, u32 sockAddrInternetPtr, int addressLength) { - ERROR_LOG(Log::sceNet, "UNIMPL sceNetInetConnect(%i, %08x, %i)", socket, sockAddrInternetPtr, addressLength); - return -1; -} - int sceNetApctlConnect(int connIndex) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i)", __FUNCTION__, connIndex); // Is this connIndex is the index to the scanning's result data or sceNetApctlGetBSSDescIDListUser result? @@ -1374,12 +1298,6 @@ static int sceNetApctlGetBSSDescEntry2(int entryId, int infoId, u32 resultAddr) return NetApctl_GetBSSDescEntryUser(entryId, infoId, resultAddr); } -static int sceNetResolverInit() -{ - ERROR_LOG(Log::sceNet, "UNIMPL %s()", __FUNCTION__); - return 0; -} - static int sceNetApctlAddInternalHandler(u32 handlerPtr, u32 handlerArg) { ERROR_LOG(Log::sceNet, "UNIMPL %s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg); // This seems to be a 2nd kind of handler @@ -1433,46 +1351,38 @@ static int sceNetApctl_lib2_C20A144C(int connIndex, u32 ps3MacAddressPtr) { return sceNetApctlConnect(connIndex); } - -static int sceNetUpnpInit(int unknown1,int unknown2) -{ +static int sceNetUpnpInit(int unknown1,int unknown2) { ERROR_LOG_REPORT_ONCE(sceNetUpnpInit, Log::sceNet, "UNIMPLsceNetUpnpInit %d,%d",unknown1,unknown2); return 0; } -static int sceNetUpnpStart() -{ +static int sceNetUpnpStart() { ERROR_LOG(Log::sceNet, "UNIMPLsceNetUpnpStart"); return 0; } -static int sceNetUpnpStop() -{ +static int sceNetUpnpStop() { ERROR_LOG(Log::sceNet, "UNIMPLsceNetUpnpStop"); return 0; } -static int sceNetUpnpTerm() -{ +static int sceNetUpnpTerm() { ERROR_LOG(Log::sceNet, "UNIMPLsceNetUpnpTerm"); return 0; } -static int sceNetUpnpGetNatInfo() -{ +static int sceNetUpnpGetNatInfo() { ERROR_LOG(Log::sceNet, "UNIMPLsceNetUpnpGetNatInfo"); return 0; } -static int sceNetGetDropRate(u32 dropRateAddr, u32 dropDurationAddr) -{ +static int sceNetGetDropRate(u32 dropRateAddr, u32 dropDurationAddr) { Memory::Write_U32(netDropRate, dropRateAddr); Memory::Write_U32(netDropDuration, dropDurationAddr); return hleLogSuccessInfoI(Log::sceNet, 0); } -static int sceNetSetDropRate(u32 dropRate, u32 dropDuration) -{ +static int sceNetSetDropRate(u32 dropRate, u32 dropDuration) { netDropRate = dropRate; netDropDuration = dropDuration; return hleLogSuccessInfoI(Log::sceNet, 0); @@ -1489,54 +1399,6 @@ const HLEFunction sceNet[] = { {0XAD6844C6, &WrapI_I, "sceNetThreadAbort", 'i', "i" }, }; -const HLEFunction sceNetResolver[] = { - {0X224C5F44, nullptr, "sceNetResolverStartNtoA", '?', "" }, - {0X244172AF, nullptr, "sceNetResolverCreate", '?', "" }, - {0X94523E09, nullptr, "sceNetResolverDelete", '?', "" }, - {0XF3370E61, &WrapI_V, "sceNetResolverInit", 'i', "" }, - {0X808F6063, nullptr, "sceNetResolverStop", '?', "" }, - {0X6138194A, nullptr, "sceNetResolverTerm", '?', "" }, - {0X629E2FB7, nullptr, "sceNetResolverStartAtoN", '?', "" }, - {0X14C17EF9, nullptr, "sceNetResolverStartNtoAAsync", '?', "" }, - {0XAAC09184, nullptr, "sceNetResolverStartAtoNAsync", '?', "" }, - {0X12748EB9, nullptr, "sceNetResolverWaitAsync", '?', "" }, - {0X4EE99358, nullptr, "sceNetResolverPollAsync", '?', "" }, -}; - -const HLEFunction sceNetInet[] = { - {0X17943399, &WrapI_V, "sceNetInetInit", 'i', "" }, - {0X4CFE4E56, nullptr, "sceNetInetShutdown", '?', "" }, - {0XA9ED66B9, &WrapI_V, "sceNetInetTerm", 'i', "" }, - {0X8B7B220F, &WrapI_III, "sceNetInetSocket", 'i', "iii" }, - {0X2FE71FE7, &WrapI_IIIUI, "sceNetInetSetsockopt", 'i', "iiixi"}, - {0X4A114C7C, nullptr, "sceNetInetGetsockopt", '?', "" }, - {0X410B34AA, &WrapI_IUI, "sceNetInetConnect", 'i', "ixi" }, - {0X805502DD, nullptr, "sceNetInetCloseWithRST", '?', "" }, - {0XD10A1A7A, nullptr, "sceNetInetListen", '?', "" }, - {0XDB094E1B, nullptr, "sceNetInetAccept", '?', "" }, - {0XFAABB1DD, &WrapI_VUI, "sceNetInetPoll", 'i', "pxi" }, - {0X5BE8D595, nullptr, "sceNetInetSelect", '?', "" }, - {0X8D7284EA, nullptr, "sceNetInetClose", '?', "" }, - {0XCDA85C99, &WrapI_IUUU, "sceNetInetRecv", 'i', "ixxx" }, - {0XC91142E4, nullptr, "sceNetInetRecvfrom", '?', "" }, - {0XEECE61D2, nullptr, "sceNetInetRecvmsg", '?', "" }, - {0X7AA671BC, &WrapI_IUUU, "sceNetInetSend", 'i', "ixxx" }, - {0X05038FC7, nullptr, "sceNetInetSendto", '?', "" }, - {0X774E36F4, nullptr, "sceNetInetSendmsg", '?', "" }, - {0XFBABE411, &WrapI_V, "sceNetInetGetErrno", 'i', "" }, - {0X1A33F9AE, nullptr, "sceNetInetBind", '?', "" }, - {0XB75D5B0A, nullptr, "sceNetInetInetAddr", '?', "" }, - {0X1BDF5D13, &WrapI_CU, "sceNetInetInetAton", 'i', "sx" }, - {0XD0792666, nullptr, "sceNetInetInetNtop", '?', "" }, - {0XE30B8C19, nullptr, "sceNetInetInetPton", '?', "" }, - {0X8CA3A97E, nullptr, "sceNetInetGetPspError", '?', "" }, - {0XE247B6D6, nullptr, "sceNetInetGetpeername", '?', "" }, - {0X162E6FD5, nullptr, "sceNetInetGetsockname", '?', "" }, - {0X80A21ABD, nullptr, "sceNetInetSocketAbort", '?', "" }, - {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, - {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, -}; - const HLEFunction sceNetApctl[] = { {0XCFB957C6, &WrapI_I, "sceNetApctlConnect", 'i', "i" }, {0X24FE91A1, &WrapI_V, "sceNetApctlDisconnect", 'i', "" }, @@ -1588,8 +1450,6 @@ const HLEFunction sceNetIfhandle[] = { void Register_sceNet() { RegisterModule("sceNet", ARRAY_SIZE(sceNet), sceNet); - RegisterModule("sceNetResolver", ARRAY_SIZE(sceNetResolver), sceNetResolver); - RegisterModule("sceNetInet", ARRAY_SIZE(sceNetInet), sceNetInet); RegisterModule("sceNetApctl", ARRAY_SIZE(sceNetApctl), sceNetApctl); } diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index d34ab560dd..043ffd2773 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -17,7 +17,6 @@ #pragma once -#include #include "Core/HLE/proAdhoc.h" // Using constants instead of numbers for readability reason, since PSP_THREAD_ATTR_KERNEL/USER is located in sceKernelThread.cpp instead of sceKernelThread.h @@ -32,14 +31,8 @@ #define PSP_SO_REUSEPORT 0x0200 #define PSP_SO_NBIO 0x1009 -// Infrastructure Errno Numbers -#define INET_EAGAIN 0x0B -#define INET_ETIMEDOUT 0x74 -#define INET_EINPROGRESS 0x77 -#define INET_EISCONN 0x7F - // On-Demand Nonblocking Flag -#define INET_MSG_DONTWAIT 0x80 +// #define INET_MSG_DONTWAIT 0x80 // Event Flags #define INET_POLLRDNORM 0x0040 @@ -64,19 +57,6 @@ enum { ERROR_NET_CORE_80211_NO_BSS = 0x80410106, ERROR_NET_CORE_80211_NO_AVAIL_BSS = 0x80410107, - // 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, - // pspnet_poeclient ERROR_NET_POECLIENT_INIT = 0x80410301, ERROR_NET_POECLIENT_NO_PADO = 0x80410302, @@ -90,31 +70,6 @@ enum { ERROR_NET_POECLIENT_TERMINATE = 0x8041030a, ERROR_NET_POECLIENT_NOT_STARTED = 0x8041030b, - // pspnet_resolver - ERROR_NET_RESOLVER_NOT_TERMINATED = 0x80410401, - ERROR_NET_RESOLVER_NO_DNS_SERVER = 0x80410402, - ERROR_NET_RESOLVER_INVALID_PTR = 0x80410403, - ERROR_NET_RESOLVER_INVALID_BUFLEN = 0x80410404, - ERROR_NET_RESOLVER_INVALID_ID = 0x80410405, - ERROR_NET_RESOLVER_ID_MAX = 0x80410406, - ERROR_NET_RESOLVER_NO_MEM = 0x80410407, - ERROR_NET_RESOLVER_BAD_ID = 0x80410408, // ERROR_NET_RESOLVER_ID_NOT_FOUND - ERROR_NET_RESOLVER_CTX_BUSY = 0x80410409, - ERROR_NET_RESOLVER_ALREADY_STOPPED = 0x8041040a, - ERROR_NET_RESOLVER_NOT_SUPPORTED = 0x8041040b, - ERROR_NET_RESOLVER_BUF_NO_SPACE = 0x8041040c, - ERROR_NET_RESOLVER_INVALID_PACKET = 0x8041040d, - ERROR_NET_RESOLVER_STOPPED = 0x8041040e, - ERROR_NET_RESOLVER_SOCKET = 0x8041040f, - ERROR_NET_RESOLVER_TIMEOUT = 0x80410410, - ERROR_NET_RESOLVER_NO_RECORD = 0x80410411, - ERROR_NET_RESOLVER_RES_PACKET_FORMAT = 0x80410412, - ERROR_NET_RESOLVER_RES_SERVER_FAILURE = 0x80410413, - ERROR_NET_RESOLVER_INVALID_HOST = 0x80410414, // ERROR_NET_RESOLVER_NO_HOST - ERROR_NET_RESOLVER_RES_NOT_IMPLEMENTED = 0x80410415, - ERROR_NET_RESOLVER_RES_SERVER_REFUSED = 0x80410416, - ERROR_NET_RESOLVER_INTERNAL = 0x80410417, - // pspnet_dhcp ERROR_NET_DHCP_INVALID_PACKET = 0x80410501, ERROR_NET_DHCP_NO_SERVER = 0x80410502, @@ -221,28 +176,6 @@ enum { #ifdef _MSC_VER #pragma pack(push,1) #endif -// Sockaddr -typedef struct SceNetInetSockaddr { - uint8_t sa_len; - uint8_t sa_family; - uint8_t sa_data[14]; -} 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]; -} PACK SceNetInetSockaddrIn; - -// 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; -} PACK SceNetInetPollfd; typedef struct ProductStruct { // Similar to SceNetAdhocctlAdhocId ? s32_le unknown; // Unknown, set to 0 // Product Type ? @@ -310,7 +243,7 @@ private: }; extern bool netInited; -extern bool netInetInited; +// extern bool netInetInited; extern bool netApctlInited; extern u32 netApctlState; extern SceNetApctlInfoInternal netApctlInfo; diff --git a/Core/HLE/sceNetApctl.cpp b/Core/HLE/sceNetApctl.cpp new file mode 100644 index 0000000000..acf79bda58 --- /dev/null +++ b/Core/HLE/sceNetApctl.cpp @@ -0,0 +1,3 @@ +// TODO: move apctl here + +#include "sceNetApctl.h" diff --git a/Core/HLE/sceNetApctl.h b/Core/HLE/sceNetApctl.h new file mode 100644 index 0000000000..e6b22d6505 --- /dev/null +++ b/Core/HLE/sceNetApctl.h @@ -0,0 +1,3 @@ +#pragma once + +// TODO: move sceNetApctl here \ No newline at end of file diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp new file mode 100644 index 0000000000..035cec0610 --- /dev/null +++ b/Core/HLE/sceNetInet.cpp @@ -0,0 +1,1396 @@ +// 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/ + +// 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 +#endif + +#include "Common/Net/Resolve.h" +#include "Common/Data/Text/Parsers.h" + +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Core/Config.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/MIPS/MIPS.h" +#include "Core/MemMapHelpers.h" + +#include "Core/HLE/proAdhoc.h" +#include "Core/HLE/sceNet.h" +#include "Core/HLE/sceNetInet.h" + +#include +#include + +#include "Core/HLE/sceNp.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)); + + struct FdSet { + fdMask mFdsBits[256 / gFdsBitsCount]; + }; + + static void Set(FdSet &sceFdSetBits, int socket) { + sceFdSetBits.mFdsBits[Position(socket)] |= ConvertToMask(socket); + } + + static bool IsSet(const FdSet &sceFdSetBits, int socket) { + return (sceFdSetBits.mFdsBits[Position(socket)] & ConvertToMask(socket)) != 0; + } + + static void Clear(FdSet &sceFdSetBits, int socket) { + sceFdSetBits.mFdsBits[Position(socket)] &= ~ConvertToMask(socket); + } + + static void Zero(FdSet &sceFdSetBits) { + memset(sceFdSetBits.mFdsBits, 0, sizeof(FdSet)); + } + +private: + static int Position(const int socket) { + return socket / gFdsBitsCount; + } + + static int ConvertToMask(const int socket) { + return static_cast(1UL << (socket % gFdsBitsCount)); + } +}; + +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; + } + + // 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; + } + const auto sceNetSockaddrIn = Memory::GetTypedPointerWriteRange(destAddrPtr, sceNetSocklen); + if (sceNetSockaddrIn == nullptr) { + return false; + } + DEBUG_LOG(Log::sceNet, "writeSockAddrInToSceSockAddr size: %d vs %d", (int)sizeof(SceNetInetSockaddrIn), sceNetSocklen); + if (sceNetSocklenPtr) { + *sceNetSocklenPtr = std::min(sceNetSocklen, sizeof(SceNetInetSockaddr)); + } + if (sceNetSocklen >= 1) { + sceNetSockaddrIn->sin_len = sceNetSocklen; + } + if (sceNetSocklen >= 2) { + sceNetSockaddrIn->sin_family = src.sin_family; + } + if (sceNetSocklen >= 4) { + sceNetSockaddrIn->sin_port = src.sin_port; + } + if (sceNetSocklen >= 8) { + sceNetSockaddrIn->sin_addr = src.sin_addr.s_addr; + } + 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 +} + +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); +} + +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"); + } + + // 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); + } + + 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); + } + + int nativeProtocol; + if (!SceNetInet::TranslateInetProtocolToNative(nativeProtocol, inetProtocol)) { + sceNetInet->SetLastError(EPROTONOSUPPORT); + return hleLogError(Log::sceNet, -1, "%s: Unable to translate inet protocol %i", __func__, inetProtocol); + } + + // 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)); + } + + // 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); + } + + // 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); + } + 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"); + } + + 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(); + + int nativeSocketLevel; + if (!SceNetInet::TranslateInetSocketLevelToNative(nativeSocketLevel, inetSocketLevel)) { + sceNetInet->SetLastError(EINVAL); + return hleLogError(Log::sceNet, -1, "[%i] %s: Unknown socket level %04x", nativeSocketId, __func__, inetSocketLevel); + } + + int nativeOptname; + if (!SceNetInet::TranslateInetOptnameToNativeOptname(nativeOptname, inetOptname)) { + sceNetInet->SetLastError(ENOPROTOOPT); + return hleLogError(Log::sceNet, ERROR_NET_INET_CONFIG_INVALID_ARG, "Unknown optname %04x", inetOptname); + } + + if (nativeOptname != inetOptname) { + DEBUG_LOG(Log::sceNet, "sceNetInetSetsockopt: Translated optname %04x into %04x", inetOptname, nativeOptname); + } + + 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); + } + + 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); + } + + 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); + } + } +} + +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(); + + int nativeSocketLevel; + if (!SceNetInet::TranslateInetSocketLevelToNative(nativeSocketLevel, inetSocketLevel)) { + sceNetInet->SetLastError(EINVAL); + return hleLogError(Log::sceNet, -1, "[%i] %s: Unknown socket level %04x", nativeSocketId, __func__, inetSocketLevel); + } + + int nativeOptname; + if (!SceNetInet::TranslateInetOptnameToNativeOptname(nativeOptname, inetOptname)) { + sceNetInet->SetLastError(ENOPROTOOPT); + return hleLogError(Log::sceNet, -1, "[%i] %s: Unknown optname %04x", nativeSocketId, __func__, inetOptname); + } + + if (nativeOptname != inetOptname) { + DEBUG_LOG(Log::sceNet, "sceNetInetSetsockopt: Translated optname %04x into %04x", inetOptname, nativeOptname); + } + + // 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); + } + + if (!Memory::IsValidAddress(optvalPtr)) { + sceNetInet->SetLastError(EFAULT); + return hleLogError(Log::sceNet, -1, "[%i]: %s: Invalid address %08x for optval", nativeSocketId, __func__, optvalPtr); + } + + 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); + } + } +} + +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 + int ret = connect(nativeSocketId, reinterpret_cast(&convertedSockaddr), sizeof(convertedSockaddr)); + if (ret < 0) { + const auto error = sceNetInet->SetLastErrorToMatchPlatform(); + return hleLogError(Log::sceNet, ret, "[%i] %s: Error connecting %i: %s", nativeSocketId, __func__, error, strerror(error)); + } + return hleLogSuccessI(Log::sceNet, ret); +} + +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)); + } + return hleLogSuccessI(Log::sceNet, ret); +} + +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 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? + } + free(fdtmp); + */ +//#endif + return eventCount; +} + +static int sceNetInetSelect(int maxfd, u32 readFdsPtr, u32 writeFdsPtr, u32 exceptFdsPtr, u32 timeoutPtr) { + WARN_LOG_ONCE(sceNetInetSelect, Log::sceNet, "UNTESTED 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__); + } + } + + // 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)); + } + + INFO_LOG(Log::sceNet, "%s: select() returned %i", __func__, ret); + return hleDelayResult(ret, "TODO: unhack", 500); +} + +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__); + } + return hleLogSuccessI(Log::sceNet, ret); +} + +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"); + } + + in_addr inAddr{}; +#if PPSSPP_PLATFORM(WINDOWS) + const int ret = inet_pton(AF_INET, hostname, &inAddr); +#else + const int ret = inet_aton(hostname, &inAddr); +#endif + if (ret != 0) { + sceNetInet->SetLastErrorToMatchPlatform(); + return inAddr.s_addr; + } + return hleLogSuccessI(Log::sceNet, ret); +} + +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); +#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); +} + +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); + } + + 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); +} + +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)); + } + + // 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); + } + + 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 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"); + } + + int nativeSocketId; + if (!sceNetInet->GetNativeSocketIdForInetSocketId(nativeSocketId, socket)) { + ERROR_LOG(Log::sceNet, "%s: Requested socket %i which does not exist", __func__, socket); + return -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); + } + + 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)); + } + + // 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); +} + +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"); + } + + 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); + } + + // 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)); + } + + // 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); +} + +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); + } + + 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); + } + + 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); +} + +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); + } + + 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); + } + + // 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)); + } + 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__); + } + INFO_LOG(Log::sceNet, "[%i] %s: Got %i bytes from recvfrom", nativeSocketId, __func__, ret); + } + return hleDelayResult(ret, "TODO: unhack", 500); +} + +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"); + } + + 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 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); + } + + // 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)); + } + 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"); + } + + 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 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; + } + + // 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; + } + + 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)); + + 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); + + if (ret < 0) { + const auto error = sceNetInet->SetLastErrorToMatchPlatform(); + WARN_LOG(Log::sceNet, "[%i] %s: Got error %i=%s", nativeSocketId, __func__, error, strerror(error)); + } + + return hleLogSuccessI(Log::sceNet, ret); +} + +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 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"); + } + + 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 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; + } + socklen_t socklen = sizeof(convertedSockaddr); + + // 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; + } + } + + // 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; + } + } + return hleLogSuccessI(Log::sceNet, ret); +} + +// 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 }, +}; + +// 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 }, + { INET_SO_NONBLOCK, INET_SO_NONBLOCK }, +}; + +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 +}; + +std::unordered_map SceNetInet::gNativeErrorCodeToInetErrorCode = { + { EINPROGRESS, INET_EINPROGRESS } +}; + +bool SceNetInet::Init() { + auto lock = std::unique_lock(gLock); + if (gInstance) { + return false; + } + gInstance = std::make_shared(); + return true; +} + +bool SceNetInet::Shutdown() { + auto lock = std::unique_lock(gLock); + if (!gInstance) { + return false; + } + gInstance->CloseAllRemainingSockets(); + gInstance = nullptr; + return true; +} + +bool SceNetInet::TranslateInetAddressFamilyToNative(int &destAddressFamily, int srcAddressFamily) { + const auto it = gInetAddressFamilyToNativeAddressFamily.find(static_cast(srcAddressFamily)); + if (it == gInetAddressFamilyToNativeAddressFamily.end()) { + return false; + } + destAddressFamily = it->second; + return true; +} + +bool SceNetInet::TranslateInetSocketLevelToNative(int &destSocketLevel, int srcSocketLevel) { + if (srcSocketLevel != PSP_NET_INET_SOL_SOCKET) { + return false; + } + destSocketLevel = SOL_SOCKET; + return true; +} + +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; +} + +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; +} + +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; +} + +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; + } + } + +#if !PPSSPP_PLATFORM(WINDOWS) + if (nonBlocking) { + nativeFlags |= MSG_DONTWAIT; + } +#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(); +#else + error = errno; +#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; + } + DEBUG_LOG(Log::sceNet, "%s: Translating input %i into %i", __func__, inetSocket, nativeSocketId); + FD_SET(nativeSocketId, &destFdSet); + } + } + + DEBUG_LOG(Log::sceNet, "%s: Translated %i sockets", __func__, setSize); + return true; +} + +void SceNetInet::CloseAllRemainingSockets() const { + for (const auto &[first, second] : mInetSocketIdToNativeSocket) { + if (!second) { + continue; + } + close(second->GetNativeSocketId()); + } +} + +void Register_sceNetInet() { + RegisterModule("sceNetInet", std::size(sceNetInet), sceNetInet); +} diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h new file mode 100644 index 0000000000..ea21af767d --- /dev/null +++ b/Core/HLE/sceNetInet.h @@ -0,0 +1,193 @@ +#pragma once + +#include "Core/HLE/HLE.h" +#include "Core/Net/InetSocket.h" + +#if PPSSPP_PLATFORM(WINDOWS) +#include +#endif + +#include +#include +#include + +// Sockaddr +typedef struct SceNetInetSockaddr { + uint8_t sa_len; + uint8_t sa_family; + uint8_t sa_data[14]; +} 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]; +} PACK SceNetInetSockaddrIn; + +// 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; +} 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, +}; + +enum PspInetAddressFamily { + PSP_NET_INET_AF_UNSPEC = 0, + PSP_NET_INET_AF_LOCAL = 1, + PSP_NET_INET_AF_INET = 2, +}; + +enum PspInetSocketLevel { + PSP_NET_INET_SOL_SOCKET = 0xFFFF, +}; + +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, +}; + +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, +}; + +// 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 +}; + +// TODO: INET_ +enum PspInetSocketOptionName { + // TODO: also specify minimum socket size + 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 +}; + +enum PspInetLimit { + PSP_NET_INET_SOMAXCONN = 128, +}; + +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 +}; + +enum InetErrorCode { + INET_EINPROGRESS = 119, +}; + +class SceNetInet { +public: + static bool Init(); + static bool Shutdown(); + static std::shared_ptr Get() { + return gInstance; + } + + // 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 Register_sceNetInet(); diff --git a/Core/HLE/sceNetResolver.cpp b/Core/HLE/sceNetResolver.cpp new file mode 100644 index 0000000000..970dafee58 --- /dev/null +++ b/Core/HLE/sceNetResolver.cpp @@ -0,0 +1,312 @@ +// 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/ + +// 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 +#endif + +// TODO: fixme move Core/Net to Common/Net +#include "Common/Net/Resolve.h" +#include "Core/Net/InetCommon.h" +#include "Common/Data/Text/Parsers.h" + +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Common/Serialize/SerializeMap.h" +#include "Core/Config.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceKernelMemory.h" +#include "Core/MIPS/MIPS.h" +#include "Core/Config.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/sceNetResolver.h" + +#include +#include + +#include "sceNet.h" +#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 +#define INADDR_NONE 0xFFFFFFFF +#endif + +static int sceNetResolverInit() { + ERROR_LOG(Log::sceNet, "UNTESTED %s()", __FUNCTION__); + g_Config.mHostToAlias["socomftb2.psp.online.scea.com"] = "67.222.156.250"; + g_Config.mHostToAlias["socompsp-prod.muis.pdonline.scea.com"] = "67.222.156.250"; + SceNetResolver::Init(); + return 0; +} + +static int sceNetResolverTerm() { + ERROR_LOG(Log::sceNet, "UNTESTED %s()", __FUNCTION__); + SceNetResolver::Shutdown(); + return 0; +} + +// Note: timeouts are in seconds +int NetResolver_StartNtoA(u32 resolverId, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) { + auto sceNetResolver = SceNetResolver::Get(); + if (!sceNetResolver) { + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_STOPPED, "Net Resolver Subsystem Shutdown: Resolver %i", + resolverId); + } + + const auto resolver = sceNetResolver->GetNetResolver(resolverId); + if (resolver == nullptr) { + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", resolverId); + } + + addrinfo* resolved = nullptr; + std::string err, hostname = std::string(safe_string(Memory::GetCharPointer(hostnamePtr))); + SockAddrIN4 addr{}; + addr.in.sin_addr.s_addr = INADDR_NONE; + + // Flag resolver as in-progress - not relevant for sync functions but potentially relevant for async + resolver->SetIsRunning(true); + + // Resolve any aliases + if (g_Config.mHostToAlias.find(hostname) != g_Config.mHostToAlias.end()) { + const std::string& alias = g_Config.mHostToAlias[hostname]; + INFO_LOG(Log::sceNet, "%s - Resolved alias %s from hostname %s", __FUNCTION__, alias.c_str(), hostname.c_str()); + hostname = alias; + } + + // Attempt to execute a DNS resolution + if (!net::DNSResolve(hostname, "", &resolved, err)) { + // TODO: Return an error based on the outputted "err" (unfortunately it's already converted to string) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_INVALID_HOST, "DNS Error Resolving %s (%s)\n", hostname.c_str(), + err.c_str()); + } + + // If successful, write to memory + if (resolved != nullptr) { + for (auto ptr = resolved; ptr != nullptr; ptr = ptr->ai_next) { + switch (ptr->ai_family) { + case AF_INET: + addr.in = *(sockaddr_in *) ptr->ai_addr; + break; + } + } + net::DNSResolveFree(resolved); + + Memory::Write_U32(addr.in.sin_addr.s_addr, inAddrPtr); + INFO_LOG(Log::sceNet, "%s - Hostname: %s => IPv4: %s", __FUNCTION__, hostname.c_str(), + ip2str(addr.in.sin_addr, false).c_str()); + } + + // Flag resolver as complete + resolver->SetIsRunning(false); + return 0; +} + +static int sceNetResolverStartNtoA(int resolverId, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) { + for (int attempt = 0; attempt < retry; ++attempt) { + if (const int status = NetResolver_StartNtoA(resolverId, hostnamePtr, inAddrPtr, timeout, retry); status >= 0) { + return status; + } + } + return -1; +} + +static int sceNetResolverStartNtoAAsync(int resolverId, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) { + ERROR_LOG_REPORT_ONCE(sceNetResolverStartNtoAAsync, Log::sceNet, "UNIMPL %s(%d, %08x, %08x, %d, %d) at %08x", + __FUNCTION__, resolverId, hostnamePtr, inAddrPtr, timeout, retry, currentMIPS->pc); + return NetResolver_StartNtoA(resolverId, hostnamePtr, inAddrPtr, timeout, retry); +} + +static int sceNetResolverPollAsync(int resolverId, u32 unknown) { + ERROR_LOG_REPORT_ONCE(sceNetResolverPollAsync, Log::sceNet, "UNIMPL %s(%d, %08x) at %08x", __FUNCTION__, resolverId, + unknown, currentMIPS->pc); + // TODO: Implement after confirming that this returns the state of resolver.isRunning + return 0; +} + +static int sceNetResolverWaitAsync(int resolverId, u32 unknown) { + ERROR_LOG_REPORT_ONCE(sceNetResolverWaitAsync, Log::sceNet, "UNIMPL %s(%d, %08x) at %08x", __FUNCTION__, resolverId, + unknown, currentMIPS->pc); + // TODO: Implement after confirming that this blocks current thread until resolver.isRunning flips to false + return 0; +} + +static int sceNetResolverStartAtoN(int resolverId, u32 inAddr, u32 hostnamePtr, int hostnameLength, int timeout, + int retry) { + ERROR_LOG_REPORT_ONCE(sceNetResolverStartAtoN, Log::sceNet, "UNIMPL %s(%d, %08x[%s], %08x, %i, %i, %i) at %08x", + __FUNCTION__, resolverId, inAddr, ip2str(*(in_addr*)&inAddr, false).c_str(), hostnamePtr, + hostnameLength, timeout, retry, currentMIPS->pc); + // TODO: Implement via getnameinfo + return 0; +} + +static int sceNetResolverStartAtoNAsync(int resolverId, u32 inAddr, u32 hostnamePtr, int hostnameLength, int timeout, + int retry) { + ERROR_LOG_REPORT_ONCE(sceNetResolverStartAtoNAsync, Log::sceNet, "UNIMPL %s(%d, %08x[%s], %08x, %i, %i, %i) at %08x", + __FUNCTION__, resolverId, inAddr, ip2str(*(in_addr*)&inAddr, false).c_str(), hostnamePtr, + hostnameLength, timeout, retry, currentMIPS->pc); + return 0; +} + +static int sceNetResolverCreate(u32 resolverIdPtr, u32 bufferPtr, int bufferLen) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%08x[%d], %08x, %d) at %08x", __FUNCTION__, resolverIdPtr, + Memory::Read_U32(resolverIdPtr), bufferPtr, bufferLen, currentMIPS->pc); + if (!Memory::IsValidRange(resolverIdPtr, 4)) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_INVALID_PTR, "Invalid Ptr: %08x", resolverIdPtr); + + if (Memory::IsValidRange(bufferPtr, 4) && bufferLen < 1) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_INVALID_BUFLEN, "Invalid Buffer Length: %i", bufferLen); + + auto sceNetResolver = SceNetResolver::Get(); + if (!sceNetResolver) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_STOPPED, "Resolver Subsystem Stopped"); + + const auto resolver = sceNetResolver->CreateNetResolver(bufferPtr, bufferLen); + + Memory::Write_U32(resolver->GetId(), resolverIdPtr); + return 0; +} + +static int sceNetResolverStop(u32 resolverId) { + auto sceNetResolver = SceNetResolver::Get(); + if (!sceNetResolver) { + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_STOPPED, "Resolver Subsystem Stopped (Resolver Id: %i)", + resolverId); + } + + const auto resolver = sceNetResolver->GetNetResolver(resolverId); + + WARN_LOG(Log::sceNet, "UNTESTED %s(%d) at %08x", __FUNCTION__, resolverId, currentMIPS->pc); + if (resolver == nullptr) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", resolverId); + + if (!resolver->GetIsRunning()) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_ALREADY_STOPPED, "Resolver Already Stopped (Id: %i)", resolverId); + + resolver->SetIsRunning(false); + return 0; +} + +static int sceNetResolverDelete(u32 resolverId) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%d) at %08x", __FUNCTION__, resolverId, currentMIPS->pc); + + auto sceNetResolver = SceNetResolver::Get(); + if (!sceNetResolver) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_STOPPED, "Resolver Subsystem Stopped (Resolver Id: %i)", + resolverId); + + if (!sceNetResolver->DeleteNetResolver(resolverId)) + return hleLogError(Log::sceNet, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", resolverId); + + return 0; +} + +const HLEFunction sceNetResolver[] = { + {0X224C5F44, &WrapI_IUUII, "sceNetResolverStartNtoA", 'i', "ixxii"}, + {0X244172AF, &WrapI_UUI, "sceNetResolverCreate", 'i', "xxi"}, + {0X94523E09, &WrapI_U, "sceNetResolverDelete", 'i', "i"}, + {0XF3370E61, &WrapI_V, "sceNetResolverInit", 'i', ""}, + {0X808F6063, &WrapI_U, "sceNetResolverStop", 'i', "i"}, + {0X6138194A, &WrapI_V, "sceNetResolverTerm", 'i', ""}, + {0X629E2FB7, &WrapI_IUUIII, "sceNetResolverStartAtoN", 'i', "ixxiii"}, + {0X14C17EF9, &WrapI_IUUII, "sceNetResolverStartNtoAAsync", 'i', "ixxii"}, + {0XAAC09184, &WrapI_IUUIII, "sceNetResolverStartAtoNAsync", 'i', "ixxiii"}, + {0X12748EB9, &WrapI_IU, "sceNetResolverWaitAsync", 'i', "ix"}, + {0X4EE99358, &WrapI_IU, "sceNetResolverPollAsync", 'i', "ix"}, +}; + +std::shared_ptr SceNetResolver::gInstance; +std::shared_mutex SceNetResolver::gLock; + +void SceNetResolver::Init() { + auto lock = std::unique_lock(gLock); + gInstance = std::make_shared(); +} + +void SceNetResolver::Shutdown() { + auto lock = std::unique_lock(gLock); + gInstance = nullptr; +} + +std::shared_ptr SceNetResolver::Get() { + auto lock = std::shared_lock(gLock); + return gInstance; +} + +std::shared_ptr SceNetResolver::GetNetResolver(u32 resolverId) { + std::shared_lock lock(mNetResolversLock); + const auto it = mNetResolvers.find(resolverId); + return it != mNetResolvers.end() ? it->second : nullptr; +} + +std::shared_ptr SceNetResolver::CreateNetResolver(u32 bufferPtr, u32 bufferLen) { + // TODO: Consider using SceUidManager instead of this 1-indexed id + // TODO: Implement ERROR_NET_RESOLVER_ID_MAX (possibly 32?) + std::unique_lock lock(mNetResolversLock); + int currentNetResolverId = mCurrentNetResolverId++; + return mNetResolvers[currentNetResolverId] = std::make_shared( + currentNetResolverId, // id + bufferPtr, // bufferPtr + bufferLen // bufferLen + ); +} + +bool SceNetResolver::TerminateNetResolver(u32 resolverId) { + const auto netResolver = GetNetResolver(resolverId); + if (netResolver == nullptr) { + return false; + } + netResolver->SetIsRunning(false); + return true; +} + +bool SceNetResolver::DeleteNetResolver(u32 resolverId) { + std::unique_lock lock(mNetResolversLock); + const auto it = mNetResolvers.find(resolverId); + if (it == mNetResolvers.end()) { + return false; + } + mNetResolvers.erase(it); + return true; +} + +void SceNetResolver::ClearNetResolvers() { + std::unique_lock lock(mNetResolversLock); + for (auto &[first, second]: mNetResolvers) { + second->SetIsRunning(false); + } + mNetResolvers.clear(); +} + +void Register_sceNetResolver() { + RegisterModule("sceNetResolver", ARRAY_SIZE(sceNetResolver), sceNetResolver); +} diff --git a/Core/HLE/sceNetResolver.h b/Core/HLE/sceNetResolver.h new file mode 100644 index 0000000000..5c32ab2adb --- /dev/null +++ b/Core/HLE/sceNetResolver.h @@ -0,0 +1,74 @@ +// 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/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once +#include +#include + +#include "Core/Net/NetResolver.h" + +class SceNetResolver; + +enum { + // pspnet_resolver + ERROR_NET_RESOLVER_NOT_TERMINATED = 0x80410401, + ERROR_NET_RESOLVER_NO_DNS_SERVER = 0x80410402, + ERROR_NET_RESOLVER_INVALID_PTR = 0x80410403, + ERROR_NET_RESOLVER_INVALID_BUFLEN = 0x80410404, + ERROR_NET_RESOLVER_INVALID_ID = 0x80410405, + ERROR_NET_RESOLVER_ID_MAX = 0x80410406, + ERROR_NET_RESOLVER_NO_MEM = 0x80410407, + ERROR_NET_RESOLVER_BAD_ID = 0x80410408, // ERROR_NET_RESOLVER_ID_NOT_FOUND + ERROR_NET_RESOLVER_CTX_BUSY = 0x80410409, + ERROR_NET_RESOLVER_ALREADY_STOPPED = 0x8041040a, + ERROR_NET_RESOLVER_NOT_SUPPORTED = 0x8041040b, + ERROR_NET_RESOLVER_BUF_NO_SPACE = 0x8041040c, + ERROR_NET_RESOLVER_INVALID_PACKET = 0x8041040d, + ERROR_NET_RESOLVER_STOPPED = 0x8041040e, + ERROR_NET_RESOLVER_SOCKET = 0x8041040f, + ERROR_NET_RESOLVER_TIMEOUT = 0x80410410, + ERROR_NET_RESOLVER_NO_RECORD = 0x80410411, + ERROR_NET_RESOLVER_RES_PACKET_FORMAT = 0x80410412, + ERROR_NET_RESOLVER_RES_SERVER_FAILURE = 0x80410413, + ERROR_NET_RESOLVER_INVALID_HOST = 0x80410414, // ERROR_NET_RESOLVER_NO_HOST + ERROR_NET_RESOLVER_RES_NOT_IMPLEMENTED = 0x80410415, + ERROR_NET_RESOLVER_RES_SERVER_REFUSED = 0x80410416, + ERROR_NET_RESOLVER_INTERNAL = 0x80410417, +}; + +class SceNetResolver { +public: + static void Init(); + static void Shutdown(); + static std::shared_ptr Get(); + + std::shared_ptr GetNetResolver(u32 resolverId); + std::shared_ptr CreateNetResolver(u32 bufferPtr, u32 bufferLen); + bool TerminateNetResolver(u32 resolverId); + bool DeleteNetResolver(u32 resolverId); + void ClearNetResolvers(); + +private: + static std::shared_ptr gInstance; + static std::shared_mutex gLock; + + int mCurrentNetResolverId = 1; + std::unordered_map> mNetResolvers; + std::shared_mutex mNetResolversLock; +}; + +void Register_sceNetResolver(); \ No newline at end of file diff --git a/Core/MemMap.h b/Core/MemMap.h index a22a68b973..03a77bc954 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -248,7 +248,16 @@ u8* GetPointerWrite(const u32 address); const u8* GetPointer(const u32 address); u8 *GetPointerWriteRange(const u32 address, const u32 size); +template +T* GetTypedPointerWriteRange(const u32 address, const u32 size) { + return reinterpret_cast(GetPointerWriteRange(address, size)); +} + const u8 *GetPointerRange(const u32 address, const u32 size); +template +const T* GetTypedPointerRange(const u32 address, const u32 size) { + return reinterpret_cast(GetPointerRange(address, size)); +} bool IsRAMAddress(const u32 address); inline bool IsVRAMAddress(const u32 address) { diff --git a/Core/Net/InetCommon.cpp b/Core/Net/InetCommon.cpp new file mode 100644 index 0000000000..29909fd04e --- /dev/null +++ b/Core/Net/InetCommon.cpp @@ -0,0 +1,54 @@ +// TODO: license + +#if __linux__ || __APPLE__ || defined(__OpenBSD__) +#include +#include +#include +#include +#endif + +// TODO: fixme move Core/Net to Common/Net +#include "Common/Net/Resolve.h" +#include "Core/Net/InetCommon.h" +#include "Common/Data/Text/Parsers.h" + +#include "Common/Serialize/Serializer.h" +#include "Core/Config.h" +#include "Core/HLE/HLE.h" +#include "Core/MemMapHelpers.h" + +#include "Core/HLE/proAdhoc.h" + +#include +#include + +#include "Core/HLE/sceNp.h" + +#if PPSSPP_PLATFORM(WINDOWS) +#define close closesocket +#endif + +bool getDefaultOutboundSockaddr(sockaddr_in& destSockaddrIn, socklen_t& destSocklen) { + auto fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + ERROR_LOG(Log::sceNet, "getSockAddrFromDefaultSocket: Failed to open socket (%s)", strerror(errno)); + return false; + } + sockaddr_in connectingTo; + memset(&connectingTo, 0, sizeof(connectingTo)); + connectingTo.sin_family = AF_INET; + connectingTo.sin_port = htons(53); + connectingTo.sin_addr.s_addr = 0x08080808; + if (connect(fd, (sockaddr*) &connectingTo, sizeof(connectingTo)) < 0) { + ERROR_LOG(Log::sceNet, "getSockAddrFromDefaultSocket: Failed to connect to Google (%s)", strerror(errno)); + close(fd); + return false; + } + if (getsockname(fd, (sockaddr*) &destSockaddrIn, &destSocklen) < 0) { + ERROR_LOG(Log::sceNet, "getSockAddrFromDefaultSocket: Failed to execute getsockname (%s)", strerror(errno)); + close(fd); + return false; + } + close(fd); + return true; +} diff --git a/Core/Net/InetCommon.h b/Core/Net/InetCommon.h new file mode 100644 index 0000000000..647bf8f345 --- /dev/null +++ b/Core/Net/InetCommon.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/HLE/HLE.h" + +#if PPSSPP_PLATFORM(WINDOWS) +#include +#include +#else +#include +#endif + +bool getDefaultOutboundSockaddr(sockaddr_in& destSockaddrIn, socklen_t& destSocklen); diff --git a/Core/Net/InetSocket.cpp b/Core/Net/InetSocket.cpp new file mode 100644 index 0000000000..945ecd077e --- /dev/null +++ b/Core/Net/InetSocket.cpp @@ -0,0 +1,3 @@ +// TODO: remove me if the functions remain inlined + +#include "InetSocket.h" diff --git a/Core/Net/InetSocket.h b/Core/Net/InetSocket.h new file mode 100644 index 0000000000..25a36b2e87 --- /dev/null +++ b/Core/Net/InetSocket.h @@ -0,0 +1,37 @@ +#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/Core/Net/NetResolver.cpp b/Core/Net/NetResolver.cpp new file mode 100644 index 0000000000..ec9b79e6a9 --- /dev/null +++ b/Core/Net/NetResolver.cpp @@ -0,0 +1,3 @@ +#include "NetResolver.h" + +// TODO: remove me if the functions remain inlined \ No newline at end of file diff --git a/Core/Net/NetResolver.h b/Core/Net/NetResolver.h new file mode 100644 index 0000000000..effa453c90 --- /dev/null +++ b/Core/Net/NetResolver.h @@ -0,0 +1,32 @@ +#pragma once + +#include "CommonTypes.h" + +class NetResolver { +public: + NetResolver(const NetResolver& other) = default; + + NetResolver() : + mId(0), + mIsRunning(false), + mBufferAddr(0), + mBufferLen(0) {} + + NetResolver(const int id, const u32 bufferAddr, const int bufferLen) : + mId(id), + mIsRunning(false), + mBufferAddr(bufferAddr), + mBufferLen(bufferLen) {} + + int GetId() const { return mId; } + + bool GetIsRunning() const { return mIsRunning; } + + void SetIsRunning(const bool isRunning) { this->mIsRunning = isRunning; } + +private: + int mId; + bool mIsRunning; + u32 mBufferAddr; + u32 mBufferLen; +}; diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 36d298c658..4d60951372 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -177,6 +177,9 @@ + + + @@ -314,6 +317,9 @@ + + + @@ -433,6 +439,9 @@ + + + @@ -608,6 +617,9 @@ + + + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index d9483b71af..e6172e3d0f 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -86,6 +86,9 @@ {1ad7ee67-c496-4709-bf98-cad322fe5d97} + + {4341253d-564e-490a-9780-179786e7ab2a} + @@ -1211,6 +1214,24 @@ Dialog + + HLE + + + HLE + + + HLE + + + Net + + + Net + + + Net + @@ -1909,6 +1930,24 @@ Dialog + + HLE + + + HLE + + + HLE + + + Net + + + Net + + + Net + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index a363969cf6..6d93211ad5 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -510,6 +510,9 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/MIPS/IR/IRNativeCommon.cpp \ $(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 \ $(SRC)/GPU/GPUCommon.cpp \ @@ -709,6 +712,9 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/proAdhocServer.cpp \ $(SRC)/Core/HLE/sceNetAdhoc.cpp \ $(SRC)/Core/HLE/sceNetAdhocMatching.cpp \ + $(SRC)/Core/HLE/sceNetApctl.cpp \ + $(SRC)/Core/HLE/sceNetInet.cpp \ + $(SRC)/Core/HLE/sceNetResolver.cpp \ $(SRC)/Core/HLE/sceOpenPSID.cpp \ $(SRC)/Core/HLE/sceP3da.cpp \ $(SRC)/Core/HLE/sceMt19937.cpp \ diff --git a/ext/rcheevos b/ext/rcheevos index 32917bdddf..3a91a58605 160000 --- a/ext/rcheevos +++ b/ext/rcheevos @@ -1 +1 @@ -Subproject commit 32917bdddf4982e62047862c6633e7671aaaf2cb +Subproject commit 3a91a58605c0fb05833a228dbb674357b0e65a09 diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 7826895f91..008747f162 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -739,6 +739,9 @@ SOURCES_CXX += \ $(COREDIR)/HLE/sceNet.cpp \ $(COREDIR)/HLE/sceNetAdhoc.cpp \ $(COREDIR)/HLE/sceNetAdhocMatching.cpp \ + $(COREDIR)/HLE/sceNetApctl.cpp \ + $(COREDIR)/HLE/sceNetInet.cpp \ + $(COREDIR)/HLE/sceNetResolver.cpp \ $(COREDIR)/HLE/proAdhocServer.cpp \ $(COREDIR)/HLE/proAdhoc.cpp \ $(COREDIR)/HLE/sceOpenPSID.cpp \ @@ -811,6 +814,9 @@ SOURCES_CXX += \ $(COREDIR)/MemFault.cpp \ $(COREDIR)/MemMap.cpp \ $(COREDIR)/MemMapFunctions.cpp \ + $(COREDIR)/Net/InetCommon.cpp \ + $(COREDIR)/Net/InetSocket.cpp \ + $(COREDIR)/Net/NetResolver.cpp \ $(COREDIR)/PSPLoaders.cpp \ $(COREDIR)/Replay.cpp \ $(COREDIR)/Reporting.cpp \