diff --git a/CMakeLists.txt b/CMakeLists.txt index 20a0b97f950f..17ac3f6a093b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,9 @@ option(USE_SYSTEM_MINIUPNPC "Dynamically link against system miniUPnPc" ${USE_SY option(USE_ASAN "Use address sanitizer" OFF) option(USE_UBSAN "Use undefined behaviour sanitizer" OFF) +# TODO: stop hardcoding this before PR +# set(USING_QT_UI ON) + if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN) if(USING_X11_VULKAN) message("Using X11 for Vulkan") @@ -2128,6 +2131,12 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceNet.h Core/HLE/sceNetAdhoc.cpp Core/HLE/sceNetAdhoc.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 @@ -2248,6 +2257,12 @@ add_library(${CoreLibName} ${CoreLinkType} Core/MIPS/MIPSVFPUFallbacks.h Core/MIPS/MIPSAsm.cpp Core/MIPS/MIPSAsm.h + Core/Net/InetCommon.cpp + Core/Net/InetCommon.h + Core/Net/NetResolver.cpp + Core/Net/NetResolver.h + Core/Net/SceSocket.cpp + Core/Net/SceSocket.h Core/MemFault.cpp Core/MemFault.h Core/MemMap.cpp @@ -2337,6 +2352,11 @@ include_directories(ext/libchdr/include) target_link_libraries(${CoreLibName} Common native chdr kirk cityhash sfmt19937 xbrz xxhash rcheevos ${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 e948ff82234f..ef973f38cea5 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1198,6 +1198,10 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { mPostShaderSetting[it.first] = std::stof(it.second); } + 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()) { @@ -1323,6 +1327,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 19ead994ed97..28276d834214 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -429,6 +429,7 @@ struct Config { // Networking std::string proAdhocServer; bool bEnableWlan; + std::map mHostToAlias; // TODO: mPostShaderSetting bool bEnableAdhocServer; bool bEnableUPnP; bool bUPnPUseOriginalPort; @@ -453,6 +454,7 @@ struct Config { int iFirmwareVersion; bool bBypassOSKWithKeyboard; + // Virtual reality bool bEnableVR; bool bEnable6DoF; diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index d745818338d5..a8ab014f9fc0 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); diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 0e0999c0f91c..c2adc46ea420 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -79,6 +79,8 @@ #include "sceSfmt19937.h" #include "sceG729.h" #include "KUBridge.h" +#include "sceNetInet.h" +#include "sceNetResolver.h" #define N(s) s @@ -236,6 +238,8 @@ void RegisterAllModules() { Register_sceFont(); Register_sceNet(); Register_sceNetAdhoc(); + Register_sceNetInet(); + Register_sceNetResolver(); Register_sceRtc(); Register_sceWlanDrv(); Register_sceMpeg(); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 49ab5c8e4806..13b861fb933a 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -55,6 +55,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 2e256fffbbc6..13606fb23515 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -20,14 +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 +50,12 @@ #include "Core/HLE/proAdhoc.h" #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" + +#include +#include + +#include "sceNetInet.h" +#include "sceNetResolver.h" #include "Core/HLE/sceNp.h" #include "Core/Reporting.h" #include "Core/Instance.h" @@ -54,7 +66,6 @@ #endif bool netInited; -bool netInetInited; u32 netDropRate = 0; u32 netDropDuration = 0; @@ -131,6 +142,26 @@ 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; + } +#else + return true; +#endif +} + +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(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(); } } @@ -794,21 +832,6 @@ static int sceNetGetMallocStat(u32 statPtr) { return 0; } -static int sceNetInetInit() { - ERROR_LOG(SCENET, "UNIMPL sceNetInetInit()"); - if (netInetInited) return ERROR_NET_INET_ALREADY_INITIALIZED; - netInetInited = true; - - return 0; -} - -int sceNetInetTerm() { - ERROR_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? @@ -822,7 +845,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); @@ -983,7 +1007,8 @@ static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { case PSP_NET_APCTL_INFO_USE_PROXY: if (!Memory::IsValidRange(pInfoAddr, 4)) return hleLogError(SCENET, -1, "apctl invalid arg"); - Memory::WriteUnchecked_U32(netApctlInfo.useProxy, pInfoAddr); + // TODO: fixme + Memory::WriteUnchecked_U32(1, pInfoAddr); NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_PROXY_URL: @@ -1083,107 +1108,6 @@ static int sceNetApctlDelHandler(u32 handlerID) { return NetApctl_DelHandler(handlerID); } -static int sceNetInetInetAton(const char *hostname, u32 addrPtr) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetInetAton(%s, %08x)", hostname, addrPtr); - return -1; -} - -int sceNetInetPoll(void *fds, u32 nfds, int timeout) { // timeout in miliseconds - DEBUG_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(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(SCENET, "UNIMPL sceNetInetSend(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); - return -1; -} - -static int sceNetInetGetErrno() { - ERROR_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(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(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(SCENET, "UNIMPL sceNetInetConnect(%i, %08x, %i)", socket, sockAddrInternetPtr, addressLength); - return -1; -} - int sceNetApctlConnect(int connIndex) { WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, connIndex); // Is this connIndex is the index to the scanning's result data or sceNetApctlGetBSSDescIDListUser result? @@ -1373,12 +1297,6 @@ static int sceNetApctlGetBSSDescEntry2(int entryId, int infoId, u32 resultAddr) return NetApctl_GetBSSDescEntryUser(entryId, infoId, resultAddr); } -static int sceNetResolverInit() -{ - ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); - return 0; -} - static int sceNetApctlAddInternalHandler(u32 handlerPtr, u32 handlerArg) { ERROR_LOG(SCENET, "UNIMPL %s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg); // This seems to be a 2nd kind of handler @@ -1433,45 +1351,38 @@ static int sceNetApctl_lib2_C20A144C(int connIndex, u32 ps3MacAddressPtr) { } -static int sceNetUpnpInit(int unknown1,int unknown2) -{ +static int sceNetUpnpInit(int unknown1,int unknown2) { ERROR_LOG_REPORT_ONCE(sceNetUpnpInit, SCENET, "UNIMPLsceNetUpnpInit %d,%d",unknown1,unknown2); return 0; } -static int sceNetUpnpStart() -{ +static int sceNetUpnpStart() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpStart"); return 0; } -static int sceNetUpnpStop() -{ +static int sceNetUpnpStop() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpStop"); return 0; } -static int sceNetUpnpTerm() -{ +static int sceNetUpnpTerm() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpTerm"); return 0; } -static int sceNetUpnpGetNatInfo() -{ +static int sceNetUpnpGetNatInfo() { ERROR_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(SCENET, 0); } -static int sceNetSetDropRate(u32 dropRate, u32 dropDuration) -{ +static int sceNetSetDropRate(u32 dropRate, u32 dropDuration) { netDropRate = dropRate; netDropDuration = dropDuration; return hleLogSuccessInfoI(SCENET, 0); @@ -1488,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', "" }, @@ -1587,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 d34ab560dd2c..c502505cb8ad 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 @@ -64,19 +63,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 +76,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, @@ -310,7 +271,7 @@ class AfterApctlMipsCall : public PSPAction { }; 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 000000000000..acf79bda5872 --- /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 000000000000..e6b22d650587 --- /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 000000000000..c5e327fb7669 --- /dev/null +++ b/Core/HLE/sceNetInet.cpp @@ -0,0 +1,906 @@ +// 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/SceSocket.h" + +#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 + +struct SceTimeval { + u32 tv_sec; /* Seconds. */ + u32 tv_usec; /* Microseconds. */ +}; + +class SceFdSetOperations { +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 int getLastError() { +#if PPSSPP_PLATFORM(WINDOWS) + return WSAGetLastError(); +#else + return errno; +#endif +} + +static int sceNetInetInit() { + ERROR_LOG(SCENET, "UNTESTED sceNetInetInit()"); + return SceNetInet::Init() ? 0 : ERROR_NET_INET_ALREADY_INITIALIZED; +} + +int sceNetInetTerm() { + ERROR_LOG(SCENET, "UNTESTED sceNetInetTerm()"); + SceNetInet::Shutdown(); + return 0; +} + +static int sceNetInetInetAton(const char *hostname, u32 addrPtr) { + ERROR_LOG(SCENET, "UNTESTED sceNetInetInetAton(%s, %08x)", hostname, addrPtr); + if (!Memory::IsValidAddress(addrPtr)) { + ERROR_LOG(SCENET, "sceNetInetInetAton: Invalid addrPtr: %08x", addrPtr); + return -1; + } + + 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) + Memory::Write_U32(inAddr.s_addr, addrPtr); + return ret; +} + +static u32 sceNetInetInetAddr(const char *hostname) { + ERROR_LOG(SCENET, "UNTESTED sceNetInetInetAddr(%s)", hostname); + in_addr inAddr{}; + // TODO: de-dupe +#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) { + return inAddr.s_addr; + } + return ret; +} + +static bool sceSockaddrToNativeSocketAddr(sockaddr_in &dest, u32 sockAddrInternetPtr, size_t addressLength) { + const auto sceNetSockaddrIn = Memory::GetTypedPointerRange(sockAddrInternetPtr, addressLength); + if (sceNetSockaddrIn == nullptr || addressLength == 0) { + return false; + } + + memset(&dest, 0, sizeof(dest)); + dest.sin_family = sceNetSockaddrIn->sin_family; + dest.sin_port = sceNetSockaddrIn->sin_port; + dest.sin_addr.s_addr = sceNetSockaddrIn->sin_addr; + DEBUG_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(), sceNetSockaddrIn->sin_len); + return true; +} + +static bool writeSockAddrInToSceSockAddr(u32 destAddrPtr, u32 destAddrLenPtr, sockaddr_in src) { + const auto sceNetSocklen = reinterpret_cast(Memory::GetPointerWrite(destAddrLenPtr)); + if (sceNetSocklen == nullptr) { + return false; + } + const auto sceNetSockaddrIn = Memory::GetTypedPointerWriteRange(destAddrPtr, *sceNetSocklen); + if (sceNetSockaddrIn == nullptr) { + return false; + } + INFO_LOG(SCENET, "writeSockAddrInToSceSockAddr: %lu vs %i", sizeof(SceNetInetSockaddrIn), *sceNetSocklen); + *sceNetSocklen = std::min(*sceNetSocklen, sizeof(SceNetInetSockaddr)); + // TODO: re-evaluate len field + 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(fd, 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 sceNetInetGetsockname(int socket, u32 addrPtr, u32 addrLenPtr) { + ERROR_LOG(SCENET, "UNTESTED sceNetInetGetsockname(%i, %08x, %08x)", socket, addrPtr, addrLenPtr); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + int nativeSocketId; + if (!sceNetInet->GetNativeSocketIdForSceSocketId(nativeSocketId, socket)) { + ERROR_LOG(SCENET, "sceNetInetGetsockname: Requested socket %i which does not exist", socket); + return -1; + } + + sockaddr_in sockaddrIn{}; + socklen_t socklen = sizeof(sockaddr_in); + const int ret = getsockname(nativeSocketId, reinterpret_cast(&sockaddrIn), &socklen); + if (ret < 0) { + const auto error = getLastError(); + ERROR_LOG(SCENET, "[%i] sceNetInetGetsockname: Failed to execute getsockname %i: %s", nativeSocketId, error, strerror(error)); + return ret; + } + + if (!writeSockAddrInToSceSockAddr(addrPtr, addrLenPtr, sockaddrIn)) { + ERROR_LOG(SCENET, "[%i] sceNetInetGetsockname: Failed to write results of getsockname to SceNetInetSockaddrIn", nativeSocketId); + return -1; + } + return ret; +} + +static int sceNetInetGetErrno() { + ERROR_LOG_ONCE(sceNetInetGetErrno, SCENET, "UNTESTED sceNetInetGetErrno()"); + const auto error = getLastError(); + if (error != ERROR_WHEN_NONBLOCKING_CALL_OCCURS && error != 0) { + INFO_LOG(SCENET, "Requested sceNetInetGetErrno %i=%s", error, strerror(error)); + } + switch (error) { + case ETIMEDOUT: + return INET_ETIMEDOUT; + case EISCONN: + return INET_EISCONN; +#if PPSSPP_PLATFORM(WINDOWS) + case EINPROGRESS: + return INET_EAGAIN; +#else + case EINPROGRESS: + return INET_EINPROGRESS; +#endif + } + return error; //-1; +} + +int sceNetInetPoll(void *fds, u32 nfds, int timeout) { // timeout in miliseconds + DEBUG_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(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; +} + +bool SceNetInet::TranslateSceFdSetToNativeFdSet(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(SceFdSetOperations::FdSet)); + if (sceFdSet == nullptr) { + ERROR_LOG(SCENET, "%s: Invalid fdsPtr %08x", __func__, fdsPtr); + return false; + } + + int setSize = 0; + for (auto& it : mSceSocketIdToNativeSocket) { + const auto sceSocket = it.first; + const auto nativeSocketId = it.second->GetNativeSocketId(); + if (nativeSocketId + 1 > maxFd) { + maxFd = nativeSocketId + 1; + } + if (SceFdSetOperations::IsSet(sceFdSet, sceSocket)) { + if (++setSize > FD_SETSIZE) { + ERROR_LOG(SCENET, "%s: Encountered input FD_SET which is greater than max supported size %i", __func__, setSize); + return false; + } + DEBUG_LOG(SCENET, "%s: Translating input %i into %i", __func__, sceSocket, nativeSocketId); + FD_SET(nativeSocketId, &destFdSet); + } + } + + DEBUG_LOG(SCENET, "%s: Translated %i sockets", __func__, setSize); + return true; +} + +void SceNetInet::CloseAllRemainingSockets() const { + for (auto& it : mSceSocketIdToNativeSocket) { + if (!it.second) + continue; + close(it.second->GetNativeSocketId()); + } +} + +static int sceNetInetSelect(int maxfd, u32 readFdsPtr, u32 writeFdsPtr, u32 exceptFdsPtr, u32 timeoutPtr) { + WARN_LOG_ONCE(sceNetInetSelect, SCENET, "UNTESTED sceNetInetSelect(%i, %08x, %08x, %08x, %08x)", maxfd, readFdsPtr, writeFdsPtr, exceptFdsPtr, timeoutPtr); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + int recomputedMaxFd = 1; + fd_set readFds; + sceNetInet->TranslateSceFdSetToNativeFdSet(recomputedMaxFd, readFds, readFdsPtr); + fd_set writeFds; + sceNetInet->TranslateSceFdSetToNativeFdSet(recomputedMaxFd, writeFds, writeFdsPtr); + fd_set exceptFds; + sceNetInet->TranslateSceFdSetToNativeFdSet(recomputedMaxFd, exceptFds, exceptFdsPtr); + + timeval tv{}; + if (timeoutPtr != 0) { + const auto sceTimeval = Memory::GetTypedPointerRange(timeoutPtr, sizeof(SceTimeval)); + if (sceTimeval != nullptr) { + tv.tv_sec = sceTimeval->tv_sec; + tv.tv_usec = sceTimeval->tv_usec; + DEBUG_LOG(SCENET, "sceNetInetSelect: Timeout seconds=%lu, useconds=%lu", tv.tv_sec, tv.tv_usec); + } else { + WARN_LOG(SCENET, "sceNetInetSelect: Encountered invalid timeout value, continuing anyway"); + } + } + + // 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 = getLastError(); + ERROR_LOG(SCENET, "sceNetInetSelect: Received error from select() %i: %s", error, strerror(error)); + } + + INFO_LOG(SCENET, "sceNetInetSelect: select() returned %i", ret); + return hleDelayResult(ret, "TODO: unhack", 300); +} + +static int sceNetInetClose(int socket) { + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + WARN_LOG(SCENET, "sceNetInetClose: Attempting to close socket %i which does not exist", socket); + return -1; + } + + const int ret = close(sceSocket->GetNativeSocketId()); + if (!sceNetInet->EraseNativeSocket(socket)) { + ERROR_LOG(SCENET, "sceNetInetClose: Unable to clear mapping of sceSocketId->nativeSocketId, was there contention?"); + return -1; + } + + return ret; +} + +static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, int flags) { + WARN_LOG_ONCE(sceNetInetRecv, SCENET, "UNTESTED sceNetInetRecv(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + WARN_LOG(SCENET, "sceNetInetClose: Attempting to close socket %i which does not exist", socket); + return -1; + } + + const auto dstBuf = Memory::GetTypedPointerWriteRange(bufPtr, bufLen); + if (dstBuf == nullptr) { + return hleLogError(SCENET, ERROR_NET_INET_INVALID_ARG, "sceNetInetRecv: Invalid pointer %08x (size %i)", bufPtr, bufLen); + } + +#if !PPSSPP_PLATFORM(WINDOWS) + if (sceSocket->IsNonBlocking()) { + flags |= MSG_DONTWAIT; + } +#endif + + const int ret = recv(sceSocket->GetNativeSocketId(), dstBuf, bufLen, flags); + if (ret < 0) { + if (const auto error = getLastError(); error != ERROR_WHEN_NONBLOCKING_CALL_OCCURS) { + ERROR_LOG(SCENET, "[%i]: %s: recv() encountered error %i: %s", socket, __func__, error, strerror(error)); + } + } + return ret; +} + +static int sceNetInetRecvfrom(int socket, u32 bufPtr, u32 bufLen, int flags, u32 fromAddr, u32 fromLenAddr) { + WARN_LOG_ONCE(sceNetInetRecvFrom, SCENET, "UNTESTED sceNetInetRecvfrom(%i, %08x, %i, %08x, %08x, %08x)", socket, bufPtr, bufLen, flags, fromAddr, fromLenAddr); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + ERROR_LOG(SCENET, "sceNetInetRecvfrom: Attempting to operate on unmapped socket %i", socket); + return -1; + } + +#if !PPSSPP_PLATFORM(WINDOWS) + if (sceSocket->IsNonBlocking()) { + flags |= MSG_DONTWAIT; + } +#endif + + DEBUG_LOG(SCENET, "sceNetInetRecvfrom(%i, %08x, %i, %08x, %08x, %08x)", socket, bufPtr, bufLen, flags, fromAddr, fromLenAddr); + + const auto nativeSocketId = sceSocket->GetNativeSocketId(); + sockaddr_in sockaddrIn{}; + socklen_t socklen = sizeof(sockaddr_in); + const auto dstBuf = Memory::GetTypedPointerWriteRange(bufPtr, bufLen); + if (dstBuf == nullptr) { + ERROR_LOG(SCENET, "[%i] sceNetInetRecvfrom: Invalid pointer range: %08x (size %i)", socket, bufPtr, bufLen); + return -1; + } + + Memory::Memset(bufPtr, 0, bufLen, "sceNetInetRecvfrom"); + const int ret = recvfrom(nativeSocketId, dstBuf, bufLen, flags, reinterpret_cast(&sockaddrIn), &socklen); + + if (ret < 0) { + const auto error = getLastError(); + // TODO: winsockify + if (error != 0 && error != ERROR_WHEN_NONBLOCKING_CALL_OCCURS) { + WARN_LOG(SCENET, "[%i] sceNetInetRecvfrom: Received error %i: %s", nativeSocketId, error, strerror(error)); + } + return hleDelayResult(ret, "TODO: unhack", 160); + } + + if (ret > 0) { + if (!writeSockAddrInToSceSockAddr(fromAddr, fromLenAddr, sockaddrIn)) { + ERROR_LOG(SCENET, "[%i] sceNetInetRecvfrom: Error writing native sockaddr to sceSockaddr", nativeSocketId); + } + INFO_LOG(SCENET, "[%i] sceNetInetRecvfrom: Got %i bytes from recvfrom", nativeSocketId, ret); + } + return hleDelayResult(ret, "TODO: unhack", 160); +} + +static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { + WARN_LOG_ONCE(sceNetInetSend, SCENET, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + ERROR_LOG(SCENET, "%s: Attempting to operate on unmapped socket %i", __func__, socket); + return -1; + } + +#if !PPSSPP_PLATFORM(WINDOWS) + if (sceSocket->IsNonBlocking()) { + flags |= MSG_DONTWAIT; + } +#endif + + const auto resolvedPtr = Memory::GetTypedPointerRange(bufPtr, bufLen); + if (resolvedPtr == nullptr) { + ERROR_LOG(SCENET, "[%i] %s: Invalid pointer range: %08x (size %i)", socket, __func__, bufPtr, bufLen); + return -1; + } + + const int ret = send(sceSocket->GetNativeSocketId(), resolvedPtr, bufLen, flags); + if (ret < 0) { + const auto error = getLastError(); + ERROR_LOG(SCENET, "[%i]: %s: send() encountered error %i: %s", socket, __func__, error, strerror(error)); + } + + return ret; +} + +static int sceNetInetSendto(int socket, u32 bufPtr, u32 bufLen, u32 flags, u32 toAddr, u32 toLen) { + ERROR_LOG_ONCE(sceNetInetSendto, SCENET, "UNTESTED sceNetInetSendto(%i, %08x, %i, %08x, %08x, %i)", socket, bufPtr, bufLen, flags, toAddr, toLen); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + ERROR_LOG(SCENET, "sceNetInetSendto: Attempting to operate on unmapped socket %i", socket); + return -1; + } + +#if PPSSPP_PLATFORM(LINUX) + if (sceSocket->IsNonBlocking()) { + flags |= MSG_DONTWAIT; + } +#endif + + const auto nativeSocketId = sceSocket->GetNativeSocketId(); + + // TODO: validatd socket + // TODO: validated ptr + const auto srcBuf = Memory::GetTypedPointerRange(bufPtr, bufLen); + if (srcBuf == nullptr) { + ERROR_LOG(SCENET, "[%i] sceNetInetSendto: Invalid pointer range: %08x (size %i)", socket, bufPtr, bufLen); + return -1; + } + + sockaddr_in convertedSockAddr{}; + if (!sceSockaddrToNativeSocketAddr(convertedSockAddr, toAddr, toLen)) { + ERROR_LOG(SCENET, "[%i] sceNetInetSendto: Unable to translate sceSockAddr to native sockaddr", nativeSocketId); + return -1; + } + + DEBUG_LOG(SCENET, "[%i] sceNetInetSendto: Writing %i bytes to %s on port %i", nativeSocketId, bufLen, ip2str(convertedSockAddr.sin_addr, false).c_str(), ntohs(convertedSockAddr.sin_port)); + + const int ret = sendto(nativeSocketId, srcBuf, bufLen, flags, reinterpret_cast(&convertedSockAddr), sizeof(sockaddr_in)); + DEBUG_LOG(SCENET, "[%i] sceNetInetSendto: sendto returned %i", nativeSocketId, ret); + + if (ret < 0) { + const auto error = getLastError(); + WARN_LOG(SCENET, "[%i] sceNetInetSendto: Got error %i=%s", nativeSocketId, error, strerror(error)); + } + + return ret; +} + +static int sceNetInetBind(int socket, u32 addrPtr, u32 addrLen) { + WARN_LOG_ONCE(sceNetInetSend, SCENET, "UNTESTED sceNetInetBind(%i, %08x, %08x)", socket, addrPtr, addrLen); + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + int nativeSocketId; + if (!sceNetInet->GetNativeSocketIdForSceSocketId(nativeSocketId, socket)) { + ERROR_LOG(SCENET, "sceNetInetBind: Attempting to operate on unmapped socket %i", socket); + return -1; + } + +#if PPSSPP_PLATFORM(LINUX) + // Set broadcast + // TODO: move broadcast SceSocket + int broadcastEnabled = 1; + int sockoptRet = setsockopt(nativeSocketId, SOL_SOCKET, SO_BROADCAST, &broadcastEnabled, sizeof(broadcastEnabled)); + + // Set reuseport / reuseaddr by default + // TODO: evaluate + int opt = 1; +#if defined(SO_REUSEPORT) + setsockopt(nativeSocketId, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); +#endif + setsockopt(nativeSocketId, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#elif PPSSPP_PLATFORM(WINDOWS) + // Set broadcast + // TODO: move broadcast SceSocket + int broadcastEnabled = 1; + int sockoptRet = setsockopt(nativeSocketId, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&broadcastEnabled), sizeof(broadcastEnabled)); + int opt = 1; + setsockopt(nativeSocketId, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&opt), sizeof(opt)); +#endif + + sockaddr_in convertedSockaddr{}; + if (!sceSockaddrToNativeSocketAddr(convertedSockaddr, addrPtr, addrLen)) { + ERROR_LOG(SCENET, "[%i] Error translating sceSockaddr to native sockaddr", nativeSocketId); + return -1; + } + socklen_t socklen = sizeof(convertedSockaddr); + if (!getDefaultOutboundSockaddr(convertedSockaddr, socklen)) { + WARN_LOG(SCENET, "Failed to get default bound address"); + return -1; + } + INFO_LOG(SCENET, "[%i] Binding to family %i, port %i, addr %s sockoptRet %i", nativeSocketId, convertedSockaddr.sin_family, ntohs(convertedSockaddr.sin_port), ip2str(convertedSockaddr.sin_addr, false).c_str(), sockoptRet); + const int ret = bind(nativeSocketId, reinterpret_cast(&convertedSockaddr), socklen); + INFO_LOG(SCENET, "Bind returned %i for fd=%i", ret, nativeSocketId); + return ret; +} + +static int sceNetInetSocket(int domain, int type, int protocol) { + ERROR_LOG(SCENET, "UNTESTED sceNetInetSocket(%i, %i, %i)", domain, type, protocol); + auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const int nativeSocketId = socket(domain, type, protocol); + const auto sceSocket = sceNetInet->CreateAndAssociateSceSocket(nativeSocketId); + + if (!sceSocket) { + close(nativeSocketId); + return hleLogError(SCENET, ERROR_NET_INET_INVALID_ARG, "%s: Unable to create new SceSocket for native socket id %i, closing"); + } + + return sceSocket->GetSceSocketId(); +} + +static int sceNetInetGetsockopt(int socket, int level, int inetOptname, u32 optvalPtr, u32 optlenPtr) { + WARN_LOG(SCENET, "UNTESTED sceNetInetGetsockopt(%i, %i, %i, %08x, %08x)", socket, level, inetOptname, optvalPtr, optlenPtr); + if (!isSockoptNameAllowed(inetOptname)) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Unknown optname %04x", inetOptname); + } + + const auto optname = translateOptname(static_cast(inetOptname)); + if (optname != inetOptname) { + DEBUG_LOG(SCENET, "sceNetInetSetsockopt: Translated optname %04x into %04x", inetOptname, optname); + } + + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + ERROR_LOG(SCENET, "sceNetInetGetsockopt: Attempting to operate on unmapped socket %i", socket); + return -1; + } + + // TODO: implement non-blocking + const auto nativeSocketId = sceSocket->GetNativeSocketId(); + +#if PPSSPP_PLATFORM(WINDOWS) + auto optlen = reinterpret_cast(Memory::GetPointerWrite(optlenPtr)); +#else + auto optlen = reinterpret_cast(Memory::GetPointerWrite(optlenPtr)); +#endif + if (optlen == nullptr) { + ERROR_LOG(SCENET, "[%i] sceNetInetGetsockopt: Invalid pointer %08x", nativeSocketId, optlenPtr); + return -1; + } + + const auto optval = Memory::GetTypedPointerWriteRange(optvalPtr, *optlen); + if (optval == nullptr) { + ERROR_LOG(SCENET, "[%i] sceNetInetGetsockopt: Invalid pointer range %08x (size %i)", nativeSocketId, optvalPtr, *optlen); + return -1; + } + + const int ret = getsockopt(nativeSocketId, SOL_SOCKET, optname, optval, optlen); + if (ret < 0) { + const auto error = getLastError(); + ERROR_LOG(SCENET, "[%i] sceNetInetGetsockopt returned error %i: %s", nativeSocketId, error, strerror(error)); + } + return ret; +} + +static int sceNetInetSetsockopt(int socket, int level, int inetOptname, u32 optvalPtr, int optlen) { + WARN_LOG_ONCE(sceNetInetSetsockopt, SCENET, "UNTESTED sceNetInetSetsockopt(%i, %i, %i, %08x, %i)", socket, level, inetOptname, optvalPtr, optlen); + if (!isSockoptNameAllowed(inetOptname)) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Unknown optname %04x", inetOptname); + } + + const auto optname = translateOptname(static_cast(inetOptname)); + if (optname != inetOptname) { + DEBUG_LOG(SCENET, "sceNetInetSetsockopt: Translated optname %04x into %04x", inetOptname, optname); + } + + const auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + } + + const auto sceSocket = sceNetInet->GetSceSocket(socket); + if (!sceSocket) { + ERROR_LOG(SCENET, "sceNetInetSetsockopt: Attempting to operate on unmapped socket %i", socket); + return -1; + } + + // If optlens of != sizeof(u32) are created, split out the handling into separate functions for readability + if (optlen != sizeof(u32)) { + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "[%i]: Unhandled optlen %i for optname %04x", sceSocket->GetNativeSocketId(), optlen, inetOptname); + } + + auto optval = Memory::Read_U32(optvalPtr); + const auto nativeSocketId = sceSocket->GetNativeSocketId(); + INFO_LOG(SCENET, "[%i] setsockopt_u32(%i, %i, %i, %i)", nativeSocketId, nativeSocketId, level, optname, optval); + + switch (optname) { + // Unmatched PSP functions - no direct equivalent + case INET_SO_NONBLOCK: { + const bool nonblocking = optval != 0; + sceSocket->SetNonBlocking(nonblocking); + INFO_LOG(SCENET, "[%i] setsockopt_u32: Set non-blocking=%i", nativeSocketId, nonblocking); + if (setBlockingMode(nativeSocketId, nonblocking) != 0) { + const auto error = getLastError(); + ERROR_LOG(SCENET, "[%i] Failed to set to non-blocking: %i: %s", nativeSocketId, error, strerror(error)); + } + return 0; + } + // Matched PSP functions - may be different optname constants (which would have been translated) but the handling the same or similar + case SO_BROADCAST: { + INFO_LOG(SCENET, "UNTESTED SCE_SO_BROADCAST sceNetInetSetsockopt(%i, %i, %i, %u, %i)", nativeSocketId, level, optname, optval, 4); + int ret = setsockopt(nativeSocketId, SOL_SOCKET, optname, reinterpret_cast(&optval), sizeof(optval)); + if (ret < 0) { + const auto error = getLastError(); + INFO_LOG(SCENET, "setsockopt_u32: Got error %i: %s on socket %i", error, strerror(error), nativeSocketId); + } else { + INFO_LOG(SCENET, "setsockopt_u32: setsockopt returned %i for %i", ret, nativeSocketId); + } + return 0; + } + default: { + INFO_LOG(SCENET, "UNTESTED sceNetInetSetsockopt(%i, %i, %i, %u, %i)", nativeSocketId, level, optname, optval, 4); + int ret = setsockopt(nativeSocketId, SOL_SOCKET, optname, reinterpret_cast(&optval), sizeof(optval)); + INFO_LOG(SCENET, "setsockopt_u32: setsockopt returned %i for %i", ret, nativeSocketId); + return ret; + } + } +} + +static int sceNetInetConnect(int socket, u32 sockAddrInternetPtr, int addressLength) { + ERROR_LOG(SCENET, "UNTESTED sceNetInetConnect(%i, %08x, %i, %i)", socket, sockAddrInternetPtr, Memory::Read_U32(sockAddrInternetPtr), addressLength); + auto sceNetInet = SceNetInet::Get(); + if (!sceNetInet) + return hleLogError(SCENET, ERROR_NET_INET_CONFIG_INVALID_ARG, "Inet Subsystem Not Running - Use sceNetInetInit"); + + int nativeSocket; + if (!sceNetInet->GetNativeSocketIdForSceSocketId(nativeSocket, socket)) { + ERROR_LOG(SCENET, "sceNetInetConnect: Attempting to operate on unmapped socket %i", socket); + return -1; + } + + sockaddr_in convertedSockaddr{}; + if (!sceSockaddrToNativeSocketAddr(convertedSockaddr, sockAddrInternetPtr, addressLength)) { + ERROR_LOG(SCENET, "[%i] sceNetInetConnect: Error translating sceSockaddr to native sockaddr", socket); + return -1; + } + + DEBUG_LOG(SCENET, "[%i] sceNetInetConnect: Connecting to %s on %i", nativeSocket, ip2str(convertedSockaddr.sin_addr, false).c_str(), ntohs(convertedSockaddr.sin_port)); + + int ret = connect(nativeSocket, reinterpret_cast(&convertedSockaddr), sizeof(convertedSockaddr)); + if (ret < 0) { + const auto error = getLastError(); + INFO_LOG(SCENET, "[%i] sceNetInetConnect: Encountered error %i: %s", nativeSocket, error, strerror(error)); + } + return ret; +} + +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, nullptr, "sceNetInetListen", '?', "" }, + {0XDB094E1B, nullptr, "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_IUUU, "sceNetInetSend", 'i', "ixxx" }, + {0X05038FC7, &WrapI_IUUUUU, "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, nullptr, "sceNetInetInetNtop", '?', "" }, + {0XE30B8C19, nullptr, "sceNetInetInetPton", '?', "" }, + {0X8CA3A97E, nullptr, "sceNetInetGetPspError", '?', "" }, + {0XE247B6D6, nullptr, "sceNetInetGetpeername", '?', "" }, + {0X162E6FD5, &WrapI_IUU, "sceNetInetGetsockname", '?', "" }, + {0X80A21ABD, nullptr, "sceNetInetSocketAbort", '?', "" }, + {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, + {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, +}; + +std::shared_ptr SceNetInet::gInstance; +std::shared_mutex SceNetInet::gLock; + +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; +} + +std::shared_ptr SceNetInet::CreateAndAssociateSceSocket(int nativeSocketId) { + auto lock = std::unique_lock(mLock); + + int sceSocketId = ++mCurrentSceSocketId; + if (const auto it = mSceSocketIdToNativeSocket.find(sceSocketId); it != mSceSocketIdToNativeSocket.end()) { + WARN_LOG(SCENET, "%s: Attempted to re-associate socket from already-associated sceSocketId: %i", __func__, sceSocketId); + return nullptr; + } + auto sceSocket = std::make_shared(sceSocketId, nativeSocketId); + mSceSocketIdToNativeSocket.emplace(sceSocketId, sceSocket); + return sceSocket; +} + +std::shared_ptr SceNetInet::GetSceSocket(int sceSocketId) { + auto lock = std::shared_lock(mLock); + + const auto it = mSceSocketIdToNativeSocket.find(sceSocketId); + if (it == mSceSocketIdToNativeSocket.end()) { + WARN_LOG(SCENET, "%s: Attempted to get unassociated socket from sceSocketId: %i", __func__, sceSocketId); + return nullptr; + } + + return it->second; +} + +bool SceNetInet::GetNativeSocketIdForSceSocketId(int& nativeSocketId, int sceSocketId) { + const auto sceSocket = GetSceSocket(sceSocketId); + if (!sceSocket) + return false; + nativeSocketId = sceSocket->GetNativeSocketId(); + return true; +} + +bool SceNetInet::EraseNativeSocket(int sceSocketId) { + auto lock = std::unique_lock(mLock); + + const auto it = mSceSocketIdToNativeSocket.find(sceSocketId); + if (it == mSceSocketIdToNativeSocket.end()) { + WARN_LOG(SCENET, "%s: Attempted to delete unassociated socket from sceSocketId: %i", __func__, sceSocketId); + return false; + } + mSceSocketIdToNativeSocket.erase(it); + return true; +} + +void Register_sceNetInet() { + RegisterModule("sceNetInet", ARRAY_SIZE(sceNetInet), sceNetInet); +} diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h new file mode 100644 index 000000000000..7e5a962afcc7 --- /dev/null +++ b/Core/HLE/sceNetInet.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Core/HLE/HLE.h" +#include "Core/Net/SceSocket.h" + +#if PPSSPP_PLATFORM(WINDOWS) +#include +#endif + +#include +#include +#include + + +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, +}; + +class SceNetInet { +public: + static bool Init(); + static bool Shutdown(); + static std::shared_ptr Get() { + return gInstance; + } + + // TODO: document + std::shared_ptr CreateAndAssociateSceSocket(int nativeSocketId); + std::shared_ptr GetSceSocket(int sceSocketId); + bool GetNativeSocketIdForSceSocketId(int &nativeSocketId, int sceSocketId); + bool EraseNativeSocket(int sceSocketId); + bool TranslateSceFdSetToNativeFdSet(int& maxFd, fd_set &destFdSet, u32 fdsPtr) const; + +private: + void CloseAllRemainingSockets() const; + + static std::shared_ptr gInstance; + static std::shared_mutex gLock; + + std::unordered_map> mSceSocketIdToNativeSocket; + int mCurrentSceSocketId = 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 000000000000..567162e6c5a0 --- /dev/null +++ b/Core/HLE/sceNetResolver.cpp @@ -0,0 +1,311 @@ +// 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/SceSocket.h" + +#if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE) +// Missing toolchain define +#define INADDR_NONE 0xFFFFFFFF +#endif + +static int sceNetResolverInit() { + ERROR_LOG(SCENET, "UNTESTED %s()", __FUNCTION__); + g_Config.mHostToAlias["socomftb2.psp.online.scea.com"] = "67.222.156.250"; + SceNetResolver::Init(); + return 0; +} + +static int sceNetResolverTerm() { + ERROR_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(SCENET, ERROR_NET_RESOLVER_STOPPED, "Net Resolver Subsystem Shutdown: Resolver %i", + resolverId); + } + + const auto resolver = sceNetResolver->GetNetResolver(resolverId); + if (resolver == nullptr) { + return hleLogError(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(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(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(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, 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, 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, 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, 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, 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(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(SCENET, ERROR_NET_RESOLVER_INVALID_PTR, "Invalid Ptr: %08x", resolverIdPtr); + + if (Memory::IsValidRange(bufferPtr, 4) && bufferLen < 1) + return hleLogError(SCENET, ERROR_NET_RESOLVER_INVALID_BUFLEN, "Invalid Buffer Length: %i", bufferLen); + + auto sceNetResolver = SceNetResolver::Get(); + if (!sceNetResolver) + return hleLogError(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(SCENET, ERROR_NET_RESOLVER_STOPPED, "Resolver Subsystem Stopped (Resolver Id: %i)", + resolverId); + } + + const auto resolver = sceNetResolver->GetNetResolver(resolverId); + + WARN_LOG(SCENET, "UNTESTED %s(%d) at %08x", __FUNCTION__, resolverId, currentMIPS->pc); + if (resolver == nullptr) + return hleLogError(SCENET, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", resolverId); + + if (!resolver->GetIsRunning()) + return hleLogError(SCENET, ERROR_NET_RESOLVER_ALREADY_STOPPED, "Resolver Already Stopped (Id: %i)", resolverId); + + resolver->SetIsRunning(false); + return 0; +} + +static int sceNetResolverDelete(u32 resolverId) { + WARN_LOG(SCENET, "UNTESTED %s(%d) at %08x", __FUNCTION__, resolverId, currentMIPS->pc); + + auto sceNetResolver = SceNetResolver::Get(); + if (!sceNetResolver) + return hleLogError(SCENET, ERROR_NET_RESOLVER_STOPPED, "Resolver Subsystem Stopped (Resolver Id: %i)", + resolverId); + + if (!sceNetResolver->DeleteNetResolver(resolverId)) + return hleLogError(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 000000000000..5c32ab2adbca --- /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 67e17babcd0e..34f3591c90e9 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 000000000000..5969240b1bd4 --- /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(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(SCENET, "getSockAddrFromDefaultSocket: Failed to connect to Google (%s)", strerror(errno)); + close(fd); + return false; + } + if (getsockname(fd, (sockaddr*) &destSockaddrIn, &destSocklen) < 0) { + ERROR_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 000000000000..647bf8f345c6 --- /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/NetResolver.cpp b/Core/Net/NetResolver.cpp new file mode 100644 index 000000000000..ec9b79e6a9fd --- /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 000000000000..effa453c90f0 --- /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/Core/Net/SceSocket.cpp b/Core/Net/SceSocket.cpp new file mode 100644 index 000000000000..c155401f3b8f --- /dev/null +++ b/Core/Net/SceSocket.cpp @@ -0,0 +1,3 @@ +// TODO: remove me if the functions remain inlined + +#include "SceSocket.h" diff --git a/Core/Net/SceSocket.h b/Core/Net/SceSocket.h new file mode 100644 index 000000000000..43766b43ee3d --- /dev/null +++ b/Core/Net/SceSocket.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include + +// TODO: INET_ +enum SocketOptionName { + // 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 +}; + +// TODO: Handle commented out options +static std::unordered_map 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 }, +}; + +// TODO: Move me +static bool isSockoptNameAllowed(int optname) { + return gInetSocketOptnameToNativeOptname.find(static_cast(optname)) != gInetSocketOptnameToNativeOptname.end(); +} + +static int translateOptname(SocketOptionName inetOptname) { + const auto it = gInetSocketOptnameToNativeOptname.find(inetOptname); + if (it == gInetSocketOptnameToNativeOptname.end()) { + return inetOptname; + } + return it->second; +} + +// TODO: document +class SceSocket { +public: + SceSocket(int sceSocketId, int nativeSocketId) : m_sceSocketId(sceSocketId), m_nativeSocketId(nativeSocketId) {} + + int GetSceSocketId() const { + return m_sceSocketId; + } + + int GetNativeSocketId() const { + return m_nativeSocketId; + } + + // TODO: get mask of options + + bool IsNonBlocking() const { + return m_nonBlocking; + } + + void SetNonBlocking(const bool nonBlocking) { + m_nonBlocking = nonBlocking; + } +private: + int m_sceSocketId; + int m_nativeSocketId; + bool m_nonBlocking = false; +};