[Kernel] Add Linux socket support and ensure binding low ports is remapped.
Remapping ports on both Linux and Windows to ensure games boot under wine as well.
This commit is contained in:
committed by
Radosław Gliński
parent
344e8538e0
commit
4af2fd6513
@@ -248,38 +248,42 @@ DECLARE_XAM_EXPORT1(NetDll_XNetRandom, kNetworking, kStub);
|
||||
|
||||
dword_result_t NetDll_WSAStartup_entry(dword_t caller, word_t version,
|
||||
pointer_t<X_WSADATA> data_ptr) {
|
||||
// TODO(benvanik): abstraction layer needed.
|
||||
// TODO(benvanik): abstraction layer needed.
|
||||
int ret = 0;
|
||||
|
||||
#ifdef XE_PLATFORM_WIN32
|
||||
WSADATA wsaData;
|
||||
ZeroMemory(&wsaData, sizeof(WSADATA));
|
||||
int ret = WSAStartup(version, &wsaData);
|
||||
|
||||
auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr);
|
||||
ret = WSAStartup(version, &wsaData);
|
||||
#endif
|
||||
|
||||
if (data_ptr) {
|
||||
auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr);
|
||||
|
||||
#ifdef XE_PLATFORM_WIN32
|
||||
data_ptr->version = wsaData.wVersion;
|
||||
data_ptr->version_high = wsaData.wHighVersion;
|
||||
std::memcpy(&data_ptr->description, wsaData.szDescription, 0x100);
|
||||
std::memcpy(&data_ptr->system_status, wsaData.szSystemStatus, 0x80);
|
||||
data_ptr->max_sockets = wsaData.iMaxSockets;
|
||||
data_ptr->max_udpdg = wsaData.iMaxUdpDg;
|
||||
std::memcpy(&data_ptr->description, wsaData.szDescription, 0x100);
|
||||
std::memcpy(&data_ptr->system_status, wsaData.szSystemStatus, 0x80);
|
||||
#else
|
||||
// Match Windows behavior with reasonable values
|
||||
data_ptr->version = version.value();
|
||||
data_ptr->version_high = version.value();
|
||||
data_ptr->max_sockets = 100;
|
||||
data_ptr->max_udpdg = 1024;
|
||||
// WinSock 2.2 typically returns empty strings for these fields
|
||||
std::memset(&data_ptr->description, 0, 0x100);
|
||||
std::memset(&data_ptr->system_status, 0, 0x80);
|
||||
#endif
|
||||
|
||||
// Some games (5841099F) want this value round-tripped - they'll compare if
|
||||
// it changes and bugcheck if it does.
|
||||
uint32_t vendor_ptr = xe::load_and_swap<uint32_t>(data_out + 0x190);
|
||||
xe::store_and_swap<uint32_t>(data_out + 0x190, vendor_ptr);
|
||||
// vendor_info_ptr is at offset 0x18A (after max_udpdg at 0x188)
|
||||
uint32_t vendor_ptr = xe::load_and_swap<uint32_t>(data_out + 0x18A);
|
||||
xe::store_and_swap<uint32_t>(data_out + 0x18A, vendor_ptr);
|
||||
}
|
||||
#else
|
||||
int ret = 0;
|
||||
if (data_ptr) {
|
||||
// Guess these values!
|
||||
data_ptr->version = version.value();
|
||||
data_ptr->description[0] = '\0';
|
||||
data_ptr->system_status[0] = '\0';
|
||||
data_ptr->max_sockets = 100;
|
||||
data_ptr->max_udpdg = 1024;
|
||||
}
|
||||
#endif
|
||||
|
||||
// DEBUG
|
||||
/*
|
||||
@@ -659,6 +663,8 @@ dword_result_t NetDll_socket_entry(dword_t caller, dword_t af, dword_t type,
|
||||
socket->Release();
|
||||
|
||||
XThread::SetLastError(socket->GetLastWSAError());
|
||||
XELOGE("NetDll_socket: failed with error {:08X}",
|
||||
socket->GetLastWSAError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -742,6 +748,8 @@ dword_result_t NetDll_ioctlsocket_entry(dword_t caller, dword_t socket_handle,
|
||||
X_STATUS status = socket->IOControl(cmd, arg_ptr);
|
||||
if (XFAILED(status)) {
|
||||
XThread::SetLastError(socket->GetLastWSAError());
|
||||
XELOGE("NetDll_ioctlsocket: failed with error {:08X}",
|
||||
socket->GetLastWSAError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -757,6 +765,7 @@ dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle,
|
||||
kernel_state()->object_table()->LookupObject<XSocket>(socket_handle);
|
||||
if (!socket) {
|
||||
XThread::SetLastError(uint32_t(X_WSAError::X_WSAENOTSOCK));
|
||||
XELOGE("NetDll_bind: failed - invalid socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -764,6 +773,7 @@ dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle,
|
||||
X_STATUS status = socket->Bind(&native_name, namelen);
|
||||
if (XFAILED(status)) {
|
||||
XThread::SetLastError(socket->GetLastWSAError());
|
||||
XELOGE("NetDll_bind: failed with status {:08X}", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1027,7 +1037,8 @@ dword_result_t NetDll_sendto_entry(dword_t caller, dword_t socket_handle,
|
||||
}
|
||||
|
||||
N_XSOCKADDR_IN native_to(to_ptr);
|
||||
return socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len);
|
||||
int ret = socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len);
|
||||
return ret;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(NetDll_sendto, kNetworking, kImplemented);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/xam/xam_module.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -91,10 +93,73 @@ X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr,
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int ret =
|
||||
setsockopt(native_handle_, level, optname, (char*)optval_ptr, optlen);
|
||||
// Translate Xbox socket level to native
|
||||
int native_level = level;
|
||||
if (level == 0xFFFF) { // Xbox SOL_SOCKET
|
||||
native_level = SOL_SOCKET;
|
||||
}
|
||||
|
||||
// Translate Xbox socket options to native
|
||||
// Xbox uses Winsock constants which mostly match standard values
|
||||
int native_optname = optname;
|
||||
if (level == 0xFFFF) {
|
||||
switch (optname) {
|
||||
case 0x0001: // SO_DEBUG
|
||||
native_optname = SO_DEBUG;
|
||||
break;
|
||||
case 0x0002: // SO_ACCEPTCONN
|
||||
native_optname = SO_ACCEPTCONN;
|
||||
break;
|
||||
case 0x0004: // SO_REUSEADDR
|
||||
native_optname = SO_REUSEADDR;
|
||||
break;
|
||||
case 0x0008: // SO_KEEPALIVE
|
||||
native_optname = SO_KEEPALIVE;
|
||||
break;
|
||||
case 0x0010: // SO_DONTROUTE
|
||||
native_optname = SO_DONTROUTE;
|
||||
break;
|
||||
case 0x0020: // SO_BROADCAST
|
||||
native_optname = SO_BROADCAST;
|
||||
break;
|
||||
case 0x0080: // SO_LINGER
|
||||
native_optname = SO_LINGER;
|
||||
break;
|
||||
case 0x0100: // SO_OOBINLINE
|
||||
native_optname = SO_OOBINLINE;
|
||||
break;
|
||||
case 0x1001: // SO_SNDBUF
|
||||
native_optname = SO_SNDBUF;
|
||||
break;
|
||||
case 0x1002: // SO_RCVBUF
|
||||
native_optname = SO_RCVBUF;
|
||||
break;
|
||||
case 0x1003: // SO_SNDLOWAT
|
||||
native_optname = SO_SNDLOWAT;
|
||||
break;
|
||||
case 0x1004: // SO_RCVLOWAT
|
||||
native_optname = SO_RCVLOWAT;
|
||||
break;
|
||||
case 0x1005: // SO_SNDTIMEO
|
||||
native_optname = SO_SNDTIMEO;
|
||||
break;
|
||||
case 0x1006: // SO_RCVTIMEO
|
||||
native_optname = SO_RCVTIMEO;
|
||||
break;
|
||||
case 0x1007: // SO_ERROR
|
||||
native_optname = SO_ERROR;
|
||||
break;
|
||||
case 0x1008: // SO_TYPE
|
||||
native_optname = SO_TYPE;
|
||||
break;
|
||||
// Add more translations as needed
|
||||
}
|
||||
}
|
||||
|
||||
int ret = setsockopt(native_handle_, native_level, native_optname,
|
||||
static_cast<char*>(optval_ptr), optlen);
|
||||
if (ret < 0) {
|
||||
// TODO: WSAGetLastError()
|
||||
XELOGE("XSocket::SetOption: setsockopt failed, errno={}", errno);
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
@@ -113,10 +178,29 @@ X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) {
|
||||
// TODO: Get last error
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
#elif XE_PLATFORM_LINUX
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
// Translate Xbox/Windows ioctl commands to Linux equivalents
|
||||
int native_cmd = cmd;
|
||||
switch (cmd) {
|
||||
case 0x8004667E: // Windows FIONBIO - set non-blocking mode
|
||||
native_cmd = FIONBIO;
|
||||
break;
|
||||
case 0x4004667F: // Windows FIONREAD - get bytes available
|
||||
native_cmd = FIONREAD;
|
||||
break;
|
||||
default:
|
||||
XELOGW("XSocket::IOControl: unknown cmd={:08X}, passing through", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
int ret = ioctl(native_handle_, native_cmd, arg_ptr);
|
||||
if (ret < 0) {
|
||||
XELOGE("XSocket::IOControl: ioctl failed, cmd={:08X} -> {:08X}, errno={}",
|
||||
cmd, native_cmd, errno);
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
return X_STATUS_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -130,8 +214,26 @@ X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) {
|
||||
}
|
||||
|
||||
X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) {
|
||||
// On Linux and Windows (when running under Wine), ports < 1024 require root
|
||||
// privileges. Remap to port + 10000 to avoid privilege issues.
|
||||
// Note: sin_port is xe::be<uint16_t> which automatically handles endianness,
|
||||
// so we use it directly without ntohs/htons.
|
||||
const uint16_t original_port = uint16_t(name->sin_port);
|
||||
if (original_port < 1024) {
|
||||
uint16_t new_port = original_port + 10000;
|
||||
name->sin_port = new_port;
|
||||
XELOGW("XSocket::Bind: port {} requires privileges, remapping to port {}",
|
||||
original_port, new_port);
|
||||
}
|
||||
|
||||
int ret = bind(native_handle_, (sockaddr*)name, name_len);
|
||||
|
||||
if (ret < 0) {
|
||||
#ifdef XE_PLATFORM_WIN32
|
||||
XELOGE("XSocket::Bind: bind() failed with WSA error {}", WSAGetLastError());
|
||||
#else
|
||||
XELOGE("XSocket::Bind: bind() failed with errno={}", errno);
|
||||
#endif
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user