mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-06 06:51:18 +12:00
2173 lines
57 KiB
C++
2173 lines
57 KiB
C++
#include "Cafe/OS/common/OSCommon.h"
|
|
#include "nsysnet.h"
|
|
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
|
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
|
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
|
|
|
#include "Common/socket.h"
|
|
|
|
#if BOOST_OS_UNIX
|
|
|
|
#define WSAEWOULDBLOCK EWOULDBLOCK
|
|
#define WSAEINPROGRESS EINPROGRESS
|
|
#define WSAESHUTDOWN ESHUTDOWN
|
|
#define WSAECONNABORTED ECONNABORTED
|
|
#define WSAHOST_NOT_FOUND EAI_NONAME
|
|
|
|
#define GETLASTERR errno
|
|
|
|
#endif // BOOST_OS_UNIX
|
|
|
|
#if BOOST_OS_WINDOWS
|
|
|
|
#define GETLASTERR WSAGetLastError()
|
|
|
|
#endif //BOOST_OS_WINDOWS
|
|
|
|
#define WU_AF_INET 2
|
|
|
|
#define WU_SOCK_STREAM 1
|
|
#define WU_SOCK_DGRAM 2
|
|
|
|
#define WU_IPPROTO_IP 0
|
|
#define WU_IPPROTO_TCP 6
|
|
#define WU_IPPROTO_UDP 17
|
|
|
|
#define WU_SO_REUSEADDR 0x0004
|
|
#define WU_SO_KEEPALIVE 0x0008
|
|
#define WU_SO_WINSCALE 0x0400
|
|
#define WU_SO_SNDBUF 0x1001
|
|
#define WU_SO_RCVBUF 0x1002
|
|
#define WU_SO_LASTERROR 0x1007
|
|
#define WU_SO_NBIO 0x1014
|
|
#define WU_SO_NONBLOCK 0x1016
|
|
|
|
#define WU_TCP_NODELAY 0x2004
|
|
|
|
#define WU_SOL_SOCKET -1 // this constant differs from Win32 socket API
|
|
|
|
#define WU_MSG_PEEK 0x02
|
|
#define WU_MSG_DONTWAIT 0x20
|
|
|
|
// error codes
|
|
#define WU_SO_SUCCESS 0x0000
|
|
#define WU_SO_EWOULDBLOCK 0x0006
|
|
#define WU_SO_ECONNRESET 0x0008
|
|
#define WU_SO_EINVAL 0x000B
|
|
#define WU_SO_EINPROGRESS 0x0016
|
|
|
|
#define WU_SO_ESHUTDOWN 0x000F
|
|
|
|
|
|
typedef signed int WUSOCKET;
|
|
|
|
bool sockLibReady = false;
|
|
|
|
void nsysnetExport_socket_lib_init(PPCInterpreter_t* hCPU)
|
|
{
|
|
sockLibReady = true;
|
|
#if BOOST_OS_WINDOWS
|
|
WSADATA wsa;
|
|
WSAStartup(MAKEWORD(2, 2), &wsa);
|
|
#endif // BOOST_OS_WINDOWS
|
|
osLib_returnFromFunction(hCPU, 0); // 0 -> Success
|
|
}
|
|
|
|
uint32* __gh_errno_ptr()
|
|
{
|
|
OSThread_t* osThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance());
|
|
return &osThread->context.error;
|
|
}
|
|
|
|
void _setSockError(sint32 errCode)
|
|
{
|
|
// todo -> Call __gh_errno_ptr and then write 32bit error code
|
|
*(uint32*)__gh_errno_ptr() = _swapEndianU32(errCode);
|
|
//coreinitData->ghsErrno = _swapEndianU32(errCode);
|
|
}
|
|
|
|
sint32 _getSockError()
|
|
{
|
|
return (sint32)_swapEndianU32(*(uint32*)__gh_errno_ptr());
|
|
//return (sint32)_swapEndianU32(coreinitData->ghsErrno);
|
|
}
|
|
|
|
// error translation modes for _translateError
|
|
#define _ERROR_MODE_DEFAULT 0
|
|
#define _ERROR_MODE_CONNECT 1
|
|
#define _ERROR_MODE_ACCEPT 2
|
|
|
|
sint32 _translateError(sint32 returnCode, sint32 wsaError, sint32 mode = _ERROR_MODE_DEFAULT)
|
|
{
|
|
if (mode == _ERROR_MODE_ACCEPT)
|
|
{
|
|
// accept mode
|
|
if (returnCode >= 0)
|
|
{
|
|
_setSockError(WU_SO_SUCCESS);
|
|
return returnCode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// any other mode
|
|
if (returnCode == 0)
|
|
{
|
|
_setSockError(WU_SO_SUCCESS);
|
|
return 0;
|
|
}
|
|
}
|
|
// handle WSA error
|
|
switch (wsaError)
|
|
{
|
|
case 0:
|
|
_setSockError(WU_SO_SUCCESS);
|
|
break;
|
|
case WSAEWOULDBLOCK:
|
|
if( mode == _ERROR_MODE_CONNECT )
|
|
_setSockError(WU_SO_EINPROGRESS);
|
|
else
|
|
_setSockError(WU_SO_EWOULDBLOCK);
|
|
break;
|
|
case WSAEINPROGRESS:
|
|
_setSockError(WU_SO_EINPROGRESS);
|
|
break;
|
|
case WSAECONNABORTED:
|
|
debug_printf("WSAECONNABORTED\n");
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
assert_dbg();
|
|
#endif
|
|
break;
|
|
case WSAESHUTDOWN:
|
|
_setSockError(WU_SO_ESHUTDOWN);
|
|
break;
|
|
default:
|
|
cemuLog_logDebug(LogType::Force, "Unhandled wsaError {}\n", wsaError);
|
|
_setSockError(99999); // unhandled error
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void nsysnetExport_socketlasterr(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("socketlasterr() -> %d", _getSockError());
|
|
osLib_returnFromFunction(hCPU, _getSockError());
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
uint32 handle;
|
|
bool isShutdownRecv;
|
|
bool isShutdownSend;
|
|
// socket creation info
|
|
sint32 family;
|
|
sint32 type;
|
|
sint32 protocol;
|
|
// host side info
|
|
SOCKET s;
|
|
// socket options
|
|
bool isNonBlocking;
|
|
}virtualSocket_t;
|
|
|
|
typedef struct
|
|
{
|
|
uint32 wu_s_addr;
|
|
}wu_in_addr;
|
|
|
|
struct wu_sockaddr
|
|
{
|
|
uint16 sa_family;
|
|
uint8 sa_data[14]; // IPv4
|
|
};
|
|
|
|
void sockaddr_guest2host(wu_sockaddr* input, sockaddr* output)
|
|
{
|
|
output->sa_family = _swapEndianU16(input->sa_family);
|
|
memcpy(output->sa_data, input->sa_data, 14);
|
|
}
|
|
|
|
void sockaddr_host2guest(sockaddr* input, wu_sockaddr* output)
|
|
{
|
|
output->sa_family = _swapEndianU16(input->sa_family);
|
|
memcpy(output->sa_data, input->sa_data, 14);
|
|
}
|
|
|
|
struct wu_addrinfo
|
|
{
|
|
sint32 ai_flags;
|
|
sint32 ai_family;
|
|
sint32 ai_socktype;
|
|
sint32 ai_protocol;
|
|
sint32 ai_addrlen;
|
|
MPTR ai_canonname;
|
|
MPTR ai_addr;
|
|
MPTR ai_next;
|
|
};
|
|
|
|
#define WU_SOCKET_LIMIT (32) // only 32 socket handles are supported per running process
|
|
|
|
virtualSocket_t* virtualSocketTable[WU_SOCKET_LIMIT] = { 0 };
|
|
|
|
sint32 _getFreeSocketHandle()
|
|
{
|
|
for (sint32 i = 0; i < WU_SOCKET_LIMIT; i++)
|
|
{
|
|
if (virtualSocketTable[i] == NULL)
|
|
return i + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if BOOST_OS_WINDOWS
|
|
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
|
|
#endif // BOOST_OS_WINDOWS
|
|
|
|
WUSOCKET nsysnet_createVirtualSocket(sint32 family, sint32 type, sint32 protocol)
|
|
{
|
|
sint32 s = _getFreeSocketHandle();
|
|
if (s == 0)
|
|
{
|
|
forceLogDebug_printf("Ran out of socket handles");
|
|
cemu_assert(false);
|
|
}
|
|
virtualSocket_t* vs = (virtualSocket_t*)malloc(sizeof(virtualSocket_t));
|
|
memset(vs, 0, sizeof(virtualSocket_t));
|
|
vs->family = family;
|
|
vs->type = type;
|
|
vs->protocol = protocol;
|
|
vs->handle = s;
|
|
virtualSocketTable[s - 1] = vs;
|
|
// init host socket
|
|
vs->s = socket(family, type, protocol);
|
|
#if BOOST_OS_WINDOWS
|
|
// disable reporting of PORT_UNREACHABLE for UDP sockets
|
|
if (protocol == IPPROTO_UDP)
|
|
{
|
|
BOOL bNewBehavior = FALSE;
|
|
DWORD dwBytesReturned = 0;
|
|
WSAIoctl(vs->s, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
|
|
}
|
|
#endif // BOOST_OS_WINDOWS
|
|
return vs->handle;
|
|
}
|
|
|
|
WUSOCKET nsysnet_createVirtualSocketFromExistingSocket(SOCKET existingSocket)
|
|
{
|
|
forceLogDebug_printf("nsysnet_createVirtualSocketFromExistingSocket - incomplete");
|
|
|
|
|
|
sint32 s = _getFreeSocketHandle();
|
|
if (s == 0)
|
|
{
|
|
forceLogDebug_printf("Ran out of socket handles");
|
|
cemu_assert(false);
|
|
}
|
|
virtualSocket_t* vs = (virtualSocket_t*)malloc(sizeof(virtualSocket_t));
|
|
memset(vs, 0, sizeof(virtualSocket_t));
|
|
#if BOOST_OS_WINDOWS
|
|
// SO_TYPE -> type
|
|
// SO_BSP_STATE -> protocol + other info
|
|
// SO_PROTOCOL_INFO -> protocol + type?
|
|
|
|
WSAPROTOCOL_INFO protocolInfo = { 0 };
|
|
int optLen = sizeof(protocolInfo);
|
|
getsockopt(existingSocket, SOL_SOCKET, SO_PROTOCOL_INFO, (char*)&protocolInfo, &optLen);
|
|
// todo - translate protocolInfo
|
|
vs->family = protocolInfo.iAddressFamily;
|
|
vs->type = protocolInfo.iSocketType;
|
|
vs->protocol = protocolInfo.iSocketType;
|
|
#else
|
|
{
|
|
int type;
|
|
socklen_t optlen;
|
|
getsockopt(vs->s, SOL_SOCKET, SO_TYPE, &type, &optlen);
|
|
vs->type = type;
|
|
vs->protocol = type;
|
|
}
|
|
{
|
|
sockaddr saddr;
|
|
socklen_t len;
|
|
getsockname(vs->s, &saddr, &len);
|
|
vs->family = saddr.sa_family;
|
|
}
|
|
#endif
|
|
|
|
vs->handle = s;
|
|
virtualSocketTable[s - 1] = vs;
|
|
vs->s = existingSocket;
|
|
return vs->handle;
|
|
}
|
|
|
|
void nsysnet_notifyCloseSharedSocket(SOCKET existingSocket)
|
|
{
|
|
for (sint32 i = 0; i < WU_SOCKET_LIMIT; i++)
|
|
{
|
|
if (virtualSocketTable[i] && virtualSocketTable[i]->s == existingSocket)
|
|
{
|
|
// remove entry
|
|
free(virtualSocketTable[i]);
|
|
virtualSocketTable[i] = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtualSocket_t* nsysnet_getVirtualSocketObject(WUSOCKET s)
|
|
{
|
|
uint8 handleType = 0;
|
|
s--;
|
|
if (s < 0 || s >= WU_SOCKET_LIMIT)
|
|
return NULL;
|
|
return virtualSocketTable[s];
|
|
}
|
|
|
|
sint32 nsysnet_getVirtualSocketHandleFromHostHandle(SOCKET s)
|
|
{
|
|
for (sint32 i = 0; i < WU_SOCKET_LIMIT; i++)
|
|
{
|
|
if (virtualSocketTable[i] && virtualSocketTable[i]->s == s)
|
|
return i + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void nsysnetExport_socket(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("socket(%d,%d,%d)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
|
ppcDefineParamS32(family, 0);
|
|
ppcDefineParamS32(type, 1);
|
|
ppcDefineParamS32(protocol, 2);
|
|
|
|
if (sockLibReady == false)
|
|
{
|
|
_setSockError(0x2B);
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
// below here Ioctl code should start
|
|
|
|
// check family param
|
|
if (family != WU_AF_INET)
|
|
{
|
|
forceLogDebug_printf("socket(): Unsupported family");
|
|
// todo - error code
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
// check type param
|
|
if (type != WU_SOCK_STREAM && type != WU_SOCK_DGRAM)
|
|
{
|
|
forceLogDebug_printf("socket(): Unsupported family");
|
|
// todo - error code
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
|
|
if (protocol != WU_IPPROTO_TCP && protocol != WU_IPPROTO_UDP && protocol != WU_IPPROTO_IP)
|
|
{
|
|
forceLogDebug_printf("socket(): Unsupported protocol");
|
|
// todo - error code
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
|
|
WUSOCKET s = nsysnet_createVirtualSocket(family, type, protocol);
|
|
socketLog_printf("Created socket handle %d", s);
|
|
osLib_returnFromFunction(hCPU, s);
|
|
}
|
|
|
|
void nsysnetExport_mw_socket(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("mw_socket");
|
|
nsysnetExport_socket(hCPU);
|
|
}
|
|
|
|
void nsysnetExport_shutdown(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("shutdown(%d,%d)", hCPU->gpr[3], hCPU->gpr[4]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamS32(how, 1);
|
|
|
|
sint32 r = 0;
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
}
|
|
else
|
|
{
|
|
r = shutdown(vs->s, how);
|
|
if (how == 0)
|
|
{
|
|
// shutdown recv
|
|
vs->isShutdownRecv = true;
|
|
}
|
|
else if (how == 1)
|
|
{
|
|
// shutdown send
|
|
vs->isShutdownSend = true;
|
|
}
|
|
else if (how == 2)
|
|
{
|
|
// shutdown recv & send
|
|
vs->isShutdownRecv = true;
|
|
vs->isShutdownSend = true;
|
|
}
|
|
else
|
|
assert_dbg();
|
|
}
|
|
_setSockError(0); // todo
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_socketclose(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("socketclose(%d)", hCPU->gpr[3]);
|
|
ppcDefineParamS32(s, 0);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs)
|
|
{
|
|
closesocket(vs->s);
|
|
free(vs);
|
|
virtualSocketTable[s - 1] = NULL;
|
|
}
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
}
|
|
sint32 _socket_nonblock(SOCKET s, u_long mode)
|
|
{
|
|
#if BOOST_OS_WINDOWS
|
|
return ioctlsocket(s, FIONBIO, &mode);
|
|
#else
|
|
int flags = fcntl(s, F_GETFL);
|
|
if(mode)
|
|
flags |= O_NONBLOCK;
|
|
else
|
|
flags &= ~O_NONBLOCK;
|
|
return fcntl(s, F_SETFL, flags);
|
|
#endif
|
|
}
|
|
void nsysnetExport_setsockopt(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("setsockopt(%d,0x%x,0x%05x,0x%08x,%d)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamS32(level, 1);
|
|
ppcDefineParamS32(optname, 2);
|
|
ppcDefineParamStr(optval, 3);
|
|
ppcDefineParamS32(optlen, 4);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (!vs)
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
else
|
|
{
|
|
// translate level to host API
|
|
sint32 hostLevel;
|
|
if (level == WU_SOL_SOCKET)
|
|
{
|
|
hostLevel = SOL_SOCKET;
|
|
// handle op
|
|
if (optname == WU_SO_REUSEADDR)
|
|
{
|
|
if (optlen != 4)
|
|
cemu_assert_suspicious();
|
|
sint32 optvalLE = _swapEndianU32(*(uint32*)optval);
|
|
sint32 r = setsockopt(vs->s, hostLevel, SO_REUSEADDR, (char*)&optvalLE, 4);
|
|
if (r != 0)
|
|
cemu_assert_suspicious();
|
|
}
|
|
else if (optname == WU_SO_NBIO)
|
|
{
|
|
// similar to WU_SO_NONBLOCK but always sets non-blocking mode regardless of option value
|
|
if (optlen == 4)
|
|
{
|
|
sint32 optvalLE = _swapEndianU32(*(uint32*)optval);
|
|
}
|
|
else if (optlen == 0)
|
|
{
|
|
// no opt needed
|
|
}
|
|
else
|
|
cemu_assert_suspicious();
|
|
u_long mode = 1;
|
|
_socket_nonblock(vs->s, mode);
|
|
vs->isNonBlocking = true;
|
|
}
|
|
else if (optname == WU_SO_NONBLOCK)
|
|
{
|
|
if (optlen != 4)
|
|
assert_dbg();
|
|
sint32 optvalLE = _swapEndianU32(*(uint32*)optval);
|
|
u_long mode = optvalLE; // 1 -> enable, 0 -> disable
|
|
_socket_nonblock(vs->s, mode);
|
|
vs->isNonBlocking = mode != 0;
|
|
}
|
|
else if (optname == WU_SO_KEEPALIVE)
|
|
{
|
|
cemuLog_logDebug(LogType::Socket, "todo: setsockopt() for WU_SO_KEEPALIVE");
|
|
}
|
|
else if (optname == WU_SO_WINSCALE)
|
|
{
|
|
cemuLog_logDebug(LogType::Socket, "todo: setsockopt() for WU_SO_WINSCALE");
|
|
}
|
|
else if (optname == WU_SO_RCVBUF)
|
|
{
|
|
socketLog_printf("Set receive buffer size to 0x%08x", _swapEndianU32(*(uint32*)optval));
|
|
if (optlen != 4)
|
|
assert_dbg();
|
|
sint32 optvalLE = _swapEndianU32(*(uint32*)optval);
|
|
u_long mode = optvalLE;
|
|
if (setsockopt(vs->s, SOL_SOCKET, SO_RCVBUF, (const char*)&mode, sizeof(u_long)) != 0)
|
|
assert_dbg();
|
|
}
|
|
else if (optname == WU_SO_SNDBUF)
|
|
{
|
|
socketLog_printf("Set send buffer size to 0x%08x", _swapEndianU32(*(uint32*)optval));
|
|
if (optlen != 4)
|
|
assert_dbg();
|
|
sint32 optvalLE = _swapEndianU32(*(uint32*)optval);
|
|
u_long mode = optvalLE;
|
|
if (setsockopt(vs->s, SOL_SOCKET, SO_SNDBUF, (const char*)&mode, sizeof(u_long)) != 0)
|
|
assert_dbg();
|
|
}
|
|
else
|
|
{
|
|
forceLogDebug_printf("setsockopt(): Unsupported optname 0x%08x", optname);
|
|
}
|
|
}
|
|
else if (level == WU_IPPROTO_TCP)
|
|
{
|
|
hostLevel = IPPROTO_TCP;
|
|
if (optname == WU_TCP_NODELAY)
|
|
{
|
|
if (optlen != 4)
|
|
assert_dbg();
|
|
sint32 optvalLE = _swapEndianU32(*(uint32*)optval);
|
|
u_long mode = optvalLE; // 1 -> enable, 0 -> disable
|
|
if (setsockopt(vs->s, IPPROTO_TCP, TCP_NODELAY, (const char*)&mode, sizeof(u_long)) != 0)
|
|
assert_dbg();
|
|
}
|
|
else
|
|
assert_dbg();
|
|
}
|
|
else if (level == WU_IPPROTO_IP)
|
|
{
|
|
hostLevel = IPPROTO_IP;
|
|
if (optname == 0xC)
|
|
{
|
|
// unknown
|
|
}
|
|
else if( optname == 0x4 )
|
|
{
|
|
forceLogDebug_printf("setsockopt with unsupported opname 4 for IPPROTO_IP");
|
|
}
|
|
else
|
|
assert_dbg();
|
|
|
|
}
|
|
else
|
|
assert_dbg();
|
|
}
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
}
|
|
|
|
void nsysnetExport_getsockopt(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("getsockopt(%d,0x%x,0x%05x,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamS32(level, 1);
|
|
ppcDefineParamS32(optname, 2);
|
|
ppcDefineParamStr(optval, 3);
|
|
ppcDefineParamMPTR(optlenMPTR, 4);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
cemu_assert_debug(false); // todo
|
|
return;
|
|
}
|
|
|
|
sint32 r = 0;
|
|
|
|
// translate level to host API
|
|
sint32 hostLevel;
|
|
if (level == WU_SOL_SOCKET)
|
|
{
|
|
hostLevel = SOL_SOCKET;
|
|
// handle op
|
|
if (optname == WU_SO_LASTERROR)
|
|
{
|
|
int optvalLE = 0;
|
|
socklen_t optlenLE = 4;
|
|
r = getsockopt(vs->s, hostLevel, SO_ERROR, (char*)&optvalLE, &optlenLE);
|
|
r = _translateError(r, GETLASTERR);
|
|
if (memory_readU32(optlenMPTR) != 4)
|
|
assert_dbg();
|
|
memory_writeU32(optlenMPTR, 4);
|
|
if (optvalLE != 0)
|
|
assert_dbg(); // todo -> Translate error code/status
|
|
*(uint32*)optval = _swapEndianU32(optvalLE);
|
|
// YouTube app uses this to check if the connection attempt was successful after non-blocking connect()
|
|
}
|
|
else if (optname == WU_SO_RCVBUF)
|
|
{
|
|
int optvalLE = 0;
|
|
socklen_t optlenLE = 4;
|
|
r = getsockopt(vs->s, hostLevel, SO_RCVBUF, (char*)&optvalLE, &optlenLE);
|
|
r = _translateError(r, GETLASTERR);
|
|
if (memory_readU32(optlenMPTR) != 4)
|
|
assert_dbg();
|
|
memory_writeU32(optlenMPTR, 4);
|
|
*(uint32*)optval = _swapEndianU32(optvalLE);
|
|
// used by Amazon Video app when a video starts playing
|
|
}
|
|
else if (optname == WU_SO_SNDBUF)
|
|
{
|
|
int optvalLE = 0;
|
|
socklen_t optlenLE = 4;
|
|
r = getsockopt(vs->s, hostLevel, SO_SNDBUF, (char*)&optvalLE, &optlenLE);
|
|
r = _translateError(r, GETLASTERR);
|
|
if (memory_readU32(optlenMPTR) != 4)
|
|
assert_dbg();
|
|
memory_writeU32(optlenMPTR, 4);
|
|
*(uint32*)optval = _swapEndianU32(optvalLE);
|
|
// used by Lost Reavers after some loading screens
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_inet_aton(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamStr(ip, 0);
|
|
ppcDefineParamStructPtr(addr, wu_in_addr, 1);
|
|
socketLog_printf("inet_aton(\"%s\",0x%08x)", ip, hCPU->gpr[4]);
|
|
|
|
// _parse_ipad -> todo
|
|
sint32 d0, d1, d2, d3;
|
|
if (sscanf(ip, "%d.%d.%d.%d", &d0, &d1, &d2, &d3) != 4)
|
|
{
|
|
cemu_assert_debug(false);
|
|
osLib_returnFromFunction(hCPU, 0); // todo - return correct error code
|
|
return;
|
|
}
|
|
|
|
if (d0 < 0 || d0 > 255 ||
|
|
d1 < 0 || d1 > 255 ||
|
|
d2 < 0 || d2 > 255 ||
|
|
d3 < 0 || d3 > 255)
|
|
{
|
|
cemu_assert_debug(false);
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
return;
|
|
}
|
|
|
|
addr->wu_s_addr = _swapEndianU32((d0 << 24) | (d1 << 16) | (d2 << 8) | (d3 << 0));
|
|
|
|
osLib_returnFromFunction(hCPU, 1); // 0 -> error, 1 -> success
|
|
}
|
|
|
|
void nsysnetExport_inet_pton(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamS32(af, 0);
|
|
ppcDefineParamStr(ip, 1);
|
|
ppcDefineParamStructPtr(addr, wu_in_addr, 2);
|
|
|
|
if (af != 2)
|
|
{
|
|
forceLog_printf("inet_pton() only supports AF_INET");
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
return;
|
|
}
|
|
|
|
sint32 d0, d1, d2, d3;
|
|
bool invalidIp = false;
|
|
if (sscanf(ip, "%d.%d.%d.%d", &d0, &d1, &d2, &d3) != 4)
|
|
invalidIp = true;
|
|
if (d0 < 0 || d0 > 255)
|
|
invalidIp = true;
|
|
if (d1 < 0 || d1 > 255)
|
|
invalidIp = true;
|
|
if (d2 < 0 || d2 > 255)
|
|
invalidIp = true;
|
|
if (d3 < 0 || d3 > 255)
|
|
invalidIp = true;
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
if (invalidIp)
|
|
assert_dbg();
|
|
#endif
|
|
if (invalidIp)
|
|
{
|
|
socketLog_printf("inet_pton(%d, \"%s\", 0x%08x) -> Invalid ip", af, ip, hCPU->gpr[5]);
|
|
osLib_returnFromFunction(hCPU, 0); // 0 -> invalid address
|
|
return;
|
|
}
|
|
|
|
addr->wu_s_addr = _swapEndianU32((d0 << 24) | (d1 << 16) | (d2 << 8) | (d3 << 0));
|
|
socketLog_printf("inet_pton(%d, \"%s\", 0x%08x) -> Ok", af, ip, hCPU->gpr[5]);
|
|
|
|
osLib_returnFromFunction(hCPU, 1); // 1 -> success
|
|
}
|
|
|
|
MEMPTR<char> _ntoa_tempString = nullptr;
|
|
|
|
void nsysnetExport_inet_ntoa(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamStructPtr(addr, wu_in_addr, 0);
|
|
socketLog_printf("inet_ntoa(0x%08x)", hCPU->gpr[3]);
|
|
|
|
if (_ntoa_tempString == nullptr)
|
|
_ntoa_tempString = (char*)memory_getPointerFromVirtualOffset(OSAllocFromSystem(64, 4));
|
|
|
|
sprintf(_ntoa_tempString.GetPtr(), "%d.%d.%d.%d", ((uint8*)addr)[0], ((uint8*)addr)[1], ((uint8*)addr)[2], ((uint8*)addr)[3]);
|
|
|
|
osLib_returnFromFunction(hCPU, _ntoa_tempString.GetMPTR());
|
|
}
|
|
|
|
void nsysnetExport_htons(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("htons(0x%04x)", hCPU->gpr[3]);
|
|
ppcDefineParamU32(v, 0);
|
|
osLib_returnFromFunction(hCPU, v); // return value as-is
|
|
}
|
|
|
|
void nsysnetExport_htonl(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("htonl(0x%08x)", hCPU->gpr[3]);
|
|
ppcDefineParamU32(v, 0);
|
|
osLib_returnFromFunction(hCPU, v); // return value as-is
|
|
}
|
|
|
|
void nsysnetExport_ntohs(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("ntohs(0x%04x)", hCPU->gpr[3]);
|
|
ppcDefineParamU32(v, 0);
|
|
osLib_returnFromFunction(hCPU, v); // return value as-is
|
|
}
|
|
|
|
void nsysnetExport_ntohl(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("ntohl(0x%08x)", hCPU->gpr[3]);
|
|
ppcDefineParamU32(v, 0);
|
|
osLib_returnFromFunction(hCPU, v); // return value as-is
|
|
}
|
|
|
|
void nsysnetExport_bind(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("bind(%d,0x%08x,%d)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStructPtr(addr, struct wu_sockaddr, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
|
|
|
|
if (len != sizeof(struct wu_sockaddr))
|
|
assert_dbg();
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
}
|
|
else
|
|
{
|
|
if (sizeof(sockaddr) != 16)
|
|
assert_dbg();
|
|
sockaddr hostAddr;
|
|
hostAddr.sa_family = _swapEndianU16(addr->sa_family);
|
|
memcpy(hostAddr.sa_data, addr->sa_data, 14);
|
|
sint32 hr = bind(vs->s, &hostAddr, sizeof(sockaddr));
|
|
r = _translateError(hr, GETLASTERR);
|
|
|
|
|
|
socketLog_printf("bind address: %d.%d.%d.%d:%d result: %d", addr->sa_data[2], addr->sa_data[3], addr->sa_data[4], addr->sa_data[5], _swapEndianU16(*(uint16*)addr->sa_data), hr);
|
|
|
|
}
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_listen(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("listen(%d,%d)", hCPU->gpr[3], hCPU->gpr[4]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamS32(queueSize, 1);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
}
|
|
else
|
|
{
|
|
sint32 hr = listen(vs->s, queueSize);
|
|
if (hr != 0)
|
|
r = -1;
|
|
// todo: Set proper coreinit errno (via _setSockError)
|
|
}
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_accept(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("accept(%d,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStructPtr(addr, struct wu_sockaddr, 1);
|
|
ppcDefineParamMPTR(lenMPTR, 2);
|
|
|
|
sint32 r = 0;
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
// todo
|
|
return;
|
|
}
|
|
|
|
if (memory_readU32(lenMPTR) != 16)
|
|
{
|
|
forceLog_printf("invalid sockaddr len in accept()");
|
|
cemu_assert_debug(false);
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
return;
|
|
}
|
|
|
|
if (vs->isNonBlocking)
|
|
{
|
|
sockaddr hostAddr;
|
|
socklen_t hostLen = sizeof(sockaddr);
|
|
SOCKET hr = accept(vs->s, &hostAddr, &hostLen);
|
|
if (hr != SOCKET_ERROR)
|
|
{
|
|
r = nsysnet_createVirtualSocketFromExistingSocket(hr);
|
|
_setSockError(WU_SO_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
r = _translateError((sint32)hr, (sint32)GETLASTERR, _ERROR_MODE_ACCEPT);
|
|
}
|
|
sockaddr_host2guest(&hostAddr, addr);
|
|
}
|
|
else
|
|
{
|
|
// blocking accept is not supported yet
|
|
forceLog_printf("blocking accept() not supported");
|
|
cemu_assert_debug(false);
|
|
}
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_connect(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("connect(%d,0x%08x,%d)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStructPtr(addr, struct wu_sockaddr, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
|
|
if (sizeof(sockaddr) != 16)
|
|
assert_dbg();
|
|
sockaddr hostAddr;
|
|
hostAddr.sa_family = _swapEndianU16(addr->sa_family);
|
|
memcpy(hostAddr.sa_data, addr->sa_data, 14);
|
|
sint32 hr = connect(vs->s, &hostAddr, sizeof(sockaddr));
|
|
forceLog_printf("Attempt connect to %d.%d.%d.%d:%d", (sint32)(uint8)hostAddr.sa_data[2], (sint32)(uint8)hostAddr.sa_data[3], (sint32)(uint8)hostAddr.sa_data[4], (sint32)(uint8)hostAddr.sa_data[5], _swapEndianU16(*(uint16*)hostAddr.sa_data+0));
|
|
|
|
r = _translateError(hr, GETLASTERR, _ERROR_MODE_CONNECT);
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void _setSocketSendRecvNonBlockingMode(SOCKET s, bool isNonBlocking)
|
|
{
|
|
u_long mode = isNonBlocking ? 1 : 0;
|
|
sint32 r = _socket_nonblock(s, mode);
|
|
}
|
|
|
|
void nsysnetExport_send(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("send(%d,0x%08x,%d,0x%x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]);
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStr(msg, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
ppcDefineParamS32(flags, 3);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
int hostFlags = 0;
|
|
bool requestIsNonBlocking = (flags&WU_MSG_DONTWAIT) != 0;
|
|
flags &= ~WU_MSG_DONTWAIT;
|
|
if (requestIsNonBlocking != vs->isNonBlocking && vs->isNonBlocking == false)
|
|
assert_dbg();
|
|
|
|
if (flags)
|
|
assert_dbg();
|
|
|
|
sint32 hr = send(vs->s, msg, len, hostFlags);
|
|
socketLog_printf("Sent %d bytes", hr);
|
|
_translateError(hr <= 0 ? -1 : 0, GETLASTERR);
|
|
r = hr;
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_recv(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("recv(%d,0x%08x,%d,0x%x) LR: 0x%08x Thread: 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStr(msg, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
ppcDefineParamS32(flags, 3);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
int hostFlags = 0;
|
|
bool requestIsNonBlocking = (flags&WU_MSG_DONTWAIT) != 0;
|
|
flags &= ~WU_MSG_DONTWAIT;
|
|
if (flags&WU_MSG_PEEK)
|
|
{
|
|
hostFlags |= MSG_PEEK;
|
|
flags &= ~WU_MSG_PEEK;
|
|
}
|
|
|
|
if (vs->isNonBlocking)
|
|
requestIsNonBlocking = vs->isNonBlocking; // non-blocking sockets always operate in MSG_DONTWAIT mode?
|
|
|
|
if (flags)
|
|
assert_dbg();
|
|
if (requestIsNonBlocking != vs->isNonBlocking)
|
|
_setSocketSendRecvNonBlockingMode(vs->s, requestIsNonBlocking);
|
|
// if blocking, yield thread until there is an error or at least 1 byte was received
|
|
if (requestIsNonBlocking == false)
|
|
{
|
|
_setSocketSendRecvNonBlockingMode(vs->s, true);
|
|
while (true)
|
|
{
|
|
char tempBuffer[1];
|
|
sint32 tr = recv(vs->s, tempBuffer, 1, MSG_PEEK);
|
|
if (tr == 1)
|
|
break;
|
|
if (tr == 0)
|
|
break; // connection closed
|
|
if (tr < 0 && GETLASTERR != WSAEWOULDBLOCK)
|
|
break;
|
|
// yield thread
|
|
coreinit::OSSleepTicks(coreinit::EspressoTime::GetTimerClock() / 5000); // let thread wait 0.2ms to give other threads CPU time
|
|
// todo - eventually we should find a way to asynchronously signal recv instead of busy-looping here
|
|
}
|
|
_setSocketSendRecvNonBlockingMode(vs->s, requestIsNonBlocking);
|
|
}
|
|
// receive
|
|
sint32 hr = recv(vs->s, msg, len, hostFlags);
|
|
_translateError(hr <= 0 ? -1 : 0, GETLASTERR);
|
|
if (requestIsNonBlocking != vs->isNonBlocking)
|
|
_setSocketSendRecvNonBlockingMode(vs->s, vs->isNonBlocking);
|
|
socketLog_printf("Received %d bytes", hr);
|
|
r = hr;
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
struct wu_timeval
|
|
{
|
|
uint32 tv_sec;
|
|
uint32 tv_usec;
|
|
};
|
|
|
|
void _translateFDSet(fd_set* hostSet, struct wu_fd_set* fdset, sint32 nfds, int *hostnfds)
|
|
{
|
|
FD_ZERO(hostSet);
|
|
if (fdset == NULL)
|
|
return;
|
|
|
|
#if BOOST_OS_UNIX
|
|
int maxfd;
|
|
if(hostnfds)
|
|
maxfd = *hostnfds;
|
|
else
|
|
maxfd = -1;
|
|
#endif
|
|
|
|
uint32 mask = fdset->mask;
|
|
for (sint32 i = 0; i < nfds; i++)
|
|
{
|
|
if( ((mask>>i)&1) == 0 )
|
|
continue;
|
|
sint32 socketHandle = i;
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(socketHandle);
|
|
if(vs == NULL)
|
|
continue; // socket invalid
|
|
|
|
#if BOOST_OS_UNIX
|
|
if(vs->s > maxfd)
|
|
maxfd = vs->s;
|
|
#endif
|
|
|
|
FD_SET(vs->s, hostSet);
|
|
}
|
|
|
|
#if BOOST_OS_UNIX
|
|
if(hostnfds)
|
|
*hostnfds = maxfd;
|
|
#endif
|
|
}
|
|
|
|
void _translateFDSetRev(struct wu_fd_set* fdset, fd_set* hostSet, sint32 nfds)
|
|
{
|
|
if (fdset == NULL)
|
|
return;
|
|
uint32 mask = _swapEndianU32(0);
|
|
#if BOOST_OS_WINDOWS
|
|
for (sint32 i = 0; i < (sint32)hostSet->fd_count; i++)
|
|
{
|
|
sint32 virtualSocketHandle = nsysnet_getVirtualSocketHandleFromHostHandle(hostSet->fd_array[i]);
|
|
if (virtualSocketHandle < 0)
|
|
cemu_assert_debug(false);
|
|
mask |= (1<<virtualSocketHandle);
|
|
}
|
|
#else
|
|
for (sint32 i = 0; i < WU_SOCKET_LIMIT; i++)
|
|
{
|
|
if (virtualSocketTable[i] && virtualSocketTable[i]->s && FD_ISSET(virtualSocketTable[i]->s, hostSet))
|
|
mask |= (1 << virtualSocketTable[i]->handle);
|
|
}
|
|
#endif
|
|
fdset->mask = mask;
|
|
|
|
}
|
|
|
|
void nsysnetExport_select(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("select(%d,0x%08x,0x%08x,0x%08x,0x%08x) LR 0x%08x Thread 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(nfds, 0);
|
|
ppcDefineParamStructPtr(readfds, struct wu_fd_set, 1);
|
|
ppcDefineParamStructPtr(writefds, struct wu_fd_set, 2);
|
|
ppcDefineParamStructPtr(exceptfds, struct wu_fd_set, 3);
|
|
ppcDefineParamStructPtr(timeOut, struct wu_timeval, 4);
|
|
|
|
//socketLog_printf("rm %08x wm %08x em %08x", readfds ? _swapEndianU32(readfds->mask) : 0, writefds ? _swapEndianU32(writefds->mask) : 0, exceptfds ? _swapEndianU32(exceptfds->mask) : 0);
|
|
|
|
sint32 r = 0;
|
|
|
|
// translate fd_set
|
|
fd_set _readfds, _writefds, _exceptfds;
|
|
|
|
// handle empty select
|
|
if ((readfds == NULL || readfds->mask == 0) && (writefds == NULL || writefds->mask == 0) && (exceptfds == NULL || exceptfds->mask == 0))
|
|
{
|
|
if (timeOut == NULL || (timeOut->tv_sec == 0 && timeOut->tv_usec == 0))
|
|
{
|
|
// return immediately
|
|
socketLog_printf("select returned immediately because of empty fdsets without timeout");
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//// empty select with timeout is not allowed
|
|
//_setSockError(WU_SO_EINVAL);
|
|
//osLib_returnFromFunction(hCPU, -1);
|
|
//socketLog_printf("select returned SO_EINVAL because of empty fdsets with timeout");
|
|
|
|
// when fd sets are empty but timeout is set, then just wait and do nothing?
|
|
// Lost Reavers seems to expect this case to return 0 (it hardcodes empty fd sets and timeout comes from curl_multi_timeout)
|
|
|
|
timeval tv;
|
|
tv.tv_sec = timeOut->tv_sec;
|
|
tv.tv_usec = timeOut->tv_usec;
|
|
select(0, nullptr, nullptr, nullptr, &tv);
|
|
socketLog_printf("select returned 0 because of empty fdsets with timeout");
|
|
osLib_returnFromFunction(hCPU, 0);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
timeval tv = { 0 };
|
|
|
|
uint64 msTimeout = (_swapEndianU32(timeOut->tv_usec) / 1000) + (_swapEndianU32(timeOut->tv_sec) * 1000);
|
|
uint32 startTime = GetTickCount();
|
|
while (true)
|
|
{
|
|
int hostnfds = -1;
|
|
_translateFDSet(&_readfds, readfds, nfds, &hostnfds);
|
|
_translateFDSet(&_writefds, writefds, nfds, &hostnfds);
|
|
_translateFDSet(&_exceptfds, exceptfds, nfds, &hostnfds);
|
|
r = select(hostnfds + 1, readfds ? &_readfds : NULL, writefds ? &_writefds : NULL, exceptfds ? &_exceptfds : NULL, &tv);
|
|
if (r < 0)
|
|
{
|
|
forceLogDebug_printf("select() failed");
|
|
// timeout
|
|
_translateError(r, GETLASTERR);
|
|
//_setSockError(WU_SO_SUCCESS);
|
|
// in case of error, clear all FD sets (?)
|
|
if (readfds)
|
|
readfds->mask = 0;
|
|
if (writefds)
|
|
writefds->mask = 0;
|
|
if (exceptfds)
|
|
exceptfds->mask = 0;
|
|
break;
|
|
}
|
|
else if (r == 0)
|
|
{
|
|
// check for timeout
|
|
if ((GetTickCount() - startTime) >= msTimeout )
|
|
{
|
|
// timeout
|
|
_setSockError(WU_SO_SUCCESS);
|
|
r = 0;
|
|
// in case of timeout, clear all FD sets
|
|
if (readfds)
|
|
readfds->mask = 0;
|
|
if (writefds)
|
|
writefds->mask = 0;
|
|
if (exceptfds)
|
|
exceptfds->mask = 0;
|
|
break;
|
|
}
|
|
// yield thread
|
|
PPCCore_switchToScheduler();
|
|
}
|
|
else
|
|
{
|
|
// socketLog_printf("select returned %d. Read %d Write %d Except %d", r, _readfds.fd_count, _writefds.fd_count, _exceptfds.fd_count);
|
|
socketLog_printf("select returned %d.", r);
|
|
|
|
_translateFDSetRev(readfds, &_readfds, nfds);
|
|
_translateFDSetRev(writefds, &_writefds, nfds);
|
|
_translateFDSetRev(exceptfds, &_exceptfds, nfds);
|
|
break;
|
|
}
|
|
}
|
|
//forceLog_printf("selectEndTime %d", timeGetTime());
|
|
|
|
|
|
//extern int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
|
// struct timeval *timeout);
|
|
|
|
socketLog_printf("select returned %d", r);
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_getsockname(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("getsockname(%d,0x%08x,0x%08x)", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
|
|
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStructPtr(addr, struct wu_sockaddr, 1);
|
|
ppcDefineParamStructPtr(lenPtr, uint32, 2);
|
|
sint32 r = 0;
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
}
|
|
else
|
|
{
|
|
struct sockaddr hostAddr;
|
|
socklen_t hostLen = sizeof(struct sockaddr);
|
|
sint32 hr = getsockname(vs->s, &hostAddr, &hostLen);
|
|
if (hr == 0)
|
|
{
|
|
addr->sa_family = _swapEndianU16(hostAddr.sa_family);
|
|
memcpy(addr->sa_data, hostAddr.sa_data, 14);
|
|
}
|
|
else
|
|
{
|
|
assert_dbg();
|
|
}
|
|
//sint32 hr = listen(vs->s, queueSize);
|
|
//if (hr != 0)
|
|
// r = -1;
|
|
//// todo: Set proper coreinit errno (via _setSockError)
|
|
}
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_getpeername(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStructPtr(name, struct wu_sockaddr, 1);
|
|
ppcDefineParamU32BEPtr(nameLen, 2);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
// todo: Return correct error
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
|
|
sockaddr saddr;
|
|
socklen_t saddrLen = sizeof(sockaddr);
|
|
|
|
if (*nameLen < (uint32be)16)
|
|
assert_dbg();
|
|
|
|
sint32 r = getpeername(vs->s, &saddr, &saddrLen);
|
|
r = _translateError(r, GETLASTERR);
|
|
|
|
name->sa_family = _swapEndianU16(saddr.sa_family);
|
|
memcpy(name->sa_data, saddr.sa_data, 14);
|
|
*nameLen = 16;
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
MPTR h_name;
|
|
MPTR h_aliases;
|
|
sint32 h_addrType;
|
|
sint32 h_length;
|
|
MPTR h_addr_list;
|
|
}wu_hostent;
|
|
|
|
MPTR _allocString(char* str)
|
|
{
|
|
sint32 len = (sint32)strlen(str);
|
|
MPTR strMPTR = coreinit_allocFromSysArea(len+1, 4);
|
|
strcpy((char*)memory_getPointerFromVirtualOffset(strMPTR), str);
|
|
return strMPTR;
|
|
}
|
|
|
|
void nsysnetExport_gethostbyname(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamStr(name, 0);
|
|
|
|
socketLog_printf("gethostbyname(\"%s\")", name);
|
|
|
|
hostent* he = gethostbyname(name);
|
|
if (he == NULL)
|
|
{
|
|
osLib_returnFromFunction(hCPU, MPTR_NULL);
|
|
return;
|
|
}
|
|
|
|
|
|
MPTR hostentMPTR = coreinit_allocFromSysArea(sizeof(wu_hostent)*1, 4);
|
|
MPTR hostentAddrListMPTR = coreinit_allocFromSysArea(sizeof(MPTR) * 2, 4);
|
|
MPTR hostentAddrListEntriesMPTR = coreinit_allocFromSysArea(sizeof(wu_in_addr) * 1, 4);
|
|
|
|
wu_hostent* wuHostent = (wu_hostent*)memory_getPointerFromVirtualOffset(hostentMPTR);
|
|
MPTR* addrList = (MPTR*)memory_getPointerFromVirtualOffset(hostentAddrListMPTR);
|
|
|
|
wuHostent->h_addrType = _swapEndianU32((uint32)he->h_addrtype);
|
|
wuHostent->h_length = _swapEndianU32((uint32)he->h_length);
|
|
|
|
wuHostent->h_name = _swapEndianU32(_allocString(he->h_name));
|
|
wuHostent->h_addr_list = _swapEndianU32(hostentAddrListMPTR);
|
|
|
|
//memory_writeU32(hostentAddrListEntriesMPTR, _swapEndianU32(*(uint32*)he->h_addr_list[0]));
|
|
|
|
wu_in_addr* addrListEntries = (wu_in_addr*)memory_getPointerFromVirtualOffset(hostentAddrListEntriesMPTR);
|
|
addrListEntries->wu_s_addr = *(uint32*)he->h_addr_list[0]; // address is already in network (big-endian) order
|
|
|
|
memory_writeU32(hostentAddrListMPTR + 4 * 0, hostentAddrListEntriesMPTR);
|
|
memory_writeU32(hostentAddrListMPTR + 4 * 1, MPTR_NULL);
|
|
|
|
osLib_returnFromFunction(hCPU, hostentMPTR);
|
|
return;
|
|
}
|
|
|
|
SysAllocator<wu_hostent> _staticHostent;
|
|
SysAllocator<char, 256> _staticHostentName;
|
|
SysAllocator<MPTR, 32> _staticHostentPtrList;
|
|
SysAllocator<wu_in_addr> _staticHostentEntries;
|
|
|
|
void nsysnetExport_gethostbyaddr(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamStr(addr, 0);
|
|
ppcDefineParamS32(len, 1);
|
|
ppcDefineParamS32(type, 2);
|
|
|
|
sint32 maxNumEntries = 31;
|
|
|
|
socketLog_printf("gethostbyaddr(\"%s\", %d, %d)", addr, len, type);
|
|
|
|
hostent* he = gethostbyaddr(addr, len, type);
|
|
if (he == nullptr)
|
|
{
|
|
socketLog_printf("gethostbyaddr(\"%s\", %d, %d) failed", addr, len, type);
|
|
osLib_returnFromFunction(hCPU, MPTR_NULL);
|
|
return;
|
|
}
|
|
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
if (he->h_addrtype != AF_INET)
|
|
assert_dbg();
|
|
if (he->h_length != sizeof(in_addr))
|
|
assert_dbg();
|
|
#endif
|
|
|
|
wu_hostent* wuHostent = _staticHostent.GetPtr();
|
|
// setup wuHostent->h_name
|
|
wuHostent->h_name = _swapEndianU32(_staticHostentName.GetMPTR());
|
|
if (he->h_name && strlen(he->h_name) < 255)
|
|
{
|
|
strcpy(_staticHostentName.GetPtr(), he->h_name);
|
|
}
|
|
else
|
|
{
|
|
forceLog_printf("he->h_name not set or name too long");
|
|
strcpy(_staticHostentName.GetPtr(), "");
|
|
}
|
|
// setup wuHostent address list
|
|
wuHostent->h_addrType = _swapEndianU32(WU_AF_INET);
|
|
wuHostent->h_addr_list = _swapEndianU32(_staticHostentPtrList.GetMPTR());
|
|
wuHostent->h_length = _swapEndianU32(sizeof(wu_in_addr));
|
|
for (sint32 i = 0; i < maxNumEntries; i++)
|
|
{
|
|
if (he->h_addr_list[i] == nullptr)
|
|
{
|
|
_staticHostentPtrList.GetPtr()[i] = MPTR_NULL;
|
|
break;
|
|
}
|
|
memcpy(_staticHostentEntries.GetPtr() + i, he->h_addr_list[i], sizeof(in_addr));
|
|
_staticHostentPtrList.GetPtr()[i] = _swapEndianU32(memory_getVirtualOffsetFromPointer(_staticHostentEntries.GetPtr() + i));
|
|
}
|
|
_staticHostentPtrList.GetPtr()[31] = MPTR_NULL;
|
|
// aliases are ignored for now
|
|
wuHostent->h_aliases = MPTR_NULL;
|
|
|
|
osLib_returnFromFunction(hCPU, _staticHostent.GetMPTR());
|
|
return;
|
|
}
|
|
|
|
void nsysnetExport_getaddrinfo(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamStr(nodeName, 0);
|
|
ppcDefineParamStr(serviceName, 1);
|
|
ppcDefineParamStructPtr(hints, struct wu_addrinfo, 2);
|
|
ppcDefineParamMPTR(results, 3);
|
|
|
|
socketLog_printf("getaddrinfo(\"%s\",0x%08x,0x%08x,0x%08x)", nodeName, hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]);
|
|
|
|
sint32 r = 0;
|
|
|
|
// todo1: This is really slow. Make it asynchronous
|
|
// todo2: Should this set the socket last error code?
|
|
|
|
struct addrinfo hint = { 0 };
|
|
if (hints)
|
|
{
|
|
hint.ai_family = _swapEndianU32(hints->ai_family);
|
|
hint.ai_socktype = _swapEndianU32(hints->ai_socktype);
|
|
hint.ai_flags = _swapEndianU32(hints->ai_flags);
|
|
hint.ai_protocol = _swapEndianU32(hints->ai_protocol);
|
|
}
|
|
else
|
|
{
|
|
hint.ai_family = 0;
|
|
hint.ai_socktype = 0;
|
|
hint.ai_flags = 0;
|
|
hint.ai_protocol = 0;
|
|
}
|
|
struct addrinfo* result;
|
|
sint32 hr = getaddrinfo(nodeName, serviceName, &hint, &result);
|
|
if (hr != 0)
|
|
{
|
|
socketLog_printf("getaddrinfo failed with error %d", hr);
|
|
switch (hr)
|
|
{
|
|
case WSAHOST_NOT_FOUND:
|
|
r = WU_SO_ECONNRESET; // todo - verify error code
|
|
break;
|
|
default:
|
|
// unhandled error
|
|
cemu_assert_debug(false);
|
|
socketLog_printf("getaddrinfo unhandled error code");
|
|
r = 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// count how many results there are
|
|
sint32 resultCount = 0;
|
|
struct addrinfo* currentAddrInfo = result;
|
|
while (currentAddrInfo)
|
|
{
|
|
resultCount++;
|
|
currentAddrInfo = currentAddrInfo->ai_next;
|
|
}
|
|
if (resultCount == 0)
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
// allocate entries
|
|
MPTR addrInfoMPTR = coreinit_allocFromSysArea(sizeof(wu_addrinfo)*resultCount + sizeof(wu_sockaddr)*resultCount, 4);
|
|
MPTR addrInfoSockAddrMPTR = addrInfoMPTR + sizeof(wu_addrinfo)*resultCount;
|
|
wu_addrinfo* wuAddrInfo = (wu_addrinfo*)memory_getPointerFromVirtualOffset(addrInfoMPTR);
|
|
// fill entries
|
|
currentAddrInfo = result;
|
|
sint32 entryIndex = 0;
|
|
wu_addrinfo* previousWuAddrInfo = NULL;
|
|
while (currentAddrInfo)
|
|
{
|
|
if (currentAddrInfo->ai_addrlen != 16)
|
|
{
|
|
// skip this entry (IPv6)
|
|
currentAddrInfo = currentAddrInfo->ai_next;
|
|
continue;
|
|
}
|
|
|
|
memset(&wuAddrInfo[entryIndex], 0, sizeof(wu_addrinfo));
|
|
// setup pointers
|
|
wuAddrInfo[entryIndex].ai_addr = _swapEndianU32(addrInfoSockAddrMPTR + sizeof(wu_sockaddr)*entryIndex);
|
|
wuAddrInfo[entryIndex].ai_next = MPTR_NULL;
|
|
wuAddrInfo[entryIndex].ai_canonname = _swapEndianU32(MPTR_NULL);
|
|
// set ai_next for previous element
|
|
if (previousWuAddrInfo)
|
|
{
|
|
previousWuAddrInfo->ai_next = _swapEndianU32(memory_getVirtualOffsetFromPointer(&wuAddrInfo[entryIndex]));
|
|
}
|
|
previousWuAddrInfo = &wuAddrInfo[entryIndex];
|
|
// fill addrinfo struct
|
|
wuAddrInfo[entryIndex].ai_addrlen = _swapEndianU32((uint32)currentAddrInfo->ai_addrlen);
|
|
//wuAddrInfo[entryIndex].ai_canonname; todo
|
|
wuAddrInfo[entryIndex].ai_family = _swapEndianU32(currentAddrInfo->ai_family);
|
|
wuAddrInfo[entryIndex].ai_flags = _swapEndianU32(currentAddrInfo->ai_flags); // todo: These flags might need to be translated
|
|
wuAddrInfo[entryIndex].ai_protocol = _swapEndianU32(currentAddrInfo->ai_protocol);
|
|
wuAddrInfo[entryIndex].ai_socktype = _swapEndianU32(currentAddrInfo->ai_socktype);
|
|
|
|
// fill ai_addr
|
|
wu_sockaddr* sockAddr = (wu_sockaddr*)memory_getPointerFromVirtualOffset(_swapEndianU32(wuAddrInfo[entryIndex].ai_addr));
|
|
sockAddr->sa_family = _swapEndianU16(currentAddrInfo->ai_addr->sa_family);
|
|
memcpy(sockAddr->sa_data, currentAddrInfo->ai_addr->sa_data, 14);
|
|
|
|
// next
|
|
entryIndex++;
|
|
currentAddrInfo = currentAddrInfo->ai_next;
|
|
}
|
|
if (entryIndex == 0)
|
|
assert_dbg();
|
|
// set results
|
|
memory_writeU32(results, addrInfoMPTR);
|
|
}
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
void nsysnetExport_recvfrom(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("recvfrom(%d,0x%08x,%d,0x%x) LR: 0x%08x Thread: 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStr(msg, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
ppcDefineParamS32(flags, 3);
|
|
ppcDefineParamStructPtr(fromAddr, wu_sockaddr, 4);
|
|
ppcDefineParamU32BEPtr(fromLen, 5);
|
|
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
|
|
int hostFlags = 0;
|
|
bool requestIsNonBlocking = (flags&WU_MSG_DONTWAIT) != 0;
|
|
flags &= ~WU_MSG_DONTWAIT;
|
|
if (flags&WU_MSG_PEEK)
|
|
{
|
|
assert_dbg();
|
|
hostFlags |= MSG_PEEK;
|
|
flags &= ~WU_MSG_PEEK;
|
|
}
|
|
if (vs->isNonBlocking)
|
|
requestIsNonBlocking = vs->isNonBlocking;
|
|
|
|
socklen_t fromLenHost = *fromLen;
|
|
sockaddr fromAddrHost;
|
|
sint32 wsaError = 0;
|
|
|
|
while( true )
|
|
{
|
|
// is socket recv shutdown?
|
|
if (vs->isShutdownRecv)
|
|
{
|
|
// return with error
|
|
_setSockError(WU_SO_ESHUTDOWN);
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
// use select to check for exceptions and read data
|
|
fd_set fd_read;
|
|
fd_set fd_exceptions;
|
|
FD_ZERO(&fd_read);
|
|
FD_ZERO(&fd_exceptions);
|
|
FD_SET(vs->s, &fd_read);
|
|
FD_SET(vs->s, &fd_exceptions);
|
|
timeval t;
|
|
t.tv_sec = 0;
|
|
t.tv_usec = 0;
|
|
int nfds = 0;
|
|
#if BOOST_OS_UNIX
|
|
nfds = vs->s + 1;
|
|
#endif
|
|
sint32 count = select(nfds, &fd_read, NULL, &fd_exceptions, &t);
|
|
if (count > 0)
|
|
{
|
|
if (FD_ISSET(vs->s, &fd_exceptions))
|
|
{
|
|
assert_dbg(); // exception
|
|
}
|
|
if (FD_ISSET(vs->s, &fd_read))
|
|
{
|
|
// data available
|
|
r = recvfrom(vs->s, msg, len, hostFlags, &fromAddrHost, &fromLenHost);
|
|
wsaError = GETLASTERR;
|
|
if (r < 0)
|
|
cemu_assert_debug(false);
|
|
forceLogDebug_printf("recvfrom returned %d bytes", r);
|
|
*fromLen = fromLenHost;
|
|
fromAddr->sa_family = _swapEndianU16(fromAddrHost.sa_family);
|
|
memcpy(fromAddr->sa_data, fromAddrHost.sa_data, 14);
|
|
|
|
_setSockError(0);
|
|
osLib_returnFromFunction(hCPU, r);
|
|
return;
|
|
}
|
|
}
|
|
// nothing to do
|
|
if (requestIsNonBlocking)
|
|
{
|
|
// return with error
|
|
_setSockError(WU_SO_EWOULDBLOCK);
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// pause for a while and check again later
|
|
coreinit::OSSleepTicks(ESPRESSO_CORE_CLOCK / 1000); // pause for 1ms
|
|
PPCCore_switchToScheduler();
|
|
}
|
|
}
|
|
assert_dbg(); // should no longer be reached
|
|
|
|
if (requestIsNonBlocking == false)
|
|
{
|
|
// blocking
|
|
_setSocketSendRecvNonBlockingMode(vs->s, true);
|
|
while (true)
|
|
{
|
|
r = recvfrom(vs->s, msg, len, hostFlags, &fromAddrHost, &fromLenHost);
|
|
wsaError = GETLASTERR;
|
|
if (r < 0)
|
|
{
|
|
if (wsaError != WSAEWOULDBLOCK)
|
|
break;
|
|
coreinit::OSSleepTicks(ESPRESSO_CORE_CLOCK/100); // pause for 10ms
|
|
PPCCore_switchToScheduler();
|
|
continue;
|
|
}
|
|
assert_dbg();
|
|
}
|
|
_setSocketSendRecvNonBlockingMode(vs->s, vs->isNonBlocking);
|
|
}
|
|
else
|
|
{
|
|
// non blocking
|
|
assert_dbg();
|
|
}
|
|
|
|
*fromLen = fromLenHost;
|
|
fromAddr->sa_family = _swapEndianU16(fromAddrHost.sa_family);
|
|
memcpy(fromAddr->sa_data, fromAddrHost.sa_data, 14);
|
|
|
|
_translateError(r <= 0 ? -1 : 0, wsaError);
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
|
|
void nsysnetExport_recvfrom_ex(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("recvfrom_ex(%d,0x%08x,%d,0x%x,0x%08x,0x%08x,0x%08x,%d) LR: 0x%08x Thread: 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8], hCPU->gpr[9], hCPU->gpr[10], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStr(msg, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
ppcDefineParamS32(flags, 3);
|
|
ppcDefineParamStructPtr(fromAddr, wu_sockaddr, 4);
|
|
ppcDefineParamU32BEPtr(fromLen, 5);
|
|
ppcDefineParamUStr(extraData, 6);
|
|
ppcDefineParamS32(extraDataLen, 7);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
cemu_assert_debug(false);
|
|
return;
|
|
}
|
|
|
|
int hostFlags = 0;
|
|
bool requestIsNonBlocking = (flags&WU_MSG_DONTWAIT) != 0;
|
|
flags &= ~WU_MSG_DONTWAIT;
|
|
if (flags&WU_MSG_PEEK)
|
|
{
|
|
cemu_assert_debug(false);
|
|
hostFlags |= MSG_PEEK;
|
|
flags &= ~WU_MSG_PEEK;
|
|
}
|
|
if (flags & 0x40)
|
|
{
|
|
// read TTL
|
|
if (extraDataLen != 1)
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
extraData[0] = 0x5; // we currently always return 5 as TTL
|
|
flags &= ~0x40;
|
|
}
|
|
|
|
if (vs->isNonBlocking)
|
|
requestIsNonBlocking = vs->isNonBlocking;
|
|
|
|
socklen_t fromLenHost = *fromLen;
|
|
sockaddr fromAddrHost;
|
|
sint32 wsaError = 0;
|
|
|
|
while (true)
|
|
{
|
|
// is socket recv shutdown?
|
|
if (vs->isShutdownRecv)
|
|
{
|
|
// return with error
|
|
_setSockError(WU_SO_ESHUTDOWN);
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
// use select to check for exceptions and read data
|
|
fd_set fd_read;
|
|
fd_set fd_exceptions;
|
|
FD_ZERO(&fd_read);
|
|
FD_ZERO(&fd_exceptions);
|
|
FD_SET(vs->s, &fd_read);
|
|
FD_SET(vs->s, &fd_exceptions);
|
|
timeval t;
|
|
t.tv_sec = 0;
|
|
t.tv_usec = 0;
|
|
int nfds = 0;
|
|
#if BOOST_OS_UNIX
|
|
nfds = vs->s + 1;
|
|
#endif
|
|
sint32 count = select(nfds, &fd_read, NULL, &fd_exceptions, &t);
|
|
if (count > 0)
|
|
{
|
|
if (FD_ISSET(vs->s, &fd_exceptions))
|
|
{
|
|
cemu_assert_debug(false); // exception
|
|
}
|
|
if (FD_ISSET(vs->s, &fd_read))
|
|
{
|
|
// data available
|
|
r = recvfrom(vs->s, msg, len, hostFlags, &fromAddrHost, &fromLenHost);
|
|
wsaError = GETLASTERR;
|
|
if (r < 0)
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
*fromLen = fromLenHost;
|
|
fromAddr->sa_family = _swapEndianU16(fromAddrHost.sa_family);
|
|
memcpy(fromAddr->sa_data, fromAddrHost.sa_data, 14);
|
|
|
|
_setSockError(0);
|
|
osLib_returnFromFunction(hCPU, r);
|
|
return;
|
|
}
|
|
}
|
|
// nothing to do
|
|
if (requestIsNonBlocking)
|
|
{
|
|
// return with error
|
|
_setSockError(WU_SO_EWOULDBLOCK);
|
|
osLib_returnFromFunction(hCPU, -1);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// pause for a while and check again later
|
|
coreinit::OSSleepTicks(ESPRESSO_CORE_CLOCK / 100); // pause for 10ms
|
|
PPCCore_switchToScheduler();
|
|
}
|
|
}
|
|
cemu_assert_debug(false); // should no longer be reached
|
|
}
|
|
|
|
|
|
void _convertSockaddrToHostFormat(wu_sockaddr* sockaddru, sockaddr* sockaddrHost)
|
|
{
|
|
sockaddrHost->sa_family = _swapEndianU16(sockaddru->sa_family);
|
|
memcpy(sockaddrHost->sa_data, sockaddru->sa_data, 14);
|
|
}
|
|
|
|
|
|
void nsysnetExport_sendto(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("sendto(%d,0x%08x,%d,0x%x) LR: 0x%08x Thread: 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStr(msg, 1);
|
|
ppcDefineParamS32(len, 2);
|
|
ppcDefineParamS32(flags, 3);
|
|
ppcDefineParamStructPtr(toAddr, wu_sockaddr, 4);
|
|
ppcDefineParamU32(toLen, 5);
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
sint32 r = 0;
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
|
|
int hostFlags = 0;
|
|
bool requestIsNonBlocking = (flags&WU_MSG_DONTWAIT) != 0;
|
|
flags &= ~WU_MSG_DONTWAIT;
|
|
if (flags&WU_MSG_PEEK)
|
|
{
|
|
assert_dbg();
|
|
hostFlags |= MSG_PEEK;
|
|
flags &= ~WU_MSG_PEEK;
|
|
}
|
|
if (vs->isNonBlocking)
|
|
requestIsNonBlocking = vs->isNonBlocking;
|
|
|
|
sockaddr toAddrHost;
|
|
toAddrHost.sa_family = _swapEndianU16(toAddr->sa_family);
|
|
memcpy(toAddrHost.sa_data, toAddr->sa_data, 14);
|
|
|
|
sint32 wsaError = 0;
|
|
if (requestIsNonBlocking == false)
|
|
{
|
|
// blocking
|
|
_setSocketSendRecvNonBlockingMode(vs->s, true);
|
|
while (true)
|
|
{
|
|
r = sendto(vs->s, msg, len, hostFlags, &toAddrHost, toLen);
|
|
wsaError = GETLASTERR;
|
|
if (r < 0)
|
|
{
|
|
if (wsaError != WSAEWOULDBLOCK)
|
|
break;
|
|
coreinit::OSSleepTicks(ESPRESSO_CORE_CLOCK / 100); // pause for 10ms
|
|
PPCCore_switchToScheduler();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
_setSocketSendRecvNonBlockingMode(vs->s, vs->isNonBlocking);
|
|
}
|
|
else
|
|
{
|
|
// non blocking
|
|
_setSocketSendRecvNonBlockingMode(vs->s, true);
|
|
r = sendto(vs->s, msg, len, hostFlags, &toAddrHost, toLen);
|
|
wsaError = GETLASTERR;
|
|
_setSocketSendRecvNonBlockingMode(vs->s, vs->isNonBlocking);
|
|
}
|
|
|
|
_translateError(r <= 0 ? -1 : 0, wsaError);
|
|
|
|
osLib_returnFromFunction(hCPU, r);
|
|
}
|
|
|
|
|
|
void nsysnetExport_sendto_multi(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("sendto_multi(%d,0x%08x,0x%08x,%d) LR: 0x%08x Thread: 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamStr(data, 1);
|
|
ppcDefineParamS32(dataLen, 2);
|
|
ppcDefineParamU32(flags, 3);
|
|
ppcDefineParamStructPtr(destinationArray, wu_sockaddr, 4);
|
|
ppcDefineParamU32(destinationCount, 5);
|
|
|
|
if (flags != 0)
|
|
assert_dbg();
|
|
|
|
// todo - somehow handle non-blocking
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
|
|
for (uint32 i = 0; i < destinationCount; i++)
|
|
{
|
|
sockaddr destinationHost;
|
|
_convertSockaddrToHostFormat(&destinationArray[i], &destinationHost);
|
|
sint32 r = sendto(vs->s, (char*)data, (int)dataLen, 0, &destinationHost, sizeof(sockaddr));
|
|
if (r < dataLen)
|
|
assert_dbg(); // todo
|
|
}
|
|
// success
|
|
_setSockError(0);
|
|
osLib_returnFromFunction(hCPU, dataLen);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
MEMPTR<uint8> data;
|
|
uint32be dataLen;
|
|
MEMPTR<uint32be> sendLenArray;
|
|
uint32be sendLenArraySize;
|
|
MEMPTR<wu_sockaddr> destArray;
|
|
uint32be destArraySize;
|
|
MEMPTR<uint32be> resultArray;
|
|
uint32be resultArrayLen;
|
|
}sendtomultiBuffer_t;
|
|
|
|
void nsysnetExport_sendto_multi_ex(PPCInterpreter_t* hCPU)
|
|
{
|
|
socketLog_printf("sendto_multi_ex(%d,0x%08x,0x%08x,%d) LR: 0x%08x Thread: 0x%08x", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->spr.LR, coreinitThread_getCurrentThreadMPTRDepr(hCPU));
|
|
ppcDefineParamS32(s, 0);
|
|
ppcDefineParamU32(flags, 1);
|
|
ppcDefineParamStructPtr(multiBuf, sendtomultiBuffer_t, 2);
|
|
ppcDefineParamU32(num, 3);
|
|
|
|
assert_dbg(); // todo - needs testing
|
|
|
|
virtualSocket_t* vs = nsysnet_getVirtualSocketObject(s);
|
|
if (vs == NULL)
|
|
{
|
|
assert_dbg();
|
|
return;
|
|
}
|
|
|
|
// todo - lots of validation for multiBuf parameters (pointers must not be null and must be aligned, lens must be padded to alignment too)
|
|
// verify multiBuf
|
|
if ((uint32)multiBuf->sendLenArraySize < num ||
|
|
(uint32)multiBuf->destArraySize < num ||
|
|
(uint32)multiBuf->resultArrayLen < num )
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
|
|
for (uint32 i = 0; i < num; i++)
|
|
{
|
|
multiBuf->resultArray[i] = 0;
|
|
}
|
|
|
|
uint8* data = multiBuf->data.GetPtr();
|
|
sint32 sendLenSum = 0;
|
|
for (uint32 i = 0; i < num; i++)
|
|
{
|
|
sockaddr destination;
|
|
_convertSockaddrToHostFormat(&multiBuf->destArray[i], &destination);
|
|
|
|
uint32 sendLen = (uint32)(multiBuf->sendLenArray[i]);
|
|
sint32 r = sendto(vs->s, (char*)data, (int)sendLen, 0, &destination, sizeof(sockaddr));
|
|
if (r < (sint32)sendLen)
|
|
cemu_assert_debug(false);
|
|
else
|
|
multiBuf->resultArray[i] = r;
|
|
data += sendLen;
|
|
sendLenSum += sendLenSum;
|
|
}
|
|
osLib_returnFromFunction(hCPU, sendLenSum); // return value correct?
|
|
}
|
|
|
|
namespace nsysnet
|
|
{
|
|
std::vector<NSSLInternalState_t> g_nsslInternalStates;
|
|
|
|
NSSLInternalState_t* GetNSSLContext(sint32 index)
|
|
{
|
|
if (g_nsslInternalStates.size() <= index)
|
|
return nullptr;
|
|
|
|
if (g_nsslInternalStates[index].destroyed)
|
|
cemu_assert_suspicious();
|
|
|
|
return &g_nsslInternalStates[index];
|
|
}
|
|
|
|
void export_NSSLCreateContext(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(version, 0);
|
|
|
|
NSSLInternalState_t ssl = {};
|
|
ssl.sslVersion = version;
|
|
g_nsslInternalStates.push_back(ssl);
|
|
|
|
uint32 nsslHandle = (uint32)g_nsslInternalStates.size() - 1;
|
|
|
|
forceLogDebug_printf("NSSLCreateContext(0x%x) -> 0x%x", version, nsslHandle);
|
|
|
|
osLib_returnFromFunction(hCPU, nsslHandle);
|
|
}
|
|
|
|
void export_NSSLSetClientPKI(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(nsslHandle, 0);
|
|
ppcDefineParamU32(clientPKI, 1);
|
|
forceLogDebug_printf("NSSLSetClientPKI(0x%x, 0x%x)", nsslHandle, clientPKI);
|
|
|
|
if (g_nsslInternalStates.size() <= nsslHandle || g_nsslInternalStates[nsslHandle].destroyed)
|
|
{
|
|
osLib_returnFromFunction(hCPU, NSSL_INVALID_CTX);
|
|
return;
|
|
}
|
|
|
|
g_nsslInternalStates[nsslHandle].clientPKI = clientPKI;
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void export_NSSLAddServerPKI(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(nsslHandle, 0);
|
|
ppcDefineParamU32(serverPKI, 1);
|
|
forceLogDebug_printf("NSSLAddServerPKI(0x%x, 0x%x)", nsslHandle, serverPKI);
|
|
|
|
if (g_nsslInternalStates.size() <= nsslHandle || g_nsslInternalStates[nsslHandle].destroyed)
|
|
{
|
|
osLib_returnFromFunction(hCPU, NSSL_INVALID_CTX);
|
|
return;
|
|
}
|
|
|
|
g_nsslInternalStates[nsslHandle].serverPKIs.insert(serverPKI);
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void export_NSSLAddServerPKIExternal(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(nsslHandle, 0);
|
|
ppcDefineParamMEMPTR(certData, uint8, 1);
|
|
ppcDefineParamS32(certLen, 2);
|
|
ppcDefineParamS32(certType, 3);
|
|
|
|
forceLogDebug_printf("NSSLAddServerPKIExternal(0x%x, 0x%08x, 0x%x, %d)", nsslHandle, certData.GetMPTR(), certLen, certType);
|
|
if (g_nsslInternalStates.size() <= nsslHandle || g_nsslInternalStates[nsslHandle].destroyed)
|
|
{
|
|
osLib_returnFromFunction(hCPU, NSSL_INVALID_CTX);
|
|
return;
|
|
}
|
|
|
|
g_nsslInternalStates[nsslHandle].serverCustomPKIs.push_back(std::vector<uint8>(certData.GetPtr(), certData.GetPtr()+certLen));
|
|
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void export_NSSLAddServerPKIGroups(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(nsslHandle, 0);
|
|
ppcDefineParamU32(groupMask, 1);
|
|
ppcDefineParamMEMPTR(validCountOut, sint32, 2);
|
|
ppcDefineParamMEMPTR(invalidCountOut, sint32, 3);
|
|
forceLogDebug_printf("NSSLAddServerPKIGroups(0x%x, 0x%x, 0x%08x, 0x%08x)", nsslHandle, groupMask, validCountOut.GetMPTR(), invalidCountOut.GetMPTR());
|
|
|
|
if (g_nsslInternalStates.size() <= nsslHandle || g_nsslInternalStates[nsslHandle].destroyed)
|
|
{
|
|
osLib_returnFromFunction(hCPU, NSSL_INVALID_CTX);
|
|
return;
|
|
}
|
|
|
|
if (HAS_FLAG(groupMask, 1))
|
|
{
|
|
g_nsslInternalStates[nsslHandle].serverPKIs.insert({ 100,101,102,103,104,105 });
|
|
}
|
|
|
|
if (HAS_FLAG(groupMask, 2))
|
|
{
|
|
g_nsslInternalStates[nsslHandle].serverPKIs.insert({
|
|
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009,
|
|
1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019,
|
|
1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029,
|
|
1030, 1031, 1032, 1033 });
|
|
}
|
|
|
|
if (validCountOut)
|
|
*validCountOut = (sint32)g_nsslInternalStates[nsslHandle].serverPKIs.size();
|
|
|
|
if (invalidCountOut)
|
|
*invalidCountOut = 0;
|
|
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void export_NSSLDestroyContext(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(nsslHandle, 0);
|
|
forceLogDebug_printf("NSSLDestroyContext(0x%x)", nsslHandle);
|
|
|
|
if (g_nsslInternalStates.size() <= nsslHandle || g_nsslInternalStates[nsslHandle].destroyed)
|
|
{
|
|
osLib_returnFromFunction(hCPU, NSSL_INVALID_CTX);
|
|
return;
|
|
}
|
|
|
|
g_nsslInternalStates[nsslHandle].destroyed = true;
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void export_NSSLExportInternalServerCertificate(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(serverCertId, 0);
|
|
ppcDefineParamUStr(output, 1);
|
|
ppcDefineParamU32BEPtr(outputSize, 2);
|
|
ppcDefineParamU32BEPtr(certType, 3);
|
|
|
|
sint32 certificateSize = 0;
|
|
uint8* certificateData = iosuCrypto_getCertificateDataById(serverCertId, &certificateSize);
|
|
if (certificateData == nullptr)
|
|
{
|
|
cemu_assert_debug(false);
|
|
}
|
|
|
|
if( output )
|
|
memcpy(output, certificateData, certificateSize);
|
|
*outputSize = (uint32)certificateSize;
|
|
*certType = 0;
|
|
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void export_NSSLExportInternalClientCertificate(PPCInterpreter_t* hCPU)
|
|
{
|
|
ppcDefineParamU32(serverCertId, 0);
|
|
ppcDefineParamUStr(output, 1);
|
|
ppcDefineParamU32BEPtr(outputSize, 2);
|
|
ppcDefineParamU32BEPtr(certType, 3);
|
|
ppcDefineParamUStr(pkeyOutput, 4);
|
|
ppcDefineParamU32BEPtr(pkeyOutputSize, 5);
|
|
ppcDefineParamU32BEPtr(pkeyCertType, 6);
|
|
|
|
// certificate
|
|
sint32 certificateSize = 0;
|
|
uint8* certificateData = iosuCrypto_getCertificateDataById(serverCertId, &certificateSize);
|
|
if (certificateData == nullptr)
|
|
{
|
|
assert_dbg(); // todo
|
|
}
|
|
if (output)
|
|
memcpy(output, certificateData, certificateSize);
|
|
*outputSize = (uint32)certificateSize;
|
|
*certType = 0;
|
|
|
|
// private key
|
|
sint32 pkeySize = 0;
|
|
uint8* pkeyData = iosuCrypto_getCertificatePrivateKeyById(serverCertId, &pkeySize);
|
|
if (pkeyData == nullptr)
|
|
{
|
|
assert_dbg(); // todo
|
|
}
|
|
if (pkeyOutput)
|
|
memcpy(pkeyOutput, pkeyData, pkeySize);
|
|
*pkeyOutputSize = (uint32)pkeySize;
|
|
*pkeyCertType = 0;
|
|
|
|
osLib_returnFromFunction(hCPU, NSSL_OK);
|
|
}
|
|
|
|
void wuResetFD(struct wu_fd_set* fdset)
|
|
{
|
|
fdset->mask = 0;
|
|
}
|
|
|
|
void wuSetFD(struct wu_fd_set* fdset, sint32 fd)
|
|
{
|
|
fdset->mask |= (1 << fd);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// register nsysnet functions
|
|
void nsysnet_load()
|
|
{
|
|
|
|
osLib_addFunction("nsysnet", "socket_lib_init", nsysnetExport_socket_lib_init);
|
|
|
|
// socket API
|
|
osLib_addFunction("nsysnet", "socket", nsysnetExport_socket);
|
|
osLib_addFunction("nsysnet", "mw_socket", nsysnetExport_mw_socket);
|
|
osLib_addFunction("nsysnet", "shutdown", nsysnetExport_shutdown);
|
|
osLib_addFunction("nsysnet", "socketclose", nsysnetExport_socketclose);
|
|
osLib_addFunction("nsysnet", "setsockopt", nsysnetExport_setsockopt);
|
|
osLib_addFunction("nsysnet", "getsockopt", nsysnetExport_getsockopt);
|
|
osLib_addFunction("nsysnet", "bind", nsysnetExport_bind);
|
|
osLib_addFunction("nsysnet", "listen", nsysnetExport_listen);
|
|
osLib_addFunction("nsysnet", "accept", nsysnetExport_accept);
|
|
osLib_addFunction("nsysnet", "connect", nsysnetExport_connect);
|
|
osLib_addFunction("nsysnet", "send", nsysnetExport_send);
|
|
osLib_addFunction("nsysnet", "recv", nsysnetExport_recv);
|
|
osLib_addFunction("nsysnet", "select", nsysnetExport_select);
|
|
osLib_addFunction("nsysnet", "getsockname", nsysnetExport_getsockname);
|
|
osLib_addFunction("nsysnet", "getpeername", nsysnetExport_getpeername);
|
|
|
|
osLib_addFunction("nsysnet", "inet_aton", nsysnetExport_inet_aton);
|
|
osLib_addFunction("nsysnet", "inet_pton", nsysnetExport_inet_pton);
|
|
osLib_addFunction("nsysnet", "inet_ntoa", nsysnetExport_inet_ntoa);
|
|
osLib_addFunction("nsysnet", "htons", nsysnetExport_htons);
|
|
osLib_addFunction("nsysnet", "htonl", nsysnetExport_htonl);
|
|
osLib_addFunction("nsysnet", "ntohs", nsysnetExport_ntohs);
|
|
osLib_addFunction("nsysnet", "ntohl", nsysnetExport_ntohl);
|
|
osLib_addFunction("nsysnet", "gethostbyname", nsysnetExport_gethostbyname);
|
|
osLib_addFunction("nsysnet", "gethostbyaddr", nsysnetExport_gethostbyaddr);
|
|
osLib_addFunction("nsysnet", "getaddrinfo", nsysnetExport_getaddrinfo);
|
|
|
|
osLib_addFunction("nsysnet", "socketlasterr", nsysnetExport_socketlasterr);
|
|
|
|
// unfinished implementations
|
|
osLib_addFunction("nsysnet", "recvfrom", nsysnetExport_recvfrom);
|
|
osLib_addFunction("nsysnet", "recvfrom_ex", nsysnetExport_recvfrom_ex);
|
|
osLib_addFunction("nsysnet", "sendto", nsysnetExport_sendto);
|
|
|
|
osLib_addFunction("nsysnet", "sendto_multi", nsysnetExport_sendto_multi);
|
|
osLib_addFunction("nsysnet", "sendto_multi_ex", nsysnetExport_sendto_multi_ex);
|
|
|
|
|
|
// NSSL API
|
|
osLib_addFunction("nsysnet", "NSSLCreateContext", nsysnet::export_NSSLCreateContext);
|
|
osLib_addFunction("nsysnet", "NSSLSetClientPKI", nsysnet::export_NSSLSetClientPKI);
|
|
osLib_addFunction("nsysnet", "NSSLAddServerPKI", nsysnet::export_NSSLAddServerPKI);
|
|
osLib_addFunction("nsysnet", "NSSLAddServerPKIExternal", nsysnet::export_NSSLAddServerPKIExternal);
|
|
osLib_addFunction("nsysnet", "NSSLAddServerPKIGroups", nsysnet::export_NSSLAddServerPKIGroups);
|
|
osLib_addFunction("nsysnet", "NSSLDestroyContext", nsysnet::export_NSSLDestroyContext);
|
|
|
|
osLib_addFunction("nsysnet", "NSSLExportInternalServerCertificate", nsysnet::export_NSSLExportInternalServerCertificate);
|
|
osLib_addFunction("nsysnet", "NSSLExportInternalClientCertificate", nsysnet::export_NSSLExportInternalClientCertificate);
|
|
}
|