diff --git a/src/core/hle/service/soc/soc_u.cpp b/src/core/hle/service/soc/soc_u.cpp index 5ae29ebbe..7a8a5dc9f 100644 --- a/src/core/hle/service/soc/soc_u.cpp +++ b/src/core/hle/service/soc/soc_u.cpp @@ -2,898 +2,37 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include -#include -#include -#include "common/assert.h" -#include "common/bit_field.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/scope_exit.h" -#include "core/hle/ipc.h" -#include "core/hle/kernel/server_session.h" -#include "core/hle/result.h" #include "core/hle/service/soc/soc_u.h" -#include "core/memory.h" - -#ifdef _WIN32 -#include -#include - -// MinGW does not define several errno constants -#ifndef _MSC_VER -#define EBADMSG 104 -#define ENODATA 120 -#define ENOMSG 122 -#define ENOSR 124 -#define ENOSTR 125 -#define ETIME 137 -#define EIDRM 2001 -#define ENOLINK 2002 -#endif // _MSC_VER -#else -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifdef _WIN32 -#define WSAEAGAIN WSAEWOULDBLOCK -#define WSAEMULTIHOP -1 // Invalid dummy value -#define ERRNO(x) WSA##x -#define GET_ERRNO WSAGetLastError() -#define poll(x, y, z) WSAPoll(x, y, z); -#else -#define ERRNO(x) x -#define GET_ERRNO errno -#define closesocket(x) close(x) -#endif namespace Service { namespace SOC { -const s32 SOCKET_ERROR_VALUE = -1; - -/// Holds the translation from system network errors to 3DS network errors -static const std::unordered_map error_map = {{ - {E2BIG, 1}, - {ERRNO(EACCES), 2}, - {ERRNO(EADDRINUSE), 3}, - {ERRNO(EADDRNOTAVAIL), 4}, - {ERRNO(EAFNOSUPPORT), 5}, - {ERRNO(EAGAIN), 6}, - {ERRNO(EALREADY), 7}, - {ERRNO(EBADF), 8}, - {EBADMSG, 9}, - {EBUSY, 10}, - {ECANCELED, 11}, - {ECHILD, 12}, - {ERRNO(ECONNABORTED), 13}, - {ERRNO(ECONNREFUSED), 14}, - {ERRNO(ECONNRESET), 15}, - {EDEADLK, 16}, - {ERRNO(EDESTADDRREQ), 17}, - {EDOM, 18}, - {ERRNO(EDQUOT), 19}, - {EEXIST, 20}, - {ERRNO(EFAULT), 21}, - {EFBIG, 22}, - {ERRNO(EHOSTUNREACH), 23}, - {EIDRM, 24}, - {EILSEQ, 25}, - {ERRNO(EINPROGRESS), 26}, - {ERRNO(EINTR), 27}, - {ERRNO(EINVAL), 28}, - {EIO, 29}, - {ERRNO(EISCONN), 30}, - {EISDIR, 31}, - {ERRNO(ELOOP), 32}, - {ERRNO(EMFILE), 33}, - {EMLINK, 34}, - {ERRNO(EMSGSIZE), 35}, - {ERRNO(EMULTIHOP), 36}, - {ERRNO(ENAMETOOLONG), 37}, - {ERRNO(ENETDOWN), 38}, - {ERRNO(ENETRESET), 39}, - {ERRNO(ENETUNREACH), 40}, - {ENFILE, 41}, - {ERRNO(ENOBUFS), 42}, -#ifdef ENODATA - {ENODATA, 43}, -#endif - {ENODEV, 44}, - {ENOENT, 45}, - {ENOEXEC, 46}, - {ENOLCK, 47}, - {ENOLINK, 48}, - {ENOMEM, 49}, - {ENOMSG, 50}, - {ERRNO(ENOPROTOOPT), 51}, - {ENOSPC, 52}, -#ifdef ENOSR - {ENOSR, 53}, -#endif -#ifdef ENOSTR - {ENOSTR, 54}, -#endif - {ENOSYS, 55}, - {ERRNO(ENOTCONN), 56}, - {ENOTDIR, 57}, - {ERRNO(ENOTEMPTY), 58}, - {ERRNO(ENOTSOCK), 59}, - {ENOTSUP, 60}, - {ENOTTY, 61}, - {ENXIO, 62}, - {ERRNO(EOPNOTSUPP), 63}, - {EOVERFLOW, 64}, - {EPERM, 65}, - {EPIPE, 66}, - {EPROTO, 67}, - {ERRNO(EPROTONOSUPPORT), 68}, - {ERRNO(EPROTOTYPE), 69}, - {ERANGE, 70}, - {EROFS, 71}, - {ESPIPE, 72}, - {ESRCH, 73}, - {ERRNO(ESTALE), 74}, -#ifdef ETIME - {ETIME, 75}, -#endif - {ERRNO(ETIMEDOUT), 76}, -}}; - -/// Converts a network error from platform-specific to 3ds-specific -static int TranslateError(int error) { - auto found = error_map.find(error); - if (found != error_map.end()) - return -found->second; - - return error; -} - -/// Holds the translation from system network socket options to 3DS network socket options -/// Note: -1 = No effect/unavailable -static const std::unordered_map sockopt_map = {{ - {0x0004, SO_REUSEADDR}, - {0x0080, -1}, - {0x0100, -1}, - {0x1001, SO_SNDBUF}, - {0x1002, SO_RCVBUF}, - {0x1003, -1}, -#ifdef _WIN32 - /// Unsupported in WinSock2 - {0x1004, -1}, -#else - {0x1004, SO_RCVLOWAT}, -#endif - {0x1008, SO_TYPE}, - {0x1009, SO_ERROR}, -}}; - -/// Converts a socket option from 3ds-specific to platform-specific -static int TranslateSockOpt(int console_opt_name) { - auto found = sockopt_map.find(console_opt_name); - if (found != sockopt_map.end()) { - return found->second; - } - return console_opt_name; -} - -/// Holds information about a particular socket -struct SocketHolder { - u32 socket_fd; ///< The socket descriptor - bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows. -}; - -/// Structure to represent the 3ds' pollfd structure, which is different than most implementations -struct CTRPollFD { - u32 fd; ///< Socket handle - - union Events { - u32 hex; ///< The complete value formed by the flags - BitField<0, 1, u32> pollin; - BitField<1, 1, u32> pollpri; - BitField<2, 1, u32> pollhup; - BitField<3, 1, u32> pollerr; - BitField<4, 1, u32> pollout; - BitField<5, 1, u32> pollnval; - - Events& operator=(const Events& other) { - hex = other.hex; - return *this; - } - - /// Translates the resulting events of a Poll operation from platform-specific to 3ds - /// specific - static Events TranslateTo3DS(u32 input_event) { - Events ev = {}; - if (input_event & POLLIN) - ev.pollin.Assign(1); - if (input_event & POLLPRI) - ev.pollpri.Assign(1); - if (input_event & POLLHUP) - ev.pollhup.Assign(1); - if (input_event & POLLERR) - ev.pollerr.Assign(1); - if (input_event & POLLOUT) - ev.pollout.Assign(1); - if (input_event & POLLNVAL) - ev.pollnval.Assign(1); - return ev; - } - - /// Translates the resulting events of a Poll operation from 3ds specific to platform - /// specific - static u32 TranslateToPlatform(Events input_event) { - u32 ret = 0; - if (input_event.pollin) - ret |= POLLIN; - if (input_event.pollpri) - ret |= POLLPRI; - if (input_event.pollhup) - ret |= POLLHUP; - if (input_event.pollerr) - ret |= POLLERR; - if (input_event.pollout) - ret |= POLLOUT; - if (input_event.pollnval) - ret |= POLLNVAL; - return ret; - } - }; - Events events; ///< Events to poll for (input) - Events revents; ///< Events received (output) - - /// Converts a platform-specific pollfd to a 3ds specific structure - static CTRPollFD FromPlatform(pollfd const& fd) { - CTRPollFD result; - result.events.hex = Events::TranslateTo3DS(fd.events).hex; - result.revents.hex = Events::TranslateTo3DS(fd.revents).hex; - result.fd = static_cast(fd.fd); - return result; - } - - /// Converts a 3ds specific pollfd to a platform-specific structure - static pollfd ToPlatform(CTRPollFD const& fd) { - pollfd result; - result.events = Events::TranslateToPlatform(fd.events); - result.revents = Events::TranslateToPlatform(fd.revents); - result.fd = fd.fd; - return result; - } -}; - -/// Union to represent the 3ds' sockaddr structure -union CTRSockAddr { - /// Structure to represent a raw sockaddr - struct { - u8 len; ///< The length of the entire structure, only the set fields count - u8 sa_family; ///< The address family of the sockaddr - u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family - } raw; - - /// Structure to represent the 3ds' sockaddr_in structure - struct CTRSockAddrIn { - u8 len; ///< The length of the entire structure - u8 sin_family; ///< The address family of the sockaddr_in - u16 sin_port; ///< The port associated with this sockaddr_in - u32 sin_addr; ///< The actual address of the sockaddr_in - } in; - - /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr - static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) { - sockaddr result; - result.sa_family = ctr_addr.raw.sa_family; - memset(result.sa_data, 0, sizeof(result.sa_data)); - - // We can not guarantee ABI compatibility between platforms so we copy the fields manually - switch (result.sa_family) { - case AF_INET: { - sockaddr_in* result_in = reinterpret_cast(&result); - result_in->sin_port = ctr_addr.in.sin_port; - result_in->sin_addr.s_addr = ctr_addr.in.sin_addr; - memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero)); - break; - } - default: - ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); - break; - } - return result; - } - - /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr - static CTRSockAddr FromPlatform(sockaddr const& addr) { - CTRSockAddr result; - result.raw.sa_family = static_cast(addr.sa_family); - // We can not guarantee ABI compatibility between platforms so we copy the fields manually - switch (result.raw.sa_family) { - case AF_INET: { - sockaddr_in const* addr_in = reinterpret_cast(&addr); - result.raw.len = sizeof(CTRSockAddrIn); - result.in.sin_port = addr_in->sin_port; - result.in.sin_addr = addr_in->sin_addr.s_addr; - break; - } - default: - ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); - break; - } - return result; - } -}; - -/// Holds info about the currently open sockets -static std::unordered_map open_sockets; - -/// Close all open sockets -static void CleanupSockets() { - for (auto sock : open_sockets) - closesocket(sock.second.socket_fd); - open_sockets.clear(); -} - -static void Socket(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 domain = cmd_buffer[1]; // Address family - u32 type = cmd_buffer[2]; - u32 protocol = cmd_buffer[3]; - - // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use - if (protocol != 0) { - cmd_buffer[1] = - UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code - return; - } - - if (domain != AF_INET) { - cmd_buffer[1] = - UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code - return; - } - - if (type != SOCK_DGRAM && type != SOCK_STREAM) { - cmd_buffer[1] = - UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code - return; - } - - u32 ret = static_cast(::socket(domain, type, protocol)); - - if ((s32)ret != SOCKET_ERROR_VALUE) - open_sockets[ret] = {ret, true}; - - int result = 0; - if ((s32)ret == SOCKET_ERROR_VALUE) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[0] = IPC::MakeHeader(2, 2, 0); - cmd_buffer[1] = result; - cmd_buffer[2] = ret; -} - -static void Bind(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 len = cmd_buffer[2]; - - // Virtual address of the sock_addr structure - VAddr sock_addr_addr = cmd_buffer[6]; - if (!Memory::IsValidVirtualAddress(sock_addr_addr)) { - cmd_buffer[1] = -1; // TODO(Subv): Correct code - return; - } - - CTRSockAddr ctr_sock_addr; - Memory::ReadBlock(sock_addr_addr, reinterpret_cast(&ctr_sock_addr), sizeof(CTRSockAddr)); - - sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr); - - int ret = ::bind(socket_handle, &sock_addr, std::max(sizeof(sock_addr), len)); - - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[0] = IPC::MakeHeader(5, 2, 0); - cmd_buffer[1] = result; - cmd_buffer[2] = ret; -} - -static void Fcntl(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 ctr_cmd = cmd_buffer[2]; - u32 ctr_arg = cmd_buffer[3]; - - int result = 0; - u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX) - SCOPE_EXIT({ - cmd_buffer[1] = result; - cmd_buffer[2] = posix_ret; - }); - - if (ctr_cmd == 3) { // F_GETFL -#ifdef _WIN32 - posix_ret = 0; - auto iter = open_sockets.find(socket_handle); - if (iter != open_sockets.end() && iter->second.blocking == false) - posix_ret |= 4; // O_NONBLOCK -#else - int ret = ::fcntl(socket_handle, F_GETFL, 0); - if (ret == SOCKET_ERROR_VALUE) { - posix_ret = TranslateError(GET_ERRNO); - return; - } - posix_ret = 0; - if (ret & O_NONBLOCK) - posix_ret |= 4; // O_NONBLOCK -#endif - } else if (ctr_cmd == 4) { // F_SETFL -#ifdef _WIN32 - unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; - int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); - if (ret == SOCKET_ERROR_VALUE) { - posix_ret = TranslateError(GET_ERRNO); - return; - } - auto iter = open_sockets.find(socket_handle); - if (iter != open_sockets.end()) - iter->second.blocking = (tmp == 0); -#else - int flags = ::fcntl(socket_handle, F_GETFL, 0); - if (flags == SOCKET_ERROR_VALUE) { - posix_ret = TranslateError(GET_ERRNO); - return; - } - - flags &= ~O_NONBLOCK; - if (ctr_arg & 4) // O_NONBLOCK - flags |= O_NONBLOCK; - - int ret = ::fcntl(socket_handle, F_SETFL, flags); - if (ret == SOCKET_ERROR_VALUE) { - posix_ret = TranslateError(GET_ERRNO); - return; - } -#endif - } else { - LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd); - posix_ret = TranslateError(EINVAL); // TODO: Find the correct error - return; - } -} - -static void Listen(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 backlog = cmd_buffer[2]; - - int ret = ::listen(socket_handle, backlog); - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[0] = IPC::MakeHeader(3, 2, 0); - cmd_buffer[1] = result; - cmd_buffer[2] = ret; -} - -static void Accept(Interface* self) { - // TODO(Subv): Calling this function on a blocking socket will block the emu thread, - // preventing graceful shutdown when closing the emulator, this can be fixed by always - // performing nonblocking operations and spinlock until the data is available - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - socklen_t max_addr_len = static_cast(cmd_buffer[2]); - sockaddr addr; - socklen_t addr_len = sizeof(addr); - u32 ret = static_cast(::accept(socket_handle, &addr, &addr_len)); - - if ((s32)ret != SOCKET_ERROR_VALUE) - open_sockets[ret] = {ret, true}; - - int result = 0; - if ((s32)ret == SOCKET_ERROR_VALUE) { - ret = TranslateError(GET_ERRNO); - } else { - CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr); - Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr)); - } - - cmd_buffer[0] = IPC::MakeHeader(4, 2, 2); - cmd_buffer[1] = result; - cmd_buffer[2] = ret; - cmd_buffer[3] = IPC::StaticBufferDesc(static_cast(max_addr_len), 0); -} - -static void GetHostId(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - - char name[128]; - gethostname(name, sizeof(name)); - addrinfo hints = {}; - addrinfo* res; - - hints.ai_family = AF_INET; - getaddrinfo(name, nullptr, &hints, &res); - sockaddr_in* sock_addr = reinterpret_cast(res->ai_addr); - in_addr* addr = &sock_addr->sin_addr; - - cmd_buffer[2] = addr->s_addr; - cmd_buffer[1] = 0; - freeaddrinfo(res); -} - -static void Close(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - - int ret = 0; - open_sockets.erase(socket_handle); - - ret = closesocket(socket_handle); - - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[2] = ret; - cmd_buffer[1] = result; -} - -static void SendTo(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 len = cmd_buffer[2]; - u32 flags = cmd_buffer[3]; - u32 addr_len = cmd_buffer[4]; - - VAddr input_buff_address = cmd_buffer[8]; - if (!Memory::IsValidVirtualAddress(input_buff_address)) { - cmd_buffer[1] = -1; // TODO(Subv): Find the right error code - return; - } - - // Memory address of the dest_addr structure - VAddr dest_addr_addr = cmd_buffer[10]; - if (!Memory::IsValidVirtualAddress(dest_addr_addr)) { - cmd_buffer[1] = -1; // TODO(Subv): Find the right error code - return; - } - - std::vector input_buff(len); - Memory::ReadBlock(input_buff_address, input_buff.data(), input_buff.size()); - - CTRSockAddr ctr_dest_addr; - Memory::ReadBlock(dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr)); - - int ret = -1; - if (addr_len > 0) { - sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr); - ret = ::sendto(socket_handle, reinterpret_cast(input_buff.data()), len, flags, - &dest_addr, sizeof(dest_addr)); - } else { - ret = ::sendto(socket_handle, reinterpret_cast(input_buff.data()), len, flags, - nullptr, 0); - } - - int result = 0; - if (ret == SOCKET_ERROR_VALUE) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[2] = ret; - cmd_buffer[1] = result; -} - -static void RecvFrom(Interface* self) { - // TODO(Subv): Calling this function on a blocking socket will block the emu thread, - // preventing graceful shutdown when closing the emulator, this can be fixed by always - // performing nonblocking operations and spinlock until the data is available - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 len = cmd_buffer[2]; - u32 flags = cmd_buffer[3]; - - struct { - u32 output_buffer_descriptor; - u32 output_buffer_addr; - u32 address_buffer_descriptor; - u32 output_src_address_buffer; - } buffer_parameters; - - std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters)); - - if (!Memory::IsValidVirtualAddress(buffer_parameters.output_buffer_addr)) { - cmd_buffer[1] = -1; // TODO(Subv): Find the right error code - return; - } - - if (!Memory::IsValidVirtualAddress(buffer_parameters.output_src_address_buffer)) { - cmd_buffer[1] = -1; // TODO(Subv): Find the right error code - return; - } - - std::vector output_buff(len); - sockaddr src_addr; - socklen_t src_addr_len = sizeof(src_addr); - int ret = ::recvfrom(socket_handle, reinterpret_cast(output_buff.data()), len, flags, - &src_addr, &src_addr_len); - - if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) { - CTRSockAddr ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); - Memory::WriteBlock(buffer_parameters.output_src_address_buffer, &ctr_src_addr, - sizeof(ctr_src_addr)); - } - - int result = 0; - int total_received = ret; - if (ret == SOCKET_ERROR_VALUE) { - ret = TranslateError(GET_ERRNO); - total_received = 0; - } else { - // Write only the data we received to avoid overwriting parts of the buffer with zeros - Memory::WriteBlock(buffer_parameters.output_buffer_addr, output_buff.data(), - total_received); - } - - cmd_buffer[1] = result; - cmd_buffer[2] = ret; - cmd_buffer[3] = total_received; -} - -static void Poll(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 nfds = cmd_buffer[1]; - int timeout = cmd_buffer[2]; - - VAddr input_fds_addr = cmd_buffer[6]; - VAddr output_fds_addr = cmd_buffer[0x104 >> 2]; - if (!Memory::IsValidVirtualAddress(input_fds_addr) || - !Memory::IsValidVirtualAddress(output_fds_addr)) { - cmd_buffer[1] = -1; // TODO(Subv): Find correct error code. - return; - } - - std::vector ctr_fds(nfds); - Memory::ReadBlock(input_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD)); - - // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different - // sizes) - // so we have to copy the data - std::vector platform_pollfd(nfds); - std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform); - - int ret = ::poll(platform_pollfd.data(), nfds, timeout); - - // Now update the output pollfd structure - std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(), - CTRPollFD::FromPlatform); - - Memory::WriteBlock(output_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD)); - - int result = 0; - if (ret == SOCKET_ERROR_VALUE) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[1] = result; - cmd_buffer[2] = ret; -} - -static void GetSockName(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - - // Memory address of the ctr_dest_addr structure - VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2]; - - sockaddr dest_addr; - socklen_t dest_addr_len = sizeof(dest_addr); - int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len); - - if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) { - CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); - Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr)); - } else { - cmd_buffer[1] = -1; // TODO(Subv): Verify error - return; - } - - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[2] = ret; - cmd_buffer[1] = result; -} - -static void Shutdown(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - int how = cmd_buffer[2]; - - int ret = ::shutdown(socket_handle, how); - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - cmd_buffer[2] = ret; - cmd_buffer[1] = result; -} - -static void GetPeerName(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - - // Memory address of the ctr_dest_addr structure - VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2]; - - sockaddr dest_addr; - socklen_t dest_addr_len = sizeof(dest_addr); - int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len); - - if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) { - CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); - Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr)); - } else { - cmd_buffer[1] = -1; - return; - } - - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[2] = ret; - cmd_buffer[1] = result; -} - -static void Connect(Interface* self) { - // TODO(Subv): Calling this function on a blocking socket will block the emu thread, - // preventing graceful shutdown when closing the emulator, this can be fixed by always - // performing nonblocking operations and spinlock until the data is available - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - - // Memory address of the ctr_input_addr structure - VAddr ctr_input_addr_addr = cmd_buffer[6]; - if (!Memory::IsValidVirtualAddress(ctr_input_addr_addr)) { - cmd_buffer[1] = -1; // TODO(Subv): Verify error - return; - } - - CTRSockAddr ctr_input_addr; - Memory::ReadBlock(ctr_input_addr_addr, &ctr_input_addr, sizeof(ctr_input_addr)); - - sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr); - int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr)); - int result = 0; - if (ret != 0) - ret = TranslateError(GET_ERRNO); - - cmd_buffer[0] = IPC::MakeHeader(6, 2, 0); - cmd_buffer[1] = result; - cmd_buffer[2] = ret; -} - -static void InitializeSockets(Interface* self) { -// TODO(Subv): Implement -#ifdef _WIN32 - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); -#endif - - u32* cmd_buffer = Kernel::GetCommandBuffer(); - cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); - cmd_buffer[1] = RESULT_SUCCESS.raw; -} - -static void ShutdownSockets(Interface* self) { - // TODO(Subv): Implement - CleanupSockets(); - -#ifdef _WIN32 - WSACleanup(); -#endif - - u32* cmd_buffer = Kernel::GetCommandBuffer(); - cmd_buffer[1] = 0; -} - -static void GetSockOpt(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 level = cmd_buffer[2]; - int optname = TranslateSockOpt(cmd_buffer[3]); - socklen_t optlen = (socklen_t)cmd_buffer[4]; - - int ret = 0; - int err = 0; - - if (optname < 0) { -#ifdef _WIN32 - err = WSAEINVAL; -#else - err = EINVAL; -#endif - } else { - // 0x100 = static buffer offset (bytes) - // + 0x4 = 2nd pointer (u32) position - // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*) - char* optval = reinterpret_cast(Memory::GetPointer(cmd_buffer[0x104 >> 2])); - - err = ::getsockopt(socket_handle, level, optname, optval, &optlen); - if (err == SOCKET_ERROR_VALUE) { - err = TranslateError(GET_ERRNO); - } - } - - cmd_buffer[0] = IPC::MakeHeader(0x11, 4, 2); - cmd_buffer[1] = ret; - cmd_buffer[2] = err; - cmd_buffer[3] = optlen; -} - -static void SetSockOpt(Interface* self) { - u32* cmd_buffer = Kernel::GetCommandBuffer(); - u32 socket_handle = cmd_buffer[1]; - u32 level = cmd_buffer[2]; - int optname = TranslateSockOpt(cmd_buffer[3]); - - int ret = 0; - int err = 0; - - if (optname < 0) { -#ifdef _WIN32 - err = WSAEINVAL; -#else - err = EINVAL; -#endif - } else { - socklen_t optlen = static_cast(cmd_buffer[4]); - const char* optval = reinterpret_cast(Memory::GetPointer(cmd_buffer[8])); - - err = static_cast(::setsockopt(socket_handle, level, optname, optval, optlen)); - if (err == SOCKET_ERROR_VALUE) { - err = TranslateError(GET_ERRNO); - } - } - - cmd_buffer[0] = IPC::MakeHeader(0x12, 4, 4); - cmd_buffer[1] = ret; - cmd_buffer[2] = err; -} - const Interface::FunctionInfo FunctionTable[] = { - {0x00010044, InitializeSockets, "InitializeSockets"}, - {0x000200C2, Socket, "Socket"}, - {0x00030082, Listen, "Listen"}, - {0x00040082, Accept, "Accept"}, - {0x00050084, Bind, "Bind"}, - {0x00060084, Connect, "Connect"}, + {0x00010044, nullptr, "InitializeSockets"}, + {0x000200C2, nullptr, "Socket"}, + {0x00030082, nullptr, "Listen"}, + {0x00040082, nullptr, "Accept"}, + {0x00050084, nullptr, "Bind"}, + {0x00060084, nullptr, "Connect"}, {0x00070104, nullptr, "recvfrom_other"}, - {0x00080102, RecvFrom, "RecvFrom"}, + {0x00080102, nullptr, "RecvFrom"}, {0x00090106, nullptr, "sendto_other"}, - {0x000A0106, SendTo, "SendTo"}, - {0x000B0042, Close, "Close"}, - {0x000C0082, Shutdown, "Shutdown"}, + {0x000A0106, nullptr, "SendTo"}, + {0x000B0042, nullptr, "Close"}, + {0x000C0082, nullptr, "Shutdown"}, {0x000D0082, nullptr, "GetHostByName"}, {0x000E00C2, nullptr, "GetHostByAddr"}, {0x000F0106, nullptr, "GetAddrInfo"}, {0x00100102, nullptr, "GetNameInfo"}, - {0x00110102, GetSockOpt, "GetSockOpt"}, - {0x00120104, SetSockOpt, "SetSockOpt"}, - {0x001300C2, Fcntl, "Fcntl"}, - {0x00140084, Poll, "Poll"}, + {0x00110102, nullptr, "GetSockOpt"}, + {0x00120104, nullptr, "SetSockOpt"}, + {0x001300C2, nullptr, "Fcntl"}, + {0x00140084, nullptr, "Poll"}, {0x00150042, nullptr, "SockAtMark"}, - {0x00160000, GetHostId, "GetHostId"}, - {0x00170082, GetSockName, "GetSockName"}, - {0x00180082, GetPeerName, "GetPeerName"}, - {0x00190000, ShutdownSockets, "ShutdownSockets"}, + {0x00160000, nullptr, "GetHostId"}, + {0x00170082, nullptr, "GetSockName"}, + {0x00180082, nullptr, "GetPeerName"}, + {0x00190000, nullptr, "ShutdownSockets"}, {0x001A00C0, nullptr, "GetNetworkOpt"}, {0x001B0040, nullptr, "ICMPSocket"}, {0x001C0104, nullptr, "ICMPPing"}, @@ -908,12 +47,7 @@ SOC_U::SOC_U() { Register(FunctionTable); } -SOC_U::~SOC_U() { - CleanupSockets(); -#ifdef _WIN32 - WSACleanup(); -#endif -} +SOC_U::~SOC_U() {} } // namespace SOC } // namespace Service