Merge pull request #5432 from sepalani/fd-table

IOS/Network: Emulate socket fd table
This commit is contained in:
shuffle2 2017-06-03 21:58:41 -07:00 committed by GitHub
commit a2bd95a08e
5 changed files with 101 additions and 46 deletions

View File

@ -290,7 +290,7 @@ IPCCommandResult NetIPTop::HandleShutdownRequest(const IOCtlRequest& request)
u32 fd = Memory::Read_U32(request.buffer_in); u32 fd = Memory::Read_U32(request.buffer_in);
u32 how = Memory::Read_U32(request.buffer_in + 4); u32 how = Memory::Read_U32(request.buffer_in + 4);
int ret = shutdown(fd, how); int ret = shutdown(WiiSockMan::GetInstance().GetHostSocket(fd), how);
return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SHUTDOWN", false)); return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SHUTDOWN", false));
} }
@ -299,7 +299,7 @@ IPCCommandResult NetIPTop::HandleListenRequest(const IOCtlRequest& request)
{ {
u32 fd = Memory::Read_U32(request.buffer_in); u32 fd = Memory::Read_U32(request.buffer_in);
u32 BACKLOG = Memory::Read_U32(request.buffer_in + 0x04); u32 BACKLOG = Memory::Read_U32(request.buffer_in + 0x04);
u32 ret = listen(fd, BACKLOG); u32 ret = listen(WiiSockMan::GetInstance().GetHostSocket(fd), BACKLOG);
request.Log(GetDeviceName(), LogTypes::IOS_WC24); request.Log(GetDeviceName(), LogTypes::IOS_WC24);
return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_LISTEN", false)); return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_LISTEN", false));
@ -320,7 +320,8 @@ IPCCommandResult NetIPTop::HandleGetSockOptRequest(const IOCtlRequest& request)
u8 optval[20]; u8 optval[20];
u32 optlen = 4; u32 optlen = 4;
int ret = getsockopt(fd, nat_level, nat_optname, (char*)&optval, (socklen_t*)&optlen); int ret = getsockopt(WiiSockMan::GetInstance().GetHostSocket(fd), nat_level, nat_optname,
(char*)&optval, (socklen_t*)&optlen);
const s32 return_value = WiiSockMan::GetNetErrorCode(ret, "SO_GETSOCKOPT", false); const s32 return_value = WiiSockMan::GetNetErrorCode(ret, "SO_GETSOCKOPT", false);
Memory::Write_U32(optlen, request.buffer_out + 0xC); Memory::Write_U32(optlen, request.buffer_out + 0xC);
@ -366,7 +367,8 @@ IPCCommandResult NetIPTop::HandleSetSockOptRequest(const IOCtlRequest& request)
int nat_level = MapWiiSockOptLevelToNative(level); int nat_level = MapWiiSockOptLevelToNative(level);
int nat_optname = MapWiiSockOptNameToNative(optname); int nat_optname = MapWiiSockOptNameToNative(optname);
int ret = setsockopt(fd, nat_level, nat_optname, (char*)optval, optlen); int ret = setsockopt(WiiSockMan::GetInstance().GetHostSocket(fd), nat_level, nat_optname,
(char*)optval, optlen);
return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SETSOCKOPT", false)); return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SETSOCKOPT", false));
} }
@ -378,7 +380,7 @@ IPCCommandResult NetIPTop::HandleGetSockNameRequest(const IOCtlRequest& request)
sockaddr sa; sockaddr sa;
socklen_t sa_len = sizeof(sa); socklen_t sa_len = sizeof(sa);
int ret = getsockname(fd, &sa, &sa_len); int ret = getsockname(WiiSockMan::GetInstance().GetHostSocket(fd), &sa, &sa_len);
if (request.buffer_out_size < 2 + sizeof(sa.sa_data)) if (request.buffer_out_size < 2 + sizeof(sa.sa_data))
WARN_LOG(IOS_NET, "IOCTL_SO_GETSOCKNAME output buffer is too small. Truncating"); WARN_LOG(IOS_NET, "IOCTL_SO_GETSOCKNAME output buffer is too small. Truncating");
@ -402,7 +404,7 @@ IPCCommandResult NetIPTop::HandleGetPeerNameRequest(const IOCtlRequest& request)
sockaddr sa; sockaddr sa;
socklen_t sa_len = sizeof(sa); socklen_t sa_len = sizeof(sa);
int ret = getpeername(fd, &sa, &sa_len); int ret = getpeername(WiiSockMan::GetInstance().GetHostSocket(fd), &sa, &sa_len);
if (request.buffer_out_size < 2 + sizeof(sa.sa_data)) if (request.buffer_out_size < 2 + sizeof(sa.sa_data))
WARN_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME output buffer is too small. Truncating"); WARN_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME output buffer is too small. Truncating");
@ -555,7 +557,8 @@ IPCCommandResult NetIPTop::HandlePollRequest(const IOCtlRequest& request)
for (int i = 0; i < nfds; ++i) for (int i = 0; i < nfds; ++i)
{ {
ufds[i].fd = Memory::Read_U32(request.buffer_out + 0xc * i); // fd s32 wii_fd = Memory::Read_U32(request.buffer_out + 0xc * i);
ufds[i].fd = WiiSockMan::GetInstance().GetHostSocket(wii_fd); // fd
int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4); // events int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4); // events
ufds[i].revents = Memory::Read_U32(request.buffer_out + 0xc * i + 8); // revents ufds[i].revents = Memory::Read_U32(request.buffer_out + 0xc * i + 8); // revents
@ -571,7 +574,7 @@ IPCCommandResult NetIPTop::HandlePollRequest(const IOCtlRequest& request)
DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL(%d) " DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL(%d) "
"Sock: %08x, Unknown: %08x, Events: %08x, " "Sock: %08x, Unknown: %08x, Events: %08x, "
"NativeEvents: %08x", "NativeEvents: %08x",
i, ufds[i].fd, unknown, events, ufds[i].events); i, wii_fd, unknown, events, ufds[i].events);
// Do not pass return-only events to the native poll // Do not pass return-only events to the native poll
ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL); ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL);
@ -1009,10 +1012,11 @@ IPCCommandResult NetIPTop::HandleICMPPingRequest(const IOCtlVRequest& request)
icmp_length = 22; icmp_length = 22;
} }
int ret = icmp_echo_req(fd, &addr, data, icmp_length); int ret = icmp_echo_req(WiiSockMan::GetInstance().GetHostSocket(fd), &addr, data, icmp_length);
if (ret == icmp_length) if (ret == icmp_length)
{ {
ret = icmp_echo_rep(fd, &addr, (u32)timeout, icmp_length); ret = icmp_echo_rep(WiiSockMan::GetInstance().GetHostSocket(fd), &addr,
static_cast<u32>(timeout), icmp_length);
} }
// TODO proper error codes // TODO proper error codes

View File

@ -424,8 +424,10 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
WII_SSL* ssl = &_SSL[sslID]; WII_SSL* ssl = &_SSL[sslID];
mbedtls_ssl_setup(&ssl->ctx, &ssl->config); mbedtls_ssl_setup(&ssl->ctx, &ssl->config);
ssl->sockfd = Memory::Read_U32(BufferOut2); ssl->sockfd = Memory::Read_U32(BufferOut2);
WiiSockMan& sm = WiiSockMan::GetInstance();
ssl->hostfd = sm.GetHostSocket(ssl->sockfd);
INFO_LOG(IOS_SSL, "IOCTLV_NET_SSL_CONNECT socket = %d", ssl->sockfd); INFO_LOG(IOS_SSL, "IOCTLV_NET_SSL_CONNECT socket = %d", ssl->sockfd);
mbedtls_ssl_set_bio(&ssl->ctx, &ssl->sockfd, mbedtls_net_send, mbedtls_net_recv, nullptr); mbedtls_ssl_set_bio(&ssl->ctx, &ssl->hostfd, mbedtls_net_send, mbedtls_net_recv, nullptr);
Memory::Write_U32(SSL_OK, BufferIn); Memory::Write_U32(SSL_OK, BufferIn);
} }
else else

View File

@ -81,6 +81,7 @@ struct WII_SSL
mbedtls_x509_crt clicert; mbedtls_x509_crt clicert;
mbedtls_pk_context pk; mbedtls_pk_context pk;
int sockfd; int sockfd;
int hostfd;
std::string hostname; std::string hostname;
bool active; bool active;
}; };

View File

@ -25,12 +25,15 @@
#else #else
#define ERRORCODE(name) name #define ERRORCODE(name) name
#define EITHER(win32, posix) posix #define EITHER(win32, posix) posix
#define closesocket close
#endif #endif
namespace IOS namespace IOS
{ {
namespace HLE namespace HLE
{ {
constexpr int WII_SOCKET_FD_MAX = 24;
char* WiiSockMan::DecodeError(s32 ErrorCode) char* WiiSockMan::DecodeError(s32 ErrorCode)
{ {
#ifdef _WIN32 #ifdef _WIN32
@ -77,6 +80,11 @@ static s32 TranslateErrorCode(s32 native_error, bool isRW)
return -SO_ENETUNREACH; return -SO_ENETUNREACH;
case ERRORCODE(EHOSTUNREACH): case ERRORCODE(EHOSTUNREACH):
return -SO_EHOSTUNREACH; return -SO_EHOSTUNREACH;
case ENOMEM: // See man (7) ip
case ERRORCODE(ENOBUFS):
return -SO_ENOMEM;
case ERRORCODE(ENETRESET):
return -SO_ENETRESET;
case EITHER(WSAEWOULDBLOCK, EAGAIN): case EITHER(WSAEWOULDBLOCK, EAGAIN):
if (isRW) if (isRW)
{ {
@ -143,16 +151,17 @@ void WiiSocket::SetFd(s32 s)
#endif #endif
} }
void WiiSocket::SetWiiFd(s32 s)
{
wii_fd = s;
}
s32 WiiSocket::CloseFd() s32 WiiSocket::CloseFd()
{ {
s32 ReturnValue = 0; s32 ReturnValue = 0;
if (fd >= 0) if (fd >= 0)
{ {
#ifdef _WIN32
s32 ret = closesocket(fd); s32 ret = closesocket(fd);
#else
s32 ret = close(fd);
#endif
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "CloseFd", false); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "CloseFd", false);
} }
else else
@ -186,7 +195,7 @@ s32 WiiSocket::FCntl(u32 cmd, u32 arg)
ERROR_LOG(IOS_NET, "SO_FCNTL unknown command"); ERROR_LOG(IOS_NET, "SO_FCNTL unknown command");
} }
INFO_LOG(IOS_NET, "IOCTL_SO_FCNTL(%08x, %08X, %08X)", fd, cmd, arg); INFO_LOG(IOS_NET, "IOCTL_SO_FCNTL(%08x, %08X, %08X)", wii_fd, cmd, arg);
return ret; return ret;
} }
@ -220,8 +229,8 @@ void WiiSocket::Update(bool read, bool write, bool except)
int ret = bind(fd, (sockaddr*)&local_name, sizeof(local_name)); int ret = bind(fd, (sockaddr*)&local_name, sizeof(local_name));
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_BIND", false); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_BIND", false);
INFO_LOG(IOS_NET, "IOCTL_SO_BIND (%08X %s:%d) = %d", fd, inet_ntoa(local_name.sin_addr), INFO_LOG(IOS_NET, "IOCTL_SO_BIND (%08X, %s:%d) = %d", wii_fd,
Common::swap16(local_name.sin_port), ret); inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port), ret);
break; break;
} }
case IOCTL_SO_CONNECT: case IOCTL_SO_CONNECT:
@ -233,12 +242,13 @@ void WiiSocket::Update(bool read, bool write, bool except)
int ret = connect(fd, (sockaddr*)&local_name, sizeof(local_name)); int ret = connect(fd, (sockaddr*)&local_name, sizeof(local_name));
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_CONNECT", false); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_CONNECT", false);
INFO_LOG(IOS_NET, "IOCTL_SO_CONNECT (%08x, %s:%d) = %d", fd, inet_ntoa(local_name.sin_addr), INFO_LOG(IOS_NET, "IOCTL_SO_CONNECT (%08x, %s:%d) = %d", wii_fd,
Common::swap16(local_name.sin_port), ret); inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port), ret);
break; break;
} }
case IOCTL_SO_ACCEPT: case IOCTL_SO_ACCEPT:
{ {
s32 ret;
if (ioctl.buffer_out_size > 0) if (ioctl.buffer_out_size > 0)
{ {
sockaddr_in local_name; sockaddr_in local_name;
@ -246,18 +256,16 @@ void WiiSocket::Update(bool read, bool write, bool except)
WiiSockMan::Convert(*wii_name, local_name); WiiSockMan::Convert(*wii_name, local_name);
socklen_t addrlen = sizeof(sockaddr_in); socklen_t addrlen = sizeof(sockaddr_in);
int ret = (s32)accept(fd, (sockaddr*)&local_name, &addrlen); ret = static_cast<s32>(accept(fd, (sockaddr*)&local_name, &addrlen));
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_ACCEPT", true);
WiiSockMan::Convert(local_name, *wii_name, addrlen); WiiSockMan::Convert(local_name, *wii_name, addrlen);
} }
else else
{ {
int ret = (s32)accept(fd, nullptr, nullptr); ret = static_cast<s32>(accept(fd, nullptr, nullptr));
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_ACCEPT", true);
} }
WiiSockMan::GetInstance().AddSocket(ReturnValue); ReturnValue = WiiSockMan::GetInstance().AddSocket(ret, true);
ioctl.Log("IOCTL_SO_ACCEPT", LogTypes::IOS_NET); ioctl.Log("IOCTL_SO_ACCEPT", LogTypes::IOS_NET);
break; break;
@ -382,9 +390,10 @@ void WiiSocket::Update(bool read, bool write, bool except)
} }
} }
INFO_LOG(IOS_SSL, "IOCTLV_NET_SSL_DOHANDSHAKE = (%d) " INFO_LOG(IOS_SSL,
"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), " "IOCTLV_NET_SSL_DOHANDSHAKE = (%d) "
"BufferOut: (%08x, %i), BufferOut2: (%08x, %i)", "BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
"BufferOut: (%08x, %i), BufferOut2: (%08x, %i)",
ret, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut, ret, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut,
BufferOutSize, BufferOut2, BufferOutSize2); BufferOutSize, BufferOut2, BufferOutSize2);
break; break;
@ -506,7 +515,7 @@ void WiiSocket::Update(bool read, bool write, bool except)
DEBUG_LOG( DEBUG_LOG(
IOS_NET, IOS_NET,
"%s = %d Socket: %08x, BufferIn: (%08x, %i), BufferIn2: (%08x, %i), %u.%u.%u.%u", "%s = %d Socket: %08x, BufferIn: (%08x, %i), BufferIn2: (%08x, %i), %u.%u.%u.%u",
has_destaddr ? "IOCTLV_SO_SENDTO " : "IOCTLV_SO_SEND ", ReturnValue, fd, BufferIn, has_destaddr ? "IOCTLV_SO_SENDTO " : "IOCTLV_SO_SEND ", ReturnValue, wii_fd, BufferIn,
BufferInSize, BufferIn2, BufferInSize2, local_name.sin_addr.s_addr & 0xFF, BufferInSize, BufferIn2, BufferInSize2, local_name.sin_addr.s_addr & 0xFF,
(local_name.sin_addr.s_addr >> 8) & 0xFF, (local_name.sin_addr.s_addr >> 16) & 0xFF, (local_name.sin_addr.s_addr >> 8) & 0xFF, (local_name.sin_addr.s_addr >> 16) & 0xFF,
(local_name.sin_addr.s_addr >> 24) & 0xFF); (local_name.sin_addr.s_addr >> 24) & 0xFF);
@ -549,11 +558,12 @@ void WiiSocket::Update(bool read, bool write, bool except)
ReturnValue = ReturnValue =
WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true); WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true);
INFO_LOG(IOS_NET, "%s(%d, %p) Socket: %08X, Flags: %08X, " INFO_LOG(IOS_NET,
"BufferIn: (%08x, %i), BufferIn2: (%08x, %i), " "%s(%d, %p) Socket: %08X, Flags: %08X, "
"BufferOut: (%08x, %i), BufferOut2: (%08x, %i)", "BufferIn: (%08x, %i), BufferIn2: (%08x, %i), "
"BufferOut: (%08x, %i), BufferOut2: (%08x, %i)",
BufferOutSize2 ? "IOCTLV_SO_RECVFROM " : "IOCTLV_SO_RECV ", ReturnValue, data, BufferOutSize2 ? "IOCTLV_SO_RECVFROM " : "IOCTLV_SO_RECV ", ReturnValue, data,
fd, flags, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut, wii_fd, flags, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut,
BufferOutSize, BufferOut2, BufferOutSize2); BufferOutSize, BufferOut2, BufferOutSize2);
if (BufferOutSize2 != 0) if (BufferOutSize2 != 0)
@ -575,8 +585,8 @@ void WiiSocket::Update(bool read, bool write, bool except)
(it->is_ssl && ReturnValue != SSL_ERR_WAGAIN && ReturnValue != SSL_ERR_RAGAIN)) (it->is_ssl && ReturnValue != SSL_ERR_WAGAIN && ReturnValue != SSL_ERR_RAGAIN))
{ {
DEBUG_LOG(IOS_NET, DEBUG_LOG(IOS_NET,
"IOCTL(V) Sock: %08x ioctl/v: %d returned: %d nonBlock: %d forceNonBlock: %d", fd, "IOCTL(V) Sock: %08x ioctl/v: %d returned: %d nonBlock: %d forceNonBlock: %d",
it->is_ssl ? (int)it->ssl_type : (int)it->net_type, ReturnValue, nonBlock, wii_fd, it->is_ssl ? (int)it->ssl_type : (int)it->net_type, ReturnValue, nonBlock,
forceNonBlock); forceNonBlock);
// TODO: remove the dependency on a running IOS instance. // TODO: remove the dependency on a running IOS instance.
@ -604,26 +614,61 @@ void WiiSocket::DoSock(Request request, SSL_IOCTL type)
pending_sockops.push_back(so); pending_sockops.push_back(so);
} }
void WiiSockMan::AddSocket(s32 fd) s32 WiiSockMan::AddSocket(s32 fd, bool is_rw)
{ {
if (fd >= 0) const char* caller = is_rw ? "SO_ACCEPT" : "NewSocket";
if (fd < 0)
return GetNetErrorCode(fd, caller, is_rw);
s32 wii_fd;
for (wii_fd = 0; wii_fd < WII_SOCKET_FD_MAX; ++wii_fd)
{ {
WiiSocket& sock = WiiSockets[fd]; // Find an available socket fd
sock.SetFd(fd); if (WiiSockets.count(wii_fd) == 0)
break;
} }
if (wii_fd == WII_SOCKET_FD_MAX)
{
// Close host socket
closesocket(fd);
wii_fd = -SO_EMFILE;
ERROR_LOG(IOS_NET, "%s failed: Too many open sockets, ret=%d", caller, wii_fd);
}
else
{
WiiSocket& sock = WiiSockets[wii_fd];
sock.SetFd(fd);
sock.SetWiiFd(wii_fd);
}
SetLastNetError(wii_fd);
return wii_fd;
} }
s32 WiiSockMan::NewSocket(s32 af, s32 type, s32 protocol) s32 WiiSockMan::NewSocket(s32 af, s32 type, s32 protocol)
{ {
s32 fd = (s32)socket(af, type, protocol); if (af != 2 && af != 23) // AF_INET && AF_INET6
s32 ret = GetNetErrorCode(fd, "NewSocket", false); return -SO_EAFNOSUPPORT;
AddSocket(ret); if (protocol != 0) // IPPROTO_IP
return ret; return -SO_EPROTONOSUPPORT;
if (type != 1 && type != 2) // SOCK_STREAM && SOCK_DGRAM
return -SO_EPROTOTYPE;
s32 fd = static_cast<s32>(socket(af, type, protocol));
return AddSocket(fd, false);
}
s32 WiiSockMan::GetHostSocket(s32 wii_fd) const
{
if (WiiSockets.count(wii_fd) > 0)
return WiiSockets.at(wii_fd).fd;
return -EBADF;
} }
s32 WiiSockMan::DeleteSocket(s32 s) s32 WiiSockMan::DeleteSocket(s32 s)
{ {
s32 ReturnValue = EBADF; s32 ReturnValue = -SO_EBADF;
auto socket_entry = WiiSockets.find(s); auto socket_entry = WiiSockets.find(s);
if (socket_entry != WiiSockets.end()) if (socket_entry != WiiSockets.end())
{ {
@ -694,7 +739,7 @@ void WiiSockMan::Convert(sockaddr_in const& from, WiiSockAddrIn& to, s32 addrlen
to.addr.addr = from.sin_addr.s_addr; to.addr.addr = from.sin_addr.s_addr;
to.family = from.sin_family & 0xFF; to.family = from.sin_family & 0xFF;
to.port = from.sin_port; to.port = from.sin_port;
if (addrlen < 0 || addrlen > (s32)sizeof(WiiSockAddrIn)) if (addrlen < 0 || addrlen > static_cast<s32>(sizeof(WiiSockAddrIn)))
to.len = sizeof(WiiSockAddrIn); to.len = sizeof(WiiSockAddrIn);
else else
to.len = addrlen; to.len = addrlen;

View File

@ -187,11 +187,13 @@ class WiiSocket
private: private:
s32 fd; s32 fd;
s32 wii_fd;
bool nonBlock; bool nonBlock;
std::list<sockop> pending_sockops; std::list<sockop> pending_sockops;
friend class WiiSockMan; friend class WiiSockMan;
void SetFd(s32 s); void SetFd(s32 s);
void SetWiiFd(s32 s);
s32 CloseFd(); s32 CloseFd();
s32 FCntl(u32 cmd, u32 arg); s32 FCntl(u32 cmd, u32 arg);
@ -221,7 +223,8 @@ public:
static void Convert(sockaddr_in const& from, WiiSockAddrIn& to, s32 addrlen = -1); static void Convert(sockaddr_in const& from, WiiSockAddrIn& to, s32 addrlen = -1);
// NON-BLOCKING FUNCTIONS // NON-BLOCKING FUNCTIONS
s32 NewSocket(s32 af, s32 type, s32 protocol); s32 NewSocket(s32 af, s32 type, s32 protocol);
void AddSocket(s32 fd); s32 AddSocket(s32 fd, bool is_rw);
s32 GetHostSocket(s32 wii_fd) const;
s32 DeleteSocket(s32 s); s32 DeleteSocket(s32 s);
s32 GetLastNetError() const { return errno_last; } s32 GetLastNetError() const { return errno_last; }
void SetLastNetError(s32 error) { errno_last = error; } void SetLastNetError(s32 error) { errno_last = error; }