From f6ac79ea07b89e7d79ec028b966d7a11e42beea8 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 28 Nov 2016 22:06:42 -0600 Subject: [PATCH] XSocket Implementation --- src/xenia/kernel/xam/xam_net.cc | 724 +++++++++++++++++++------------- src/xenia/kernel/xsocket.cc | 246 +++++++++++ src/xenia/kernel/xsocket.h | 151 +++++++ 3 files changed, 828 insertions(+), 293 deletions(-) create mode 100644 src/xenia/kernel/xsocket.cc create mode 100644 src/xenia/kernel/xsocket.h diff --git a/src/xenia/kernel/xam/xam_net.cc b/src/xenia/kernel/xam/xam_net.cc index 05a05e098..ff64b42bb 100644 --- a/src/xenia/kernel/xam/xam_net.cc +++ b/src/xenia/kernel/xam/xam_net.cc @@ -13,10 +13,12 @@ #include "xenia/base/logging.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/xam_module.h" #include "xenia/kernel/xam/xam_private.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_error.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" #include "xenia/kernel/xevent.h" +#include "xenia/kernel/xsocket.h" #include "xenia/kernel/xthread.h" #include "xenia/xbox.h" @@ -48,11 +50,27 @@ typedef struct { uint8_t abOnline[20]; // Online identification } XNADDR; +typedef struct { + xe::be status; + xe::be cina; + IN_ADDR aina[8]; +} XNDNS; + struct Xsockaddr_t { xe::be sa_family; char sa_data[14]; }; +struct X_WSADATA { + xe::be version; + xe::be version_high; + char description[256 + 1]; + char system_status[128 + 1]; + xe::be max_sockets; + xe::be max_udpdg; + xe::be vendor_info_ptr; +}; + struct XWSABUF { xe::be len; xe::be buf_ptr; @@ -78,7 +96,7 @@ void LoadSockaddr(const uint8_t* ptr, sockaddr* out_addr) { auto in_addr = reinterpret_cast(out_addr); in_addr->sin_port = xe::load_and_swap(ptr + 2); // Maybe? Depends on type. - in_addr->sin_addr.S_un.S_addr = xe::load_and_swap(ptr + 4); + in_addr->sin_addr.S_un.S_addr = *(uint32_t*)(ptr + 4); break; } default: @@ -125,32 +143,41 @@ struct XNetStartupParams { XNetStartupParams xnet_startup_params = {0}; -SHIM_CALL NetDll_XNetStartup_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t params_ptr = SHIM_GET_ARG_32(1); - - XELOGD("NetDll_XNetStartup(%d, %.8X)", caller, params_ptr); - - if (params_ptr) { - auto params = - kernel_memory()->TranslateVirtual(params_ptr); +dword_result_t NetDll_XNetStartup(dword_t caller, + pointer_t params) { + if (params) { assert_true(params->cfgSizeOfStruct == sizeof(XNetStartupParams)); std::memcpy(&xnet_startup_params, params, sizeof(XNetStartupParams)); } - SHIM_SET_RETURN_32(0); + auto xam = kernel_state()->GetKernelModule("xam.xex"); + + /* + if (!xam->xnet()) { + auto xnet = new XNet(kernel_state()); + xnet->Initialize(); + + xam->set_xnet(xnet); + } + */ + + return 0; } +DECLARE_XAM_EXPORT(NetDll_XNetStartup, + ExportTag::kNetworking | ExportTag::kImplemented); -SHIM_CALL NetDll_XNetCleanup_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t params_ptr = SHIM_GET_ARG_32(1); +dword_result_t NetDll_XNetCleanup(dword_t caller, lpvoid_t params) { + auto xam = kernel_state()->GetKernelModule("xam.xex"); + // auto xnet = xam->xnet(); + // xam->set_xnet(nullptr); - XELOGD("NetDll_XNetCleanup(%d, %.8X)", caller, params_ptr); + // TODO: Shut down and delete. + // delete xnet; - SHIM_SET_RETURN_32(0); + return 0; } +DECLARE_XAM_EXPORT(NetDll_XNetCleanup, + ExportTag::kNetworking | ExportTag::kImplemented); dword_result_t NetDll_XNetGetOpt(dword_t one, dword_t option_id, lpvoid_t buffer_ptr, lpdword_t buffer_size) { @@ -170,101 +197,140 @@ dword_result_t NetDll_XNetGetOpt(dword_t one, dword_t option_id, } DECLARE_XAM_EXPORT(NetDll_XNetGetOpt, ExportTag::kNetworking); -SHIM_CALL NetDll_XNetRandom_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t buffer_ptr = SHIM_GET_ARG_32(1); - uint32_t length = SHIM_GET_ARG_32(2); - - XELOGD("NetDll_XNetRandom(%d, %.8X, %d)", caller, buffer_ptr, length); - +dword_result_t NetDll_XNetRandom(dword_t caller, lpvoid_t buffer_ptr, + dword_t length) { // For now, constant values. // This makes replicating things easier. - std::memset(SHIM_MEM_ADDR(buffer_ptr), 0xBB, length); + std::memset(buffer_ptr, 0xBB, length); - SHIM_SET_RETURN_32(0); + return 0; } +DECLARE_XAM_EXPORT(NetDll_XNetRandom, + ExportTag::kNetworking | ExportTag::kStub); -SHIM_CALL NetDll_WSAStartup_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint16_t version = SHIM_GET_ARG_16(1); - uint32_t data_ptr = SHIM_GET_ARG_32(2); - - XELOGD("NetDll_WSAStartup(%d, %.4X, %.8X)", caller, version, data_ptr); - +dword_result_t NetDll_WSAStartup(dword_t caller, word_t version, + pointer_t data_ptr) { // TODO(benvanik): abstraction layer needed. WSADATA wsaData; ZeroMemory(&wsaData, sizeof(WSADATA)); int ret = WSAStartup(version, &wsaData); - auto data_out = kernel_state->memory()->TranslateVirtual(data_ptr); + auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr); if (data_ptr) { - xe::store_and_swap(data_out + 0x000, wsaData.wVersion); - xe::store_and_swap(data_out + 0x002, wsaData.wHighVersion); - std::memcpy(data_out + 0x004, wsaData.szDescription, 0x100); - std::memcpy(data_out + 0x105, wsaData.szSystemStatus, 0x80); - xe::store_and_swap(data_out + 0x186, wsaData.iMaxSockets); - xe::store_and_swap(data_out + 0x188, wsaData.iMaxUdpDg); + 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; // Some games (PoG) want this value round-tripped - they'll compare if it // changes and bugcheck if it does. - uint32_t vendor_ptr = SHIM_MEM_32(data_ptr + 0x190); - SHIM_SET_MEM_32(data_ptr + 0x190, vendor_ptr); + uint32_t vendor_ptr = xe::load_and_swap(data_out + 0x190); + xe::store_and_swap(data_out + 0x190, vendor_ptr); } - SHIM_SET_RETURN_32(ret); + // DEBUG + /* + auto xam = kernel_state()->GetKernelModule("xam.xex"); + if (!xam->xnet()) { + auto xnet = new XNet(kernel_state()); + xnet->Initialize(); + + xam->set_xnet(xnet); + } + */ + + return ret; } +DECLARE_XAM_EXPORT(NetDll_WSAStartup, + ExportTag::kNetworking | ExportTag::kImplemented); -SHIM_CALL NetDll_WSACleanup_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - - XELOGD("NetDll_WSACleanup(%d)", caller); - - // Don't actually call WSACleanup - we use it for the debugger and such. - // int ret = WSACleanup(); - int ret = 0; - - SHIM_SET_RETURN_32(ret); +dword_result_t NetDll_WSACleanup(dword_t caller) { + // This does nothing. Xenia needs WSA running. + return 0; } +DECLARE_XAM_EXPORT(NetDll_WSACleanup, + ExportTag::kNetworking | ExportTag::kImplemented); -SHIM_CALL NetDll_WSAGetLastError_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - XELOGD("NetDll_WSAGetLastError()"); - - int err = WSAGetLastError(); - SHIM_SET_RETURN_32(err); -} +dword_result_t NetDll_WSAGetLastError() { return XThread::GetLastError(); } +DECLARE_XAM_EXPORT(NetDll_WSAGetLastError, + ExportTag::kNetworking | ExportTag::kImplemented); dword_result_t NetDll_WSARecvFrom(dword_t caller, dword_t socket, pointer_t buffers_ptr, dword_t buffer_count, lpdword_t num_bytes_recv, lpdword_t flags_ptr, - pointer_t from_addr, + pointer_t from_addr, pointer_t overlapped_ptr, lpvoid_t completion_routine_ptr) { if (overlapped_ptr) { - auto evt = kernel_state()->object_table()->LookupObject( - overlapped_ptr->event_handle); + // auto evt = kernel_state()->object_table()->LookupObject( + // overlapped_ptr->event_handle); - if (evt) { - evt->Set(0, false); - } + // if (evt) { + // //evt->Set(0, false); + //} } return 0; } DECLARE_XAM_EXPORT(NetDll_WSARecvFrom, ExportTag::kNetworking); +// If the socket is a VDP socket, buffer 0 is the game data length, and buffer 1 +// is the unencrypted game data. +dword_result_t NetDll_WSASendTo(dword_t caller, dword_t socket_handle, + pointer_t buffers, dword_t num_buffers, + lpdword_t num_bytes_sent, dword_t flags, + pointer_t to_ptr, dword_t to_len, + pointer_t overlapped, + lpvoid_t completion_routine) { + assert(!overlapped); + assert(!completion_routine); + + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } + + // Our sockets implementation doesn't support multiple buffers, so we need + // to combine the buffers the game has given us! + std::vector combined_buffer_mem; + uint32_t combined_buffer_size = 0; + uint32_t combined_buffer_offset = 0; + for (uint32_t i = 0; i < num_buffers; i++) { + combined_buffer_size += buffers[i].len; + combined_buffer_mem.resize(combined_buffer_size); + uint8_t* combined_buffer = combined_buffer_mem.data(); + + std::memcpy(combined_buffer + combined_buffer_offset, + kernel_memory()->TranslateVirtual(buffers[i].buf_ptr), + buffers[i].len); + combined_buffer_offset += buffers[i].len; + } + + N_XSOCKADDR_IN native_to(to_ptr); + socket->SendTo(combined_buffer_mem.data(), combined_buffer_size, flags, + &native_to, to_len); + + // TODO: Instantly complete overlapped + + return 0; +} +DECLARE_XAM_EXPORT(NetDll_WSASendTo, + ExportTag::kNetworking | ExportTag::kImplemented); + dword_result_t NetDll_WSAWaitForMultipleEvents(dword_t num_events, lpdword_t events, dword_t wait_all, dword_t timeout, dword_t alertable) { if (num_events > 64) { - XThread::GetCurrentThread()->set_last_error(87); // ERROR_INVALID_PARAMETER + XThread::SetLastError(87); // ERROR_INVALID_PARAMETER return ~0u; } @@ -279,13 +345,14 @@ dword_result_t NetDll_WSAWaitForMultipleEvents(dword_t num_events, if (XFAILED(result)) { uint32_t error = xboxkrnl::RtlNtStatusToDosError(result); - XThread::GetCurrentThread()->set_last_error(error); + XThread::SetLastError(error); return ~0u; } return 0; } -DECLARE_XAM_EXPORT(NetDll_WSAWaitForMultipleEvents, - ExportTag::kNetworking | ExportTag::kThreading); +DECLARE_XAM_EXPORT(NetDll_WSAWaitForMultipleEvents, ExportTag::kNetworking | + ExportTag::kThreading | + ExportTag::kBlocking); dword_result_t NetDll_WSACreateEvent() { XEvent* ev = new XEvent(kernel_state()); @@ -299,7 +366,7 @@ dword_result_t NetDll_WSACloseEvent(dword_t event_handle) { X_STATUS result = kernel_state()->object_table()->ReleaseHandle(event_handle); if (XFAILED(result)) { uint32_t error = xboxkrnl::RtlNtStatusToDosError(result); - XThread::GetCurrentThread()->set_last_error(error); + XThread::SetLastError(error); return 0; } return 1; @@ -311,7 +378,7 @@ dword_result_t NetDll_WSAResetEvent(dword_t event_handle) { X_STATUS result = xboxkrnl::NtClearEvent(event_handle); if (XFAILED(result)) { uint32_t error = xboxkrnl::RtlNtStatusToDosError(result); - XThread::GetCurrentThread()->set_last_error(error); + XThread::SetLastError(error); return 0; } return 1; @@ -323,7 +390,7 @@ dword_result_t NetDll_WSASetEvent(dword_t event_handle) { X_STATUS result = xboxkrnl::NtSetEvent(event_handle, nullptr); if (XFAILED(result)) { uint32_t error = xboxkrnl::RtlNtStatusToDosError(result); - XThread::GetCurrentThread()->set_last_error(error); + XThread::SetLastError(error); return 0; } return 1; @@ -357,6 +424,7 @@ struct XnAddrStatus { dword_result_t NetDll_XNetGetTitleXnAddr(dword_t caller, pointer_t addr_ptr) { // Just return a loopback address atm. + // FIXME: This needs to return the ethernet MAC address! addr_ptr->ina.S_un.S_addr = htonl(INADDR_LOOPBACK); addr_ptr->inaOnline.S_un.S_addr = 0; addr_ptr->wPortOnline = 0; @@ -378,6 +446,42 @@ dword_result_t NetDll_XNetGetDebugXnAddr(dword_t caller, DECLARE_XAM_EXPORT(NetDll_XNetGetDebugXnAddr, ExportTag::kNetworking | ExportTag::kStub); +dword_result_t NetDll_XNetXnAddrToMachineId(dword_t caller, + pointer_t addr_ptr, + lpdword_t id_ptr) { + // Tell the caller we're not signed in to live (non-zero ret) + return 1; +} +DECLARE_XAM_EXPORT(NetDll_XNetXnAddrToMachineId, + ExportTag::kNetworking | ExportTag::kStub); + +void NetDll_XNetInAddrToString(dword_t caller, dword_t in_addr, + lpstring_t string_out, dword_t string_size) { + strncpy(string_out, "666.666.666.666", string_size); +} +DECLARE_XAM_EXPORT(NetDll_XNetInAddrToString, + ExportTag::kNetworking | ExportTag::kStub); + +// This converts a XNet address to an IN_ADDR. The IN_ADDR is used for +// subsequent socket calls (like a handle to a XNet address) +dword_result_t NetDll_XNetXnAddrToInAddr(dword_t caller, + pointer_t xn_addr, + lpvoid_t xid, lpvoid_t in_addr) { + return 1; +} +DECLARE_XAM_EXPORT(NetDll_XNetXnAddrToInAddr, + ExportTag::kNetworking | ExportTag::kStub); + +// Does the reverse of the above. +// FIXME: Arguments may not be correct. +dword_result_t NetDll_XNetInAddrToXnAddr(dword_t caller, lpvoid_t in_addr, + pointer_t xn_addr, + lpvoid_t xid) { + return 1; +} +DECLARE_XAM_EXPORT(NetDll_XNetInAddrToXnAddr, + ExportTag::kNetworking | ExportTag::kStub); + // http://www.google.com/patents/WO2008112448A1?cl=en // Reserves a port for use by system link dword_result_t NetDll_XNetSetSystemLinkPort(dword_t caller, dword_t port) { @@ -386,16 +490,25 @@ dword_result_t NetDll_XNetSetSystemLinkPort(dword_t caller, dword_t port) { DECLARE_XAM_EXPORT(NetDll_XNetSetSystemLinkPort, ExportTag::kNetworking | ExportTag::kStub); -SHIM_CALL NetDll_XNetGetEthernetLinkStatus_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - // Games seem to call this before *Startup. If we return 0, they don't even - // try. - uint32_t caller = SHIM_GET_ARG_32(0); +// https://github.com/ILOVEPIE/Cxbx-Reloaded/blob/master/src/CxbxKrnl/EmuXOnline.h#L39 +struct XEthernetStatus { + static const uint32_t XNET_ETHERNET_LINK_ACTIVE = 0x01; + static const uint32_t XNET_ETHERNET_LINK_100MBPS = 0x02; + static const uint32_t XNET_ETHERNET_LINK_10MBPS = 0x04; + static const uint32_t XNET_ETHERNET_LINK_FULL_DUPLEX = 0x08; + static const uint32_t XNET_ETHERNET_LINK_HALF_DUPLEX = 0x10; +}; - XELOGD("NetDll_XNetGetEthernetLinkStatus(%d)", caller); +dword_result_t NetDll_XNetGetEthernetLinkStatus(dword_t caller) { return 0; } +DECLARE_XAM_EXPORT(NetDll_XNetGetEthernetLinkStatus, + ExportTag::kStub | ExportTag::kNetworking); - SHIM_SET_RETURN_32(0); +dword_result_t NetDll_XNetDnsLookup(lpstring_t address, dword_t evt_handle, + pointer_t host_out) { + return 0; } +DECLARE_XAM_EXPORT(NetDll_XNetDnsLookup, + ExportTag::kStub | ExportTag::kNetworking); SHIM_CALL NetDll_XNetQosServiceLookup_shim(PPCContext* ppc_context, KernelState* kernel_state) { @@ -411,6 +524,14 @@ SHIM_CALL NetDll_XNetQosServiceLookup_shim(PPCContext* ppc_context, SHIM_SET_RETURN_32(1); } +dword_result_t NetDll_XNetQosListen(dword_t caller, lpvoid_t id, lpvoid_t data, + dword_t data_size, dword_t r7, + dword_t flags) { + return X_ERROR_FUNCTION_FAILED; +} +DECLARE_XAM_EXPORT(NetDll_XNetQosListen, + ExportTag::kStub | ExportTag::kNetworking); + dword_result_t NetDll_inet_addr(lpstring_t addr_ptr) { uint32_t addr = inet_addr(addr_ptr); return xe::byte_swap(addr); @@ -418,162 +539,190 @@ dword_result_t NetDll_inet_addr(lpstring_t addr_ptr) { DECLARE_XAM_EXPORT(NetDll_inet_addr, ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_socket_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t af = SHIM_GET_ARG_32(1); - uint32_t type = SHIM_GET_ARG_32(2); - uint32_t protocol = SHIM_GET_ARG_32(3); +dword_result_t NetDll_socket(dword_t caller, dword_t af, dword_t type, + dword_t protocol) { + XSocket* socket = new XSocket(kernel_state()); + X_STATUS result = socket->Initialize(XSocket::AddressFamily((uint32_t)af), + XSocket::Type((uint32_t)type), + XSocket::Protocol((uint32_t)protocol)); - XELOGD("NetDll_socket(%d, %d, %d, %d)", caller, af, type, protocol); - if (protocol == 0xFE) { - protocol = IPPROTO_UDP; + if (XFAILED(result)) { + socket->Release(); + + uint32_t error = xboxkrnl::RtlNtStatusToDosError(result); + XThread::SetLastError(error); + return -1; } - SOCKET socket_handle = socket(af, type, protocol); - assert_true(socket_handle >> 32 == 0); - - XELOGD("NetDll_socket = %.8X", socket_handle); - - SHIM_SET_RETURN_32(static_cast(socket_handle)); + return socket->handle(); } +DECLARE_XAM_EXPORT(NetDll_socket, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_closesocket_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); +dword_result_t NetDll_closesocket(dword_t caller, dword_t socket_handle) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_closesocket(%d, %.8X)", caller, socket_handle); - int ret = closesocket(socket_handle); - - SHIM_SET_RETURN_32(ret); + // TODO: Absolutely delete this object. It is no longer valid after calling + // closesocket. + socket->Close(); + socket->ReleaseHandle(); + return 0; } +DECLARE_XAM_EXPORT(NetDll_closesocket, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_setsockopt_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t level = SHIM_GET_ARG_32(2); - uint32_t optname = SHIM_GET_ARG_32(3); - uint32_t optval_ptr = SHIM_GET_ARG_32(4); - uint32_t optlen = SHIM_GET_ARG_32(5); +dword_result_t NetDll_setsockopt(dword_t caller, dword_t socket_handle, + dword_t level, dword_t optname, + lpvoid_t optval_ptr, dword_t optlen) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_setsockopt(%d, %.8X, %d, %d, %.8X, %d)", caller, socket_handle, - level, optname, optval_ptr, optlen); - - char* optval = reinterpret_cast(SHIM_MEM_ADDR(optval_ptr)); - int ret = setsockopt(socket_handle, level, optname, optval, optlen); - - SHIM_SET_RETURN_32(ret); + X_STATUS status = socket->SetOption(level, optname, optval_ptr, optlen); + return XSUCCEEDED(status) ? 0 : -1; } +DECLARE_XAM_EXPORT(NetDll_setsockopt, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_ioctlsocket_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t cmd = SHIM_GET_ARG_32(2); - uint32_t arg_ptr = SHIM_GET_ARG_32(3); +dword_result_t NetDll_ioctlsocket(dword_t caller, dword_t socket_handle, + dword_t cmd, lpvoid_t arg_ptr) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_ioctlsocket(%d, %.8X, %.8X, %.8X)", caller, socket_handle, cmd, - arg_ptr); + X_STATUS status = socket->IOControl(cmd, arg_ptr); + if (XFAILED(status)) { + XThread::SetLastError(xboxkrnl::RtlNtStatusToDosError(status)); + return -1; + } - u_long arg = SHIM_MEM_32(arg_ptr); - int ret = ioctlsocket(socket_handle, cmd, &arg); - - SHIM_SET_MEM_32(arg_ptr, arg); - SHIM_SET_RETURN_32(ret); + // TODO + return 0; } +DECLARE_XAM_EXPORT(NetDll_ioctlsocket, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_bind_shim(PPCContext* ppc_context, KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t name_ptr = SHIM_GET_ARG_32(2); - uint32_t namelen = SHIM_GET_ARG_32(3); +dword_result_t NetDll_bind(dword_t caller, dword_t socket_handle, + pointer_t name, dword_t namelen) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_bind(%d, %.8X, %.8X, %d)", caller, socket_handle, name_ptr, - namelen); + N_XSOCKADDR_IN native_name(name); + X_STATUS status = socket->Bind(&native_name, namelen); + if (XFAILED(status)) { + XThread::SetLastError(xboxkrnl::RtlNtStatusToDosError(status)); + return -1; + } - sockaddr name; - LoadSockaddr(SHIM_MEM_ADDR(name_ptr), &name); - int ret = bind(socket_handle, &name, namelen); - - SHIM_SET_RETURN_32(ret); + return 0; } +DECLARE_XAM_EXPORT(NetDll_bind, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_connect_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t name_ptr = SHIM_GET_ARG_32(2); - uint32_t namelen = SHIM_GET_ARG_32(3); +dword_result_t NetDll_connect(dword_t caller, dword_t socket_handle, + pointer_t name, dword_t namelen) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_connect(%d, %.8X, %.8X, %d)", caller, socket_handle, name_ptr, - namelen); + N_XSOCKADDR native_name(name); + X_STATUS status = socket->Connect(&native_name, namelen); + if (XFAILED(status)) { + XThread::SetLastError(xboxkrnl::RtlNtStatusToDosError(status)); + return -1; + } - sockaddr name; - LoadSockaddr(SHIM_MEM_ADDR(name_ptr), &name); - int ret = connect(socket_handle, &name, namelen); - - SHIM_SET_RETURN_32(ret); + return 0; } +DECLARE_XAM_EXPORT(NetDll_connect, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_listen_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - int32_t backlog = SHIM_GET_ARG_32(2); +dword_result_t NetDll_listen(dword_t caller, dword_t socket_handle, + int_t backlog) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_listen(%d, %.8X, %d)", caller, socket_handle, backlog); + X_STATUS status = socket->Listen(backlog); + if (XFAILED(status)) { + XThread::SetLastError(xboxkrnl::RtlNtStatusToDosError(status)); + return -1; + } - int ret = listen(socket_handle, backlog); - - SHIM_SET_RETURN_32(ret); + return 0; } +DECLARE_XAM_EXPORT(NetDll_listen, + ExportTag::kImplemented | ExportTag::kNetworking); -SHIM_CALL NetDll_accept_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t addr_ptr = SHIM_GET_ARG_32(2); - uint32_t addrlen_ptr = SHIM_GET_ARG_32(3); +dword_result_t NetDll_accept(dword_t caller, dword_t socket_handle, + pointer_t addr_ptr, + lpdword_t addrlen_ptr) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_accept(%d, %.8X, %d)", caller, socket_handle, addr_ptr, - addrlen_ptr); + N_XSOCKADDR native_addr(addr_ptr); + int native_len = *addrlen_ptr; + auto new_socket = socket->Accept(&native_addr, &native_len); + if (new_socket) { + addr_ptr->address_family = native_addr.address_family; + std::memcpy(addr_ptr->sa_data, native_addr.sa_data, *addrlen_ptr - 2); + *addrlen_ptr = native_len; - sockaddr addr; - int addrlen; - SOCKET ret_socket = accept(socket_handle, &addr, &addrlen); - - if (ret_socket == INVALID_SOCKET) { - if (addr_ptr) { - std::memset(SHIM_MEM_ADDR(addr_ptr), 0, sizeof(addr)); - } - - if (addrlen_ptr) { - SHIM_SET_MEM_32(addrlen_ptr, sizeof(addr)); - } - - SHIM_SET_RETURN_32(-1); + return new_socket->handle(); } else { - assert_true(ret_socket >> 32 == 0); - if (addr_ptr) { - StoreSockaddr(addr, SHIM_MEM_ADDR(addr_ptr)); - } - - if (addrlen_ptr) { - SHIM_SET_MEM_32(addrlen_ptr, addrlen); - } - - SHIM_SET_RETURN_32(static_cast(ret_socket)); + return -1; } } +DECLARE_XAM_EXPORT(NetDll_accept, + ExportTag::kImplemented | ExportTag::kNetworking); void LoadFdset(const uint8_t* src, fd_set* dest) { dest->fd_count = xe::load_and_swap(src); for (int i = 0; i < 64; ++i) { auto socket_handle = - static_cast(xe::load_and_swap(src + 4 + i * 4)); - dest->fd_array[i] = socket_handle; + static_cast(xe::load_and_swap(src + 4 + i * 4)); + if (!socket_handle) { + break; + } + + // Convert from Xenia -> native + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + auto native_handle = socket->native_handle(); + + dest->fd_array[i] = native_handle; } } @@ -581,6 +730,9 @@ void StoreFdset(const fd_set& src, uint8_t* dest) { xe::store_and_swap(dest, src.fd_count); for (int i = 0; i < 64; ++i) { SOCKET socket_handle = src.fd_array[i]; + + // TODO: Native -> Xenia + assert_true(socket_handle >> 32 == 0); xe::store_and_swap(dest + 4 + i * 4, static_cast(socket_handle)); @@ -637,112 +789,98 @@ SHIM_CALL NetDll_select_shim(PPCContext* ppc_context, SHIM_SET_RETURN_32(ret); } -SHIM_CALL NetDll_recv_shim(PPCContext* ppc_context, KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t buf_ptr = SHIM_GET_ARG_32(2); - uint32_t len = SHIM_GET_ARG_32(3); - uint32_t flags = SHIM_GET_ARG_32(4); - - XELOGD("NetDll_recv(%d, %.8X, %.8X, %d, %d)", caller, socket_handle, buf_ptr, - len, flags); - - int ret = recv(socket_handle, reinterpret_cast(SHIM_MEM_ADDR(buf_ptr)), - len, flags); - - SHIM_SET_RETURN_32(ret); -} - -SHIM_CALL NetDll_recvfrom_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t buf_ptr = SHIM_GET_ARG_32(2); - uint32_t len = SHIM_GET_ARG_32(3); - uint32_t flags = SHIM_GET_ARG_32(4); - uint32_t from_ptr = SHIM_GET_ARG_32(5); - uint32_t fromlen_ptr = SHIM_GET_ARG_32(6); - - XELOGD("NetDll_recvfrom(%d, %.8X, %.8X, %d, %d, %.8X, %.8X)", caller, - socket_handle, buf_ptr, len, flags, from_ptr, fromlen_ptr); - - sockaddr from; - int fromlen = sizeof(from); - int ret = - recvfrom(socket_handle, reinterpret_cast(SHIM_MEM_ADDR(buf_ptr)), - len, flags, &from, &fromlen); - if (ret == -1) { - std::memset(SHIM_MEM_ADDR(from_ptr), 0, sizeof(from)); - SHIM_SET_MEM_32(fromlen_ptr, sizeof(from)); - } else { - StoreSockaddr(from, SHIM_MEM_ADDR(from_ptr)); - SHIM_SET_MEM_32(fromlen_ptr, fromlen); +dword_result_t NetDll_recv(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, dword_t flags) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; } - SHIM_SET_RETURN_32(ret); + return socket->Recv(buf_ptr, buf_len, flags); } +DECLARE_XAM_EXPORT(NetDll_recv, + ExportTag::kNetworking | ExportTag::kImplemented); -SHIM_CALL NetDll_send_shim(PPCContext* ppc_context, KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t buf_ptr = SHIM_GET_ARG_32(2); - uint32_t len = SHIM_GET_ARG_32(3); - uint32_t flags = SHIM_GET_ARG_32(4); +dword_result_t NetDll_recvfrom(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, dword_t flags, + pointer_t from_ptr, + lpdword_t fromlen_ptr) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_send(%d, %.8X, %.8X, %d, %d)", caller, socket_handle, buf_ptr, - len, flags); + N_XSOCKADDR_IN native_from; + if (from_ptr) { + native_from = *from_ptr; + } + uint32_t native_fromlen = fromlen_ptr ? *fromlen_ptr : 0; + int ret = socket->RecvFrom(buf_ptr, buf_len, flags, &native_from, + fromlen_ptr ? &native_fromlen : 0); - int ret = send(socket_handle, reinterpret_cast(SHIM_MEM_ADDR(buf_ptr)), - len, flags); + if (from_ptr) { + from_ptr->sin_family = native_from.sin_family; + from_ptr->sin_port = native_from.sin_port; + from_ptr->sin_addr = native_from.sin_addr; + memset(from_ptr->sin_zero, 0, 8); + } + if (fromlen_ptr) { + *fromlen_ptr = native_fromlen; + } - SHIM_SET_RETURN_32(ret); + if (ret == -1) { + // TODO: Better way of getting the error code + uint32_t error_code = WSAGetLastError(); + XThread::SetLastError(error_code); + } + + return ret; } +DECLARE_XAM_EXPORT(NetDll_recvfrom, + ExportTag::kNetworking | ExportTag::kImplemented); -SHIM_CALL NetDll_sendto_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t caller = SHIM_GET_ARG_32(0); - uint32_t socket_handle = SHIM_GET_ARG_32(1); - uint32_t buf_ptr = SHIM_GET_ARG_32(2); - uint32_t len = SHIM_GET_ARG_32(3); - uint32_t flags = SHIM_GET_ARG_32(4); - uint32_t to_ptr = SHIM_GET_ARG_32(5); - uint32_t tolen = SHIM_GET_ARG_32(6); +dword_result_t NetDll_send(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, dword_t flags) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } - XELOGD("NetDll_sendto(%d, %.8X, %.8X, %d, %d, %.8X, %d)", caller, - socket_handle, buf_ptr, len, flags, to_ptr, tolen); - - sockaddr to; - LoadSockaddr(SHIM_MEM_ADDR(to_ptr), &to); - int ret = - sendto(socket_handle, reinterpret_cast(SHIM_MEM_ADDR(buf_ptr)), - len, flags, &to, tolen); - - SHIM_SET_RETURN_32(ret); + return socket->Send(buf_ptr, buf_len, flags); } +DECLARE_XAM_EXPORT(NetDll_send, + ExportTag::kNetworking | ExportTag::kImplemented); + +dword_result_t NetDll_sendto(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, dword_t flags, + pointer_t to_ptr, dword_t to_len) { + auto socket = + kernel_state()->object_table()->LookupObject(socket_handle); + if (!socket) { + // WSAENOTSOCK + XThread::SetLastError(0x2736); + return -1; + } + + N_XSOCKADDR_IN native_to(to_ptr); + return socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len); +} +DECLARE_XAM_EXPORT(NetDll_sendto, + ExportTag::kNetworking | ExportTag::kImplemented); void RegisterNetExports(xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) { - SHIM_SET_MAPPING("xam.xex", NetDll_XNetStartup, state); - SHIM_SET_MAPPING("xam.xex", NetDll_XNetCleanup, state); - SHIM_SET_MAPPING("xam.xex", NetDll_XNetRandom, state); - SHIM_SET_MAPPING("xam.xex", NetDll_WSAStartup, state); - SHIM_SET_MAPPING("xam.xex", NetDll_WSACleanup, state); - SHIM_SET_MAPPING("xam.xex", NetDll_WSAGetLastError, state); - SHIM_SET_MAPPING("xam.xex", NetDll_XNetGetEthernetLinkStatus, state); SHIM_SET_MAPPING("xam.xex", NetDll_XNetQosServiceLookup, state); - SHIM_SET_MAPPING("xam.xex", NetDll_socket, state); - SHIM_SET_MAPPING("xam.xex", NetDll_closesocket, state); - SHIM_SET_MAPPING("xam.xex", NetDll_setsockopt, state); - SHIM_SET_MAPPING("xam.xex", NetDll_ioctlsocket, state); - SHIM_SET_MAPPING("xam.xex", NetDll_bind, state); - SHIM_SET_MAPPING("xam.xex", NetDll_connect, state); - SHIM_SET_MAPPING("xam.xex", NetDll_listen, state); - SHIM_SET_MAPPING("xam.xex", NetDll_accept, state); SHIM_SET_MAPPING("xam.xex", NetDll_select, state); - SHIM_SET_MAPPING("xam.xex", NetDll_recv, state); - SHIM_SET_MAPPING("xam.xex", NetDll_recvfrom, state); - SHIM_SET_MAPPING("xam.xex", NetDll_send, state); - SHIM_SET_MAPPING("xam.xex", NetDll_sendto, state); } } // namespace xam diff --git a/src/xenia/kernel/xsocket.cc b/src/xenia/kernel/xsocket.cc new file mode 100644 index 000000000..f18cd064d --- /dev/null +++ b/src/xenia/kernel/xsocket.cc @@ -0,0 +1,246 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "src/xenia/kernel/xsocket.h" + +#include "xenia/base/platform.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/xam/xam_module.h" +// #include "xenia/kernel/xnet.h" + +#if XE_PLATFORM_WIN32 +#include +#elif XE_PLATFORM_LINUX +#error TODO: Proper network includes +#endif + +namespace xe { +namespace kernel { + +XSocket::XSocket(KernelState* kernel_state) + : XObject(kernel_state, XObject::kTypeSocket) {} + +XSocket::XSocket(KernelState* kernel_state, uint64_t native_handle) + : XObject(kernel_state, XObject::kTypeSocket), + native_handle_(native_handle) {} + +XSocket::~XSocket() { Close(); } + +X_STATUS XSocket::Initialize(AddressFamily af, Type type, Protocol proto) { + af_ = af; + type_ = type; + proto_ = proto; + + if (proto == Protocol::IPPROTO_VDP) { + // VDP is a layer on top of UDP. + proto = Protocol::IPPROTO_UDP; + } + + native_handle_ = socket(af, type, proto); + if (native_handle_ == -1) { + return X_STATUS_UNSUCCESSFUL; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS XSocket::Close() { +#if XE_PLATFORM_WIN32 + int ret = closesocket(native_handle_); +#elif XE_PLATFORM_LINUX + +#endif + + if (ret != 0) { + return X_STATUS_UNSUCCESSFUL; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr, + uint32_t optlen) { + if (level == 0xFFFF && (optname == 0x5801 || optname == 0x5802)) { + // Disable socket encryption + secure_ = false; + return X_STATUS_SUCCESS; + } + + int ret = + setsockopt(native_handle_, level, optname, (char*)optval_ptr, optlen); + if (ret < 0) { + // TODO: WSAGetLastError() + return X_STATUS_UNSUCCESSFUL; + } + + // SO_BROADCAST + if (level == 0xFFFF && optname == 0x0020) { + broadcast_socket_ = true; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) { + int ret = ioctlsocket(native_handle_, cmd, (u_long*)arg_ptr); + if (ret < 0) { + // TODO: Get last error + return X_STATUS_UNSUCCESSFUL; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) { + int ret = connect(native_handle_, (sockaddr*)name, name_len); + if (ret < 0) { + return X_STATUS_UNSUCCESSFUL; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) { + int ret = bind(native_handle_, (sockaddr*)name, name_len); + if (ret < 0) { + return X_STATUS_UNSUCCESSFUL; + } + + bound_ = true; + bound_port_ = name->sin_port; + + return X_STATUS_SUCCESS; +} + +X_STATUS XSocket::Listen(int backlog) { + int ret = listen(native_handle_, backlog); + if (ret < 0) { + return X_STATUS_UNSUCCESSFUL; + } + + return X_STATUS_SUCCESS; +} + +object_ref XSocket::Accept(N_XSOCKADDR* name, int* name_len) { + sockaddr n_sockaddr; + int n_name_len = sizeof(sockaddr); + SOCKET ret = accept(native_handle_, &n_sockaddr, &n_name_len); + if (ret == INVALID_SOCKET) { + std::memset(name, 0, *name_len); + *name_len = 0; + return nullptr; + } + + std::memcpy(name, &n_sockaddr, n_name_len); + *name_len = n_name_len; + + // Create a kernel object to represent the new socket, and copy parameters + // over. + auto socket = object_ref(new XSocket(kernel_state_, ret)); + socket->af_ = af_; + socket->type_ = type_; + socket->proto_ = proto_; + + return socket; +} + +int XSocket::Recv(uint8_t* buf, uint32_t buf_len, uint32_t flags) { + return recv(native_handle_, reinterpret_cast(buf), buf_len, flags); +} + +int XSocket::RecvFrom(uint8_t* buf, uint32_t buf_len, uint32_t flags, + N_XSOCKADDR_IN* from, uint32_t* from_len) { + // Pop from secure packets first + // TODO(DrChat): Enable when I commit XNet + /* + { + std::lock_guard lock(incoming_packet_mutex_); + if (incoming_packets_.size()) { + packet* pkt = (packet*)incoming_packets_.front(); + int data_len = pkt->data_len; + std::memcpy(buf, pkt->data, std::min((uint32_t)pkt->data_len, buf_len)); + + from->sin_family = 2; + from->sin_addr = pkt->src_ip; + from->sin_port = pkt->src_port; + + incoming_packets_.pop(); + uint8_t* pkt_ui8 = (uint8_t*)pkt; + delete[] pkt_ui8; + + return data_len; + } + } + */ + + sockaddr_in nfrom; + int nfromlen = sizeof(sockaddr_in); + int ret = recvfrom(native_handle_, reinterpret_cast(buf), buf_len, + flags, (sockaddr*)&nfrom, &nfromlen); + if (from) { + from->sin_family = nfrom.sin_family; + from->sin_addr = ntohl(nfrom.sin_addr.s_addr); // BE <- BE + from->sin_port = nfrom.sin_port; + memset(&from->sin_zero, 0, 8); + } + + if (from_len) { + *from_len = nfromlen; + } + + return ret; +} + +int XSocket::Send(const uint8_t* buf, uint32_t buf_len, uint32_t flags) { + return send(native_handle_, reinterpret_cast(buf), buf_len, + flags); +} + +int XSocket::SendTo(uint8_t* buf, uint32_t buf_len, uint32_t flags, + N_XSOCKADDR_IN* to, uint32_t to_len) { + // Send 2 copies of the packet: One to XNet (for network security) and an + // unencrypted copy for other Xenia hosts. + // TODO(DrChat): Enable when I commit XNet. + /* + auto xam = kernel_state()->GetKernelModule("xam.xex"); + auto xnet = xam->xnet(); + if (xnet) { + xnet->SendPacket(this, to, buf, buf_len); + } + */ + + sockaddr_in nto; + if (to) { + nto.sin_addr.S_un.S_addr = to->sin_addr; + nto.sin_family = to->sin_family; + nto.sin_port = to->sin_port; + } + + return sendto(native_handle_, reinterpret_cast(buf), buf_len, flags, + to ? (sockaddr*)&nto : nullptr, to_len); +} + +bool XSocket::QueuePacket(uint32_t src_ip, uint16_t src_port, + const uint8_t* buf, size_t len) { + packet* pkt = reinterpret_cast(new uint8_t[sizeof(packet) + len]); + pkt->src_ip = src_ip; + pkt->src_port = src_port; + + pkt->data_len = (uint16_t)len; + std::memcpy(pkt->data, buf, len); + + std::lock_guard lock(incoming_packet_mutex_); + incoming_packets_.push((uint8_t*)pkt); + + // TODO: Limit on number of incoming packets? + return true; +} + +} // namespace kernel +} // namespace xe \ No newline at end of file diff --git a/src/xenia/kernel/xsocket.h b/src/xenia/kernel/xsocket.h new file mode 100644 index 000000000..18328ef03 --- /dev/null +++ b/src/xenia/kernel/xsocket.h @@ -0,0 +1,151 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XSOCKET_H_ +#define XENIA_KERNEL_XSOCKET_H_ + +#include + +#include "xenia/base/byte_order.h" +#include "xenia/base/math.h" +#include "xenia/kernel/xobject.h" + +namespace xe { +namespace kernel { +struct XSOCKADDR { + xe::be address_family; + char sa_data[14]; +}; + +struct N_XSOCKADDR { + N_XSOCKADDR() {} + N_XSOCKADDR(const XSOCKADDR* other) { *this = *other; } + N_XSOCKADDR& operator=(const XSOCKADDR& other) { + address_family = other.address_family; + std::memcpy(sa_data, other.sa_data, xe::countof(sa_data)); + return *this; + } + + uint16_t address_family; + char sa_data[14]; +}; + +struct XSOCKADDR_IN { + xe::be sin_family; + + // Always big-endian! + xe::be sin_port; + xe::be sin_addr; + char sin_zero[8]; +}; + +// Xenia native sockaddr_in +struct N_XSOCKADDR_IN { + N_XSOCKADDR_IN() {} + N_XSOCKADDR_IN(const XSOCKADDR_IN* other) { *this = *other; } + N_XSOCKADDR_IN& operator=(const XSOCKADDR_IN& other) { + sin_family = other.sin_family; + sin_port = other.sin_port; + sin_addr = other.sin_addr; + std::memset(sin_zero, 0, 8); + + return *this; + } + + uint16_t sin_family; + xe::be sin_port; + xe::be sin_addr; + char sin_zero[8]; +}; + +class XSocket : public XObject { + public: + static const Type kType = kTypeSocket; + + enum AddressFamily { + AF_INET = 2, + }; + + enum Type { + SOCK_STREAM = 1, + SOCK_DGRAM = 2, + }; + + enum Protocol { + IPPROTO_TCP = 6, + IPPROTO_UDP = 17, + + // LIVE Voice and Data Protocol + // http://blog.csdn.net/baozi3026/article/details/4277227 + // Format: [cbGameData][GameData(encrypted)][VoiceData(unencrypted)] + IPPROTO_VDP = 254, + }; + + XSocket(KernelState* kernel_state); + ~XSocket(); + + uint64_t native_handle() const { return native_handle_; } + uint16_t bound_port() const { return bound_port_; } + + X_STATUS Initialize(AddressFamily af, Type type, Protocol proto); + X_STATUS Close(); + + X_STATUS SetOption(uint32_t level, uint32_t optname, void* optval_ptr, + uint32_t optlen); + X_STATUS IOControl(uint32_t cmd, uint8_t* arg_ptr); + + X_STATUS Connect(N_XSOCKADDR* name, int name_len); + X_STATUS Bind(N_XSOCKADDR_IN* name, int name_len); + X_STATUS Listen(int backlog); + object_ref Accept(N_XSOCKADDR* name, int* name_len); + + int Recv(uint8_t* buf, uint32_t buf_len, uint32_t flags); + int Send(const uint8_t* buf, uint32_t buf_len, uint32_t flags); + + int RecvFrom(uint8_t* buf, uint32_t buf_len, uint32_t flags, + N_XSOCKADDR_IN* from, uint32_t* from_len); + int SendTo(uint8_t* buf, uint32_t buf_len, uint32_t flags, N_XSOCKADDR_IN* to, + uint32_t to_len); + + struct packet { + // These values are in network byte order. + xe::be src_port; + xe::be src_ip; + + uint16_t data_len; + uint8_t data[1]; + }; + + // Queue a packet into our internal buffer. + bool QueuePacket(uint32_t src_ip, uint16_t src_port, const uint8_t* buf, + size_t len); + + private: + XSocket(KernelState* kernel_state, uint64_t native_handle); + uint64_t native_handle_ = -1; + + AddressFamily af_; // Address family + Type type_; // Type (DGRAM/Stream/etc) + Protocol proto_; // Protocol (TCP/UDP/etc) + bool secure_ = true; // Secure socket (encryption enabled) + + bool bound_ = false; // Explicitly bound to an IP address? + uint16_t bound_port_ = 0; + + bool broadcast_socket_ = false; + + std::unique_ptr event_; + std::mutex incoming_packet_mutex_; + std::queue incoming_packets_; +}; + +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XSOCKET_H_ \ No newline at end of file