Cemu/src/Cafe/OS/libs/nsysnet/nsysnet.cpp
2022-11-15 17:03:46 +01:00

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);
}