From d680b7972585a58d5b789f7ceba01f820750c84f Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Sun, 17 Dec 2023 17:50:24 +0100 Subject: [PATCH] Implement some missing SOC functionality (#7176) * Implement some missing SOC functionality * Add LOG_POLL macro for debugging * Fix compilation * Temporary fix for Android * Temporary fix for Android (for real) * Apply suggestions * Add stubbed notice to android sockatmark * Apply suggestions --- src/core/hle/service/soc/soc_u.cpp | 923 ++++++++++++++++++++--------- src/core/hle/service/soc/soc_u.h | 69 ++- 2 files changed, 677 insertions(+), 315 deletions(-) diff --git a/src/core/hle/service/soc/soc_u.cpp b/src/core/hle/service/soc/soc_u.cpp index 00a814a4f..937c67900 100644 --- a/src/core/hle/service/soc/soc_u.cpp +++ b/src/core/hle/service/soc/soc_u.cpp @@ -65,6 +65,10 @@ SERIALIZE_EXPORT_IMPL(Service::SOC::SOC_U) +// Change according the debugging needs +#define LOG_SEND_RECV LOG_TRACE +#define LOG_POLL LOG_TRACE + namespace Service::SOC { const s32 SOCKET_ERROR_VALUE = -1; @@ -226,9 +230,16 @@ static const std::unordered_map error_map = {{ #endif {ENOSYS, 55}, {ERRNO(ENOTCONN), 56}, +#ifdef _WIN32 + {WSAESHUTDOWN, 56}, +#endif {ENOTDIR, 57}, {ERRNO(ENOTEMPTY), 58}, +#ifdef _WIN32 + {ERRNO(ENOTSOCK), 8}, +#else {ERRNO(ENOTSOCK), 59}, +#endif {ENOTSUP, 60}, {ENOTTY, 61}, {ENXIO, 62}, @@ -411,6 +422,42 @@ u32 SOC_U::SetSocketBlocking(SocketHolder& socket_holder, bool blocking) { return posix_ret; } +std::optional> SOC_U::GetSocketHolder(u32 ctr_socket_fd, + u32 process_id, + IPC::RequestParser& rp) { + if (initialized_processes.find(process_id) == initialized_processes.end()) { + LOG_DEBUG(Service_SOC, "Process not initialized: pid={}", process_id); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERR_NOT_INITIALIZED); + return std::nullopt; + } + auto fd_info = created_sockets.find(ctr_socket_fd); + if (fd_info == created_sockets.end()) { + LOG_DEBUG(Service_SOC, "Invalid socket: pid={}, fd={}", process_id, ctr_socket_fd); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERR_INVALID_SOCKET_DESCRIPTOR); + return std::nullopt; + } + if (fd_info->second.ownerProcess != process_id && !fd_info->second.isGlobal) { + LOG_DEBUG(Service_SOC, "Invalid process owner: pid={}, fd={}, owner_pid={}", process_id, + ctr_socket_fd, fd_info->second.ownerProcess); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERR_WRONG_PROCESS); + return std::nullopt; + } + return std::ref(fd_info->second); +} + +void SOC_U::CloseAndDeleteAllSockets(s32 process_id) { + std::erase_if(created_sockets, [process_id](const auto& entry) { + if (process_id == -1 || entry.second.ownerProcess == static_cast(process_id)) { + closesocket(entry.second.socket_fd); + return true; + } + return false; + }); +} + static u32 SendRecvFlagsToPlatform(u32 flags) { u32 ret = 0; if (flags & 1) { @@ -523,7 +570,7 @@ struct CTRPollFD { CTRPollFD result; result.events.hex = Events::TranslateTo3DS(fd.events, has_libctru_bug).hex; result.revents.hex = Events::TranslateTo3DS(fd.revents, has_libctru_bug).hex; - for (const auto& socket : socu.open_sockets) { + for (const auto& socket : socu.created_sockets) { if (socket.second.socket_fd == fd.fd) { result.fd = socket.first; break; @@ -538,11 +585,9 @@ struct CTRPollFD { u8 unused = 0; result.events = Events::TranslateToPlatform(fd.events, false, haslibctrbug); result.revents = Events::TranslateToPlatform(fd.revents, true, unused); - auto iter = socu.open_sockets.find(fd.fd); - result.fd = (iter != socu.open_sockets.end()) ? iter->second.socket_fd : 0; - if (iter == socu.open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", fd.fd); - } + auto iter = socu.created_sockets.find(fd.fd); + ASSERT(iter != socu.created_sockets.end()); + result.fd = iter->second.socket_fd; return result; } }; @@ -684,6 +729,19 @@ struct CTRAddrInfo { } }; +struct HostByNameData { + static constexpr u32 max_entries = 24; + + u16_le addr_type; + u16_le addr_len; + u16_le addr_count; + u16_le alias_count; + std::array h_name; + std::array, max_entries> aliases; + std::array, max_entries> addresses; +}; +static_assert(sizeof(HostByNameData) == 0x1A88, "Invalid HostByNameData size"); + static u32 NameInfoFlagsToPlatform(u32 flags) { u32 ret = 0; if (flags & 1) { @@ -706,31 +764,12 @@ static u32 NameInfoFlagsToPlatform(u32 flags) { static_assert(sizeof(CTRAddrInfo) == 0x130, "Size of CTRAddrInfo is not correct"); -void SOC_U::PreTimerAdjust() { - adjust_value_last = std::chrono::steady_clock::now(); -} - -void SOC_U::PostTimerAdjust(Kernel::HLERequestContext& ctx, const std::string& caller_method) { - std::chrono::time_point new_timer = std::chrono::steady_clock::now(); - ASSERT(new_timer >= adjust_value_last); - ctx.SleepClientThread( - fmt::format("soc_u::{}", caller_method), - std::chrono::duration_cast(new_timer - adjust_value_last), - nullptr); -} - -void SOC_U::CleanupSockets() { - for (const auto& sock : open_sockets) - closesocket(sock.second.socket_fd); - open_sockets.clear(); -} - void SOC_U::Socket(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 domain = SocketDomainToPlatform(rp.Pop()); // Address family - u32 type = SocketTypeToPlatform(rp.Pop()); - u32 protocol = SocketProtocolToPlatform(rp.Pop()); - rp.PopPID(); + const u32 domain = SocketDomainToPlatform(rp.Pop()); // Address family + const u32 type = SocketTypeToPlatform(rp.Pop()); + const u32 protocol = SocketProtocolToPlatform(rp.Pop()); + const u32 pid = rp.PopPID(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); @@ -757,7 +796,13 @@ void SOC_U::Socket(Kernel::HLERequestContext& ctx) { u32 socketHandle = GetNextSocketID(); if ((s64)ret != SOCKET_ERROR_VALUE) { - open_sockets[socketHandle] = {static_cast(ret), true}; + created_sockets[socketHandle] = { + .socket_fd = static_cast(ret), + .blocking = true, + .isGlobal = false, + .shutdown_rd = false, + .ownerProcess = pid, + }; #if _WIN32 // Disable UDP connection reset int new_behavior = 0; @@ -767,37 +812,44 @@ void SOC_U::Socket(Kernel::HLERequestContext& ctx) { #endif } - if ((s64)ret == SOCKET_ERROR_VALUE) + if ((s64)ret == SOCKET_ERROR_VALUE) { ret = TranslateError(GET_ERRNO); + } else { + ret = socketHandle; + } + + LOG_DEBUG(Service_SOC, "called, pid={}, ret={}", pid, static_cast(ret)); rb.Push(RESULT_SUCCESS); - rb.Push(socketHandle); + rb.Push(static_cast(ret)); } void SOC_U::Bind(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const u32 socket_handle = rp.Pop(); + const u32 len = rp.Pop(); + const u32 pid = rp.PopPID(); + auto sock_addr_buf = rp.PopStaticBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - u32 len = rp.Pop(); - rp.PopPID(); - auto sock_addr_buf = rp.PopStaticBuffer(); + SocketHolder& holder = socket_holder_optional->get(); CTRSockAddr ctr_sock_addr; std::memcpy(&ctr_sock_addr, sock_addr_buf.data(), len); sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr); - s32 ret = ::bind(fd_info->second.socket_fd, &sock_addr, sizeof(sock_addr)); + s32 ret = ::bind(holder.socket_fd, &sock_addr, sizeof(sock_addr)); if (ret != 0) ret = TranslateError(GET_ERRNO); + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(ret); @@ -805,20 +857,22 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) { void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const u32 socket_handle = rp.Pop(); + const u32 ctr_cmd = rp.Pop(); + const u32 ctr_arg = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - u32 ctr_cmd = rp.Pop(); - u32 ctr_arg = rp.Pop(); - rp.PopPID(); + SocketHolder& holder = socket_holder_optional->get(); u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX) SCOPE_EXIT({ + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(posix_ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(posix_ret); @@ -826,10 +880,10 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) { if (ctr_cmd == 3) { // F_GETFL posix_ret = 0; - if (GetSocketBlocking(fd_info->second) == false) + if (GetSocketBlocking(holder) == false) posix_ret |= 4; // O_NONBLOCK } else if (ctr_cmd == 4) { // F_SETFL - posix_ret = SetSocketBlocking(fd_info->second, !(ctr_arg & 4)); + posix_ret = SetSocketBlocking(holder, !(ctr_arg & 4)); } else { LOG_ERROR(Service_SOC, "Unsupported command ({}) in fcntl call", ctr_cmd); posix_ret = TranslateError(EINVAL); // TODO: Find the correct error @@ -839,69 +893,144 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) { void SOC_U::Listen(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const u32 socket_handle = rp.Pop(); + const u32 backlog = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - u32 backlog = rp.Pop(); - rp.PopPID(); + SocketHolder& holder = socket_holder_optional->get(); - s32 ret = ::listen(fd_info->second.socket_fd, backlog); + s32 ret = ::listen(holder.socket_fd, backlog); if (ret != 0) ret = TranslateError(GET_ERRNO); + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(ret); } void SOC_U::Accept(Kernel::HLERequestContext& ctx) { - // 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 IPC::RequestParser rp(ctx); const auto socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const auto max_addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - const auto max_addr_len = rp.Pop(); - rp.PopPID(); - sockaddr addr; - socklen_t addr_len = sizeof(addr); - u32 ret = static_cast(::accept(fd_info->second.socket_fd, &addr, &addr_len)); + SocketHolder& holder = socket_holder_optional->get(); - if (static_cast(ret) != SOCKET_ERROR_VALUE) { - u32 socketID = GetNextSocketID(); - open_sockets[socketID] = {static_cast(ret), true}; - ret = socketID; + struct AsyncData { + // Input + u32 max_addr_len{}; + SocketHolder* fd_info; + u32 pid; + u32 socket_handle; + + // Output + s32 ret{}; + int accept_error; + sockaddr addr; + }; + + auto async_data = std::make_shared(); + async_data->max_addr_len = max_addr_len; + async_data->fd_info = &holder; + async_data->pid = pid; + async_data->socket_handle = socket_handle; + + ctx.RunAsync( + [async_data](Kernel::HLERequestContext& ctx) { + socklen_t addr_len = sizeof(async_data->addr); + async_data->ret = static_cast( + ::accept(async_data->fd_info->socket_fd, &async_data->addr, &addr_len)); + async_data->accept_error = (async_data->ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0; + return 0; + }, + [this, async_data](Kernel::HLERequestContext& ctx) { + if (static_cast(async_data->ret) != SOCKET_ERROR_VALUE) { + u32 socketID = GetNextSocketID(); + created_sockets[socketID] = { + .socket_fd = static_cast(async_data->ret), + .blocking = true, + .isGlobal = false, + .shutdown_rd = false, + .ownerProcess = async_data->pid, + }; + async_data->ret = socketID; + } + + CTRSockAddr ctr_addr; + std::vector ctr_addr_buf(sizeof(ctr_addr)); + if (static_cast(async_data->ret) == SOCKET_ERROR_VALUE) { + async_data->ret = TranslateError(async_data->accept_error); + } else { + ctr_addr = CTRSockAddr::FromPlatform(async_data->addr); + std::memcpy(ctr_addr_buf.data(), &ctr_addr, sizeof(ctr_addr)); + } + + if (ctr_addr_buf.size() > async_data->max_addr_len) { + LOG_WARNING(Frontend, "CTRSockAddr is too long, truncating data."); + ctr_addr_buf.resize(async_data->max_addr_len); + } + + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", async_data->socket_handle, + static_cast(async_data->ret)); + + IPC::RequestBuilder rb(ctx, 0x04, 2, 2); + rb.Push(RESULT_SUCCESS); + rb.Push(async_data->ret); + rb.PushStaticBuffer(std::move(ctr_addr_buf), 0); + }); +} + +void SOC_U::SockAtMark(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto socket_handle = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; } + [[maybe_unused]] SocketHolder& holder = socket_holder_optional->get(); - CTRSockAddr ctr_addr; - std::vector ctr_addr_buf(sizeof(ctr_addr)); - if (static_cast(ret) == SOCKET_ERROR_VALUE) { + bool is_at_mark = false; + int func_res = 0; +#ifdef _WIN32 + u_long atMark = 0; + func_res = ::ioctlsocket(holder.socket_fd, SIOCATMARK, &atMark); + is_at_mark = atMark != 0; +#else +#ifdef ANDROID + func_res = 0; + LOG_WARNING(Service_SOC, "(STUBBED) called"); +#else + func_res = ::sockatmark(holder.socket_fd); +#endif + is_at_mark = func_res > 0; +#endif // _WIN32 + + u32 ret; + if (func_res == SOCKET_ERROR_VALUE) { ret = TranslateError(GET_ERRNO); } else { - ctr_addr = CTRSockAddr::FromPlatform(addr); - std::memcpy(ctr_addr_buf.data(), &ctr_addr, sizeof(ctr_addr)); + ret = is_at_mark ? 1 : 0; } - if (ctr_addr_buf.size() > max_addr_len) { - LOG_WARNING(Frontend, "CTRSockAddr is too long, truncating data."); - ctr_addr_buf.resize(max_addr_len); - } + LOG_POLL(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(ret)); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(ret); - rb.PushStaticBuffer(std::move(ctr_addr_buf), 0); } void SOC_U::GetHostId(Kernel::HLERequestContext& ctx) { @@ -920,24 +1049,23 @@ void SOC_U::GetHostId(Kernel::HLERequestContext& ctx) { void SOC_U::Close(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const u32 socket_handle = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - rp.PopPID(); + SocketHolder& holder = socket_holder_optional->get(); s32 ret = 0; + ret = closesocket(holder.socket_fd); - ret = closesocket(fd_info->second.socket_fd); - - open_sockets.erase(socket_handle); - - if (ret != 0) + if (ret != 0) { ret = TranslateError(GET_ERRNO); + } + + LOG_DEBUG(Service_SOC, "pid={}, fd={}, ret={}", pid, socket_handle, static_cast(ret)); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); @@ -947,32 +1075,31 @@ void SOC_U::Close(Kernel::HLERequestContext& ctx) { void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const u32 socket_handle = rp.Pop(); - const auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); - return; - } const u32 len = rp.Pop(); u32 flags = SendRecvFlagsToPlatform(rp.Pop()); + const u32 addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + const auto dest_addr_buffer = rp.PopStaticBuffer(); + auto input_mapped_buff = rp.PopMappedBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + bool dont_wait = (flags & MSGCUSTOM_HANDLE_DONTWAIT) != 0; flags &= ~MSGCUSTOM_HANDLE_DONTWAIT; #ifdef _WIN32 - bool was_blocking = GetSocketBlocking(fd_info->second); + bool was_blocking = GetSocketBlocking(holder); if (dont_wait && was_blocking) { - SetSocketBlocking(fd_info->second, false); + SetSocketBlocking(holder, false); } #else if (dont_wait) { flags |= MSG_DONTWAIT; } #endif // _WIN32 - const u32 addr_len = rp.Pop(); - rp.PopPID(); - const auto dest_addr_buffer = rp.PopStaticBuffer(); - - auto input_mapped_buff = rp.PopMappedBuffer(); std::vector input_buff(len); input_mapped_buff.Read(input_buff.data(), 0, std::min(input_mapped_buff.GetSize(), static_cast(len))); @@ -982,11 +1109,11 @@ void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) { CTRSockAddr ctr_dest_addr; std::memcpy(&ctr_dest_addr, dest_addr_buffer.data(), sizeof(ctr_dest_addr)); sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr); - ret = static_cast(::sendto(fd_info->second.socket_fd, + ret = static_cast(::sendto(holder.socket_fd, reinterpret_cast(input_buff.data()), len, flags, &dest_addr, sizeof(dest_addr))); } else { - ret = static_cast(::sendto(fd_info->second.socket_fd, + ret = static_cast(::sendto(holder.socket_fd, reinterpret_cast(input_buff.data()), len, flags, nullptr, 0)); } @@ -995,7 +1122,7 @@ void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) { #ifdef _WIN32 if (dont_wait && was_blocking) { - SetSocketBlocking(fd_info->second, true); + SetSocketBlocking(holder, true); } #endif @@ -1003,50 +1130,39 @@ void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) { ret = TranslateError(send_error); } + LOG_SEND_RECV(Service_SOC, "called, fd={}, ret={}", socket_handle, static_cast(ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(ret); } -void SOC_U::SendTo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); - return; - } - u32 len = rp.Pop(); - u32 flags = SendRecvFlagsToPlatform(rp.Pop()); +s32 SOC_U::SendToImpl(SocketHolder& holder, u32 len, u32 flags, u32 addr_len, + const std::vector& input_buff, const u8* dest_addr_buff) { + bool dont_wait = (flags & MSGCUSTOM_HANDLE_DONTWAIT) != 0; flags &= ~MSGCUSTOM_HANDLE_DONTWAIT; #ifdef _WIN32 - bool was_blocking = GetSocketBlocking(fd_info->second); + bool was_blocking = GetSocketBlocking(holder); if (dont_wait && was_blocking) { - SetSocketBlocking(fd_info->second, false); + SetSocketBlocking(holder, false); } #else if (dont_wait) { flags |= MSG_DONTWAIT; } #endif // _WIN32 - u32 addr_len = rp.Pop(); - rp.PopPID(); - auto input_buff = rp.PopStaticBuffer(); - auto dest_addr_buff = rp.PopStaticBuffer(); s32 ret = -1; if (addr_len > 0) { CTRSockAddr ctr_dest_addr; - std::memcpy(&ctr_dest_addr, dest_addr_buff.data(), sizeof(ctr_dest_addr)); + std::memcpy(&ctr_dest_addr, dest_addr_buff, sizeof(ctr_dest_addr)); sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr); - ret = static_cast(::sendto(fd_info->second.socket_fd, + ret = static_cast(::sendto(holder.socket_fd, reinterpret_cast(input_buff.data()), len, flags, &dest_addr, sizeof(dest_addr))); } else { - ret = static_cast(::sendto(fd_info->second.socket_fd, + ret = static_cast(::sendto(holder.socket_fd, reinterpret_cast(input_buff.data()), len, flags, nullptr, 0)); } @@ -1055,56 +1171,100 @@ void SOC_U::SendTo(Kernel::HLERequestContext& ctx) { #ifdef _WIN32 if (dont_wait && was_blocking) { - SetSocketBlocking(fd_info->second, true); + SetSocketBlocking(holder, true); } #endif if (ret == SOCKET_ERROR_VALUE) ret = TranslateError(send_error); + return ret; +} + +void SOC_U::SendToSingle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const u32 socket_handle = rp.Pop(); + u32 len = rp.Pop(); + u32 flags = SendRecvFlagsToPlatform(rp.Pop()); + u32 addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + auto input_buff = rp.PopStaticBuffer(); + auto dest_addr_buff = rp.PopStaticBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + + s32 ret = SendToImpl(holder, len, flags, addr_len, input_buff, dest_addr_buff.data()); + + LOG_SEND_RECV(Service_SOC, "called, fd={}, ret={}", socket_handle, static_cast(ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(ret); } +// A poll busy loop has to be used when recieving from a blocking socket +// because calling shutdown on a socket that's currently in a blocking +// recv call does not make it return, which causes some games to hang. +void SOC_U::RecvBusyWaitForEvent(SocketHolder& holder) { + constexpr int timeout_ms = 100; + + while (true) { + pollfd poll_fd{}; + poll_fd.fd = holder.socket_fd; + poll_fd.events = POLLIN; // Check for available data. + int res = ::poll(&poll_fd, 1, timeout_ms); + // Break if there is any event, error or socket RD shutdown. + if (res != 0 || holder.shutdown_rd) { + break; + } + } +} + void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); - return; - } + const u32 socket_handle = rp.Pop(); u32 len = rp.Pop(); u32 flags = SendRecvFlagsToPlatform(rp.Pop()); + u32 addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + Kernel::MappedBuffer& buffer = rp.PopMappedBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + bool dont_wait = (flags & MSGCUSTOM_HANDLE_DONTWAIT) != 0; flags &= ~MSGCUSTOM_HANDLE_DONTWAIT; #ifdef _WIN32 - bool was_blocking = GetSocketBlocking(fd_info->second); + bool was_blocking = GetSocketBlocking(holder); if (dont_wait && was_blocking) { - SetSocketBlocking(fd_info->second, false); + SetSocketBlocking(holder, false); } #else if (dont_wait) { flags |= MSG_DONTWAIT; } #endif // _WIN32 - u32 addr_len = rp.Pop(); - rp.PopPID(); - bool needs_async = GetSocketBlocking(fd_info->second) && !dont_wait; + bool needs_async = GetSocketBlocking(holder) && !dont_wait; struct AsyncData { // Input u32 len{}; u32 flags{}; u32 addr_len{}; SocketHolder* fd_info; + u32 socket_handle; #ifdef _WIN32 bool dont_wait; bool was_blocking; #endif + bool is_blocking; // Output s32 ret{}; @@ -1115,24 +1275,30 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) { }; auto async_data = std::make_shared(); - async_data->buffer = &rp.PopMappedBuffer(); + async_data->buffer = &buffer; async_data->ret = -1; async_data->len = len; async_data->flags = flags; async_data->addr_len = addr_len; async_data->output_buff.resize(len); async_data->addr_buff.resize(addr_len); - async_data->fd_info = &fd_info->second; + async_data->fd_info = &holder; + async_data->socket_handle = socket_handle; #ifdef _WIN32 async_data->dont_wait = dont_wait; async_data->was_blocking = was_blocking; #endif + async_data->is_blocking = needs_async; ctx.RunAsync( [async_data](Kernel::HLERequestContext& ctx) { sockaddr src_addr; socklen_t src_addr_len = sizeof(src_addr); CTRSockAddr ctr_src_addr; + // Windows, why do you have to be so special... + if (async_data->is_blocking) { + RecvBusyWaitForEvent(*async_data->fd_info); + } if (async_data->addr_len > 0) { async_data->ret = static_cast( ::recvfrom(async_data->fd_info->socket_fd, @@ -1165,6 +1331,9 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) { #else (void)this; #endif + LOG_SEND_RECV(Service_SOC, "called, fd={}, ret={}", async_data->socket_handle, + static_cast(async_data->ret)); + IPC::RequestBuilder rb(ctx, 0x07, 2, 4); rb.Push(RESULT_SUCCESS); rb.Push(async_data->ret); @@ -1176,42 +1345,44 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) { void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); - return; - } + const u32 socket_handle = rp.Pop(); u32 len = rp.Pop(); u32 flags = SendRecvFlagsToPlatform(rp.Pop()); + u32 addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + bool dont_wait = (flags & MSGCUSTOM_HANDLE_DONTWAIT) != 0; flags &= ~MSGCUSTOM_HANDLE_DONTWAIT; #ifdef _WIN32 - bool was_blocking = GetSocketBlocking(fd_info->second); + bool was_blocking = GetSocketBlocking(holder); if (dont_wait && was_blocking) { - SetSocketBlocking(fd_info->second, false); + SetSocketBlocking(holder, false); } #else if (dont_wait) { flags |= MSG_DONTWAIT; } #endif // _WIN32 - u32 addr_len = rp.Pop(); - rp.PopPID(); - bool needs_async = GetSocketBlocking(fd_info->second) && !dont_wait; + bool needs_async = GetSocketBlocking(holder) && !dont_wait; struct AsyncData { // Input u32 len{}; u32 flags{}; u32 addr_len{}; SocketHolder* fd_info; + u32 socket_handle; #ifdef _WIN32 bool dont_wait; bool was_blocking; #endif + bool is_blocking; // Output s32 ret{}; @@ -1227,17 +1398,22 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) { async_data->addr_len = addr_len; async_data->output_buff.resize(len); async_data->addr_buff.resize(addr_len); - async_data->fd_info = &fd_info->second; + async_data->fd_info = &holder; + async_data->socket_handle = socket_handle; #ifdef _WIN32 async_data->dont_wait = dont_wait; async_data->was_blocking = was_blocking; #endif + async_data->is_blocking = needs_async; ctx.RunAsync( [async_data](Kernel::HLERequestContext& ctx) { sockaddr src_addr; socklen_t src_addr_len = sizeof(src_addr); CTRSockAddr ctr_src_addr; + if (async_data->is_blocking) { + RecvBusyWaitForEvent(*async_data->fd_info); + } if (async_data->addr_len > 0) { // Only get src adr if input adr available async_data->ret = static_cast( @@ -1276,6 +1452,9 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) { // Write only the data we received to avoid overwriting parts of the buffer with zeros async_data->output_buff.resize(total_received); + LOG_SEND_RECV(Service_SOC, "called, fd={}, ret={}", async_data->socket_handle, + static_cast(async_data->ret)); + IPC::RequestBuilder rb(ctx, 0x08, 3, 4); rb.Push(RESULT_SUCCESS); rb.Push(async_data->ret); @@ -1290,7 +1469,7 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); u32 nfds = rp.Pop(); s32 timeout = rp.Pop(); - rp.PopPID(); + const u32 pid = rp.PopPID(); auto input_fds = rp.PopStaticBuffer(); struct AsyncData { @@ -1320,6 +1499,9 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) { async_data->platform_pollfd.resize(nfds); async_data->has_libctru_bug.resize(nfds, false); for (u32 i = 0; i < nfds; i++) { + if (!GetSocketHolder(async_data->ctr_fds[i].fd, pid, rp)) { + return; + } async_data->platform_pollfd[i] = CTRPollFD::ToPlatform(*this, async_data->ctr_fds[i], async_data->has_libctru_bug[i]); } @@ -1345,10 +1527,7 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) { async_data->nfds * sizeof(CTRPollFD)); if (async_data->ret == SOCKET_ERROR_VALUE) { - int err = async_data->poll_error; - LOG_DEBUG(Service_SOC, "Socket error: {}", err); - - async_data->ret = TranslateError(GET_ERRNO); + async_data->ret = TranslateError(async_data->poll_error); } IPC::RequestBuilder rb(ctx, static_cast(ctx.CommandHeader().command_id.Value()), 2, @@ -1356,6 +1535,9 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(async_data->ret); rb.PushStaticBuffer(std::move(output_fds), 0); + + LOG_POLL(Service_SOC, "called, fd_count={}, ret={}", async_data->nfds, + static_cast(async_data->ret)); }, timeout != 0); } @@ -1363,19 +1545,18 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) { void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const auto max_addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - const auto max_addr_len = rp.Pop(); - rp.PopPID(); + SocketHolder& holder = socket_holder_optional->get(); sockaddr dest_addr; socklen_t dest_addr_len = sizeof(dest_addr); - s32 ret = ::getsockname(fd_info->second.socket_fd, &dest_addr, &dest_addr_len); + s32 ret = ::getsockname(holder.socket_fd, &dest_addr, &dest_addr_len); CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); std::vector dest_addr_buff(sizeof(ctr_dest_addr)); @@ -1389,6 +1570,9 @@ void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) { dest_addr_buff.resize(max_addr_len); } + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); rb.Push(ret); @@ -1397,25 +1581,86 @@ void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) { void SOC_U::Shutdown(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const u32 socket_handle = rp.Pop(); + s32 how = ShutdownHowToPlatform(rp.Pop()); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - s32 how = ShutdownHowToPlatform(rp.Pop()); - rp.PopPID(); + SocketHolder& holder = socket_holder_optional->get(); - s32 ret = ::shutdown(fd_info->second.socket_fd, how); - if (ret != 0) + s32 ret = ::shutdown(holder.socket_fd, how); + if (ret != 0) { ret = TranslateError(GET_ERRNO); + } else { + if (how == SHUT_RD || how == SHUT_RDWR) { + holder.shutdown_rd = true; + } + } + + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(ret)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(ret); } +static std::vector HostEntToHostByNameData(struct hostent* result) { + std::vector hbn_data_out(sizeof(HostByNameData)); + HostByNameData& hbn_data = *reinterpret_cast(hbn_data_out.data()); + + hbn_data.addr_type = result->h_addrtype; + hbn_data.addr_len = result->h_length; + std::strncpy(hbn_data.h_name.data(), result->h_name, 255); + u16 count; + for (count = 0; count < HostByNameData::max_entries; count++) { + char* curr = result->h_aliases[count]; + if (!curr) { + break; + } + std::strncpy(hbn_data.aliases[count].data(), curr, 255); + } + hbn_data.alias_count = count; + for (count = 0; count < HostByNameData::max_entries; count++) { + char* curr = result->h_addr_list[count]; + if (!curr) { + break; + } + std::memcpy(hbn_data.addresses[count].data(), curr, result->h_length); + } + hbn_data.addr_count = count; + + return hbn_data_out; +} + +void SOC_U::GetHostByAddr(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + [[maybe_unused]] int addr_len = static_cast(rp.Pop()); + int type = static_cast(SocketDomainToPlatform(rp.Pop())); + [[maybe_unused]] u32 out_buf_len = rp.Pop(); + auto addr = rp.PopStaticBuffer(); + + sockaddr platform_addr = CTRSockAddr::ToPlatform(*reinterpret_cast(addr.data())); + + struct hostent* result = + ::gethostbyaddr(reinterpret_cast(&platform_addr), sizeof(platform_addr), type); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(RESULT_SUCCESS); + s32 ret; + if (!result) { + rb.Push(ret = TranslateError(GET_ERRNO)); + } else { + rb.Push(ret = 0); + rb.PushStaticBuffer(HostEntToHostByNameData(result), 0); + } + + LOG_DEBUG(Service_SOC, "called, ret={}", static_cast(ret)); +} + void SOC_U::GetHostByName(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); [[maybe_unused]] u32 name_len = rp.Pop(); @@ -1424,57 +1669,34 @@ void SOC_U::GetHostByName(Kernel::HLERequestContext& ctx) { struct hostent* result = ::gethostbyname(reinterpret_cast(host_name.data())); - std::vector hbn_data_out(sizeof(HostByNameData)); - HostByNameData& hbn_data = *reinterpret_cast(hbn_data_out.data()); - int ret = 0; - - if (result) { - hbn_data.addr_type = result->h_addrtype; - hbn_data.addr_len = result->h_length; - std::strncpy(hbn_data.h_name.data(), result->h_name, 255); - u16 count; - for (count = 0; count < HostByNameData::max_entries; count++) { - char* curr = result->h_aliases[count]; - if (!curr) { - break; - } - std::strncpy(hbn_data.aliases[count].data(), curr, 255); - } - hbn_data.alias_count = count; - for (count = 0; count < HostByNameData::max_entries; count++) { - char* curr = result->h_addr_list[count]; - if (!curr) { - break; - } - std::memcpy(hbn_data.addresses[count].data(), curr, result->h_length); - } - hbn_data.addr_count = count; - } else { - ret = -1; - } - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(ret); - rb.PushStaticBuffer(std::move(hbn_data_out), 0); + s32 ret; + if (!result) { + rb.Push(ret = TranslateError(GET_ERRNO)); + } else { + rb.Push(ret = 0); + rb.PushStaticBuffer(HostEntToHostByNameData(result), 0); + } + + LOG_DEBUG(Service_SOC, "called, ret={}", static_cast(ret)); } void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + const auto max_addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - const auto max_addr_len = rp.Pop(); - rp.PopPID(); + SocketHolder& holder = socket_holder_optional->get(); sockaddr dest_addr; socklen_t dest_addr_len = sizeof(dest_addr); - const int ret = ::getpeername(fd_info->second.socket_fd, &dest_addr, &dest_addr_len); + const int ret = ::getpeername(holder.socket_fd, &dest_addr, &dest_addr_len); CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); std::vector dest_addr_buff(sizeof(ctr_dest_addr)); @@ -1490,6 +1712,9 @@ void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) { dest_addr_buff.resize(max_addr_len); } + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(result)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); rb.Push(result); @@ -1497,56 +1722,98 @@ void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) { } void SOC_U::Connect(Kernel::HLERequestContext& ctx) { - // 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 IPC::RequestParser rp(ctx); const auto socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); + [[maybe_unused]] const auto input_addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + auto input_addr_buf = rp.PopStaticBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { return; } - [[maybe_unused]] const auto input_addr_len = rp.Pop(); - rp.PopPID(); - auto input_addr_buf = rp.PopStaticBuffer(); + SocketHolder& holder = socket_holder_optional->get(); + + struct AsyncData { + // Input + SocketHolder* fd_info; + sockaddr input_addr; + u32 socket_handle; + u32 pid; + + // Output + s32 ret{}; + int connect_error; + }; + + auto async_data = std::make_shared(); + async_data->fd_info = &holder; + async_data->pid = pid; CTRSockAddr ctr_input_addr; std::memcpy(&ctr_input_addr, input_addr_buf.data(), sizeof(ctr_input_addr)); - sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr); - if (GetSocketBlocking(fd_info->second)) { - PreTimerAdjust(); - } - s32 ret = ::connect(fd_info->second.socket_fd, &input_addr, sizeof(input_addr)); - if (GetSocketBlocking(fd_info->second)) { - PostTimerAdjust(ctx, "Connect"); - } - if (ret != 0) - ret = TranslateError(GET_ERRNO); + async_data->input_addr = CTRSockAddr::ToPlatform(ctr_input_addr); + async_data->socket_handle = socket_handle; - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(RESULT_SUCCESS); - rb.Push(ret); + ctx.RunAsync( + [async_data](Kernel::HLERequestContext& ctx) { + async_data->ret = ::connect(async_data->fd_info->socket_fd, &async_data->input_addr, + sizeof(async_data->input_addr)); + async_data->connect_error = (async_data->ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0; + return 0; + }, + [async_data](Kernel::HLERequestContext& ctx) { + if (async_data->ret != 0) { + async_data->ret = TranslateError(async_data->connect_error); + } + + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", async_data->pid, + async_data->socket_handle, static_cast(async_data->ret)); + + IPC::RequestBuilder rb(ctx, 0x06, 2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(async_data->ret); + }); } void SOC_U::InitializeSockets(Kernel::HLERequestContext& ctx) { - // TODO(Subv): Implement IPC::RequestParser rp(ctx); [[maybe_unused]] const auto memory_block_size = rp.Pop(); - rp.PopPID(); - rp.PopObject(); + const u32 pid = rp.PopPID(); + [[maybe_unused]] auto shared_memory = rp.PopObject(); + + ResultCode res = RESULT_SUCCESS; + if (initialized_processes.find(pid) == initialized_processes.end()) { + initialized_processes.insert(pid); + } else { + res = ERR_ALREADY_INITIALIZED; + } + + LOG_DEBUG(Service_SOC, "called, pid={}, res={:#08X}", pid, res.raw); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); + rb.Push(res); } void SOC_U::ShutdownSockets(Kernel::HLERequestContext& ctx) { - // TODO(Subv): Implement IPC::RequestParser rp(ctx); - CleanupSockets(); + + // This service call does not provide a PID like the others, + // instead SOC seems to fetch the PID from the current session. + const u32 pid = ctx.ClientThread()->owner_process.lock()->process_id; + + if (initialized_processes.find(pid) == initialized_processes.end()) { + LOG_DEBUG(Service_SOC, "Process not initialized: pid={}", pid); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERR_NOT_INITIALIZED); + return; + } + + CloseAndDeleteAllSockets(pid); + initialized_processes.erase(pid); + + LOG_DEBUG(Service_SOC, "called, pid={}", pid); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -1554,18 +1821,17 @@ void SOC_U::ShutdownSockets(Kernel::HLERequestContext& ctx) { void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); - return; - } + const u32 socket_handle = rp.Pop(); const u32 level = rp.Pop(); const s32 optname = rp.Pop(); u32 optlen = rp.Pop(); - rp.PopPID(); + const u32 pid = rp.PopPID(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); s32 err = 0; @@ -1582,7 +1848,7 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) { std::vector platform_data( TranslateSockOptSizeToPlatform(level_opt.first, level_opt.second)); socklen_t platform_data_size = static_cast(platform_data.size()); - err = ::getsockopt(fd_info->second.socket_fd, level_opt.first, level_opt.second, + err = ::getsockopt(holder.socket_fd, level_opt.first, level_opt.second, reinterpret_cast(platform_data.data()), &platform_data_size); if (err == SOCKET_ERROR_VALUE) { err = TranslateError(GET_ERRNO); @@ -1593,6 +1859,9 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) { } } + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(err)); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); rb.Push(RESULT_SUCCESS); rb.Push(err); @@ -1603,18 +1872,18 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) { void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto socket_handle = rp.Pop(); - auto fd_info = open_sockets.find(socket_handle); - if (fd_info == open_sockets.end()) { - LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_HANDLE); - return; - } const auto level = rp.Pop(); const auto optname = rp.Pop(); const auto optlen = rp.Pop(); - rp.PopPID(); + const u32 pid = rp.PopPID(); auto optval = rp.PopStaticBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + optval.resize(optlen); s32 err = 0; @@ -1629,8 +1898,7 @@ void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) { std::vector platform_data; const auto levelopt = TranslateSockOpt(level, optname); TranslateSockOptDataToPlatform(platform_data, optval, levelopt.first, levelopt.second); - err = static_cast(::setsockopt(fd_info->second.socket_fd, levelopt.first, - levelopt.second, + err = static_cast(::setsockopt(holder.socket_fd, levelopt.first, levelopt.second, reinterpret_cast(platform_data.data()), static_cast(platform_data.size()))); if (err == SOCKET_ERROR_VALUE) { @@ -1638,6 +1906,9 @@ void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) { } } + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}, ret={}", pid, socket_handle, + static_cast(err)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(err); @@ -1790,6 +2061,81 @@ void SOC_U::GetNameInfoImpl(Kernel::HLERequestContext& ctx) { rb.PushStaticBuffer(std::move(serv), 1); } +void SOC_U::SendToMultiple(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const u32 socket_handle = rp.Pop(); + u32 len = rp.Pop(); + u32 flags = SendRecvFlagsToPlatform(rp.Pop()); + u32 addr_len = rp.Pop(); + u32 total_addr_len = rp.Pop(); + const u32 pid = rp.PopPID(); + auto input_buff = rp.PopStaticBuffer(); + auto dest_addr_buff = rp.PopStaticBuffer(); + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + + u32 count = total_addr_len / addr_len; + s32 ret = 0; + u32 i = 0; + do { + ret = SendToImpl(holder, len, flags, addr_len, input_buff, + dest_addr_buff.data() + (i * addr_len)); + i++; + } while (i < count && ret >= 0); + + LOG_DEBUG(Service_SOC, "called, pid={}, fd_count={}, ret={}", pid, count, + static_cast(ret)); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(ret); +} + +void SOC_U::CloseSockets(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const u32 pid = rp.PopPID(); + + if (initialized_processes.find(pid) == initialized_processes.end()) { + LOG_DEBUG(Service_SOC, "Process not initialized: pid={}", pid); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERR_NOT_INITIALIZED); + return; + } + + CloseAndDeleteAllSockets(pid); + + LOG_DEBUG(Service_SOC, "called, pid={}", pid); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void SOC_U::AddGlobalSocket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto socket_handle = rp.Pop(); + + // This service call does not provide a PID like the others, + // instead SOC seems to fetch the PID from the current session. + const u32 pid = ctx.ClientThread()->owner_process.lock()->process_id; + + auto socket_holder_optional = GetSocketHolder(socket_handle, pid, rp); + if (!socket_holder_optional) { + return; + } + SocketHolder& holder = socket_holder_optional->get(); + + holder.isGlobal = true; + + LOG_DEBUG(Service_SOC, "called, pid={}, fd={}", pid, socket_handle); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + SOC_U::SOC_U() : ServiceFramework("soc:U", 18) { static const FunctionInfo functions[] = { // clang-format off @@ -1802,18 +2148,18 @@ SOC_U::SOC_U() : ServiceFramework("soc:U", 18) { {0x0007, &SOC_U::RecvFromOther, "recvfrom_other"}, {0x0008, &SOC_U::RecvFrom, "RecvFrom"}, {0x0009, &SOC_U::SendToOther, "SendToOther"}, - {0x000A, &SOC_U::SendTo, "SendTo"}, + {0x000A, &SOC_U::SendToSingle, "SendToSingle"}, {0x000B, &SOC_U::Close, "Close"}, {0x000C, &SOC_U::Shutdown, "Shutdown"}, {0x000D, &SOC_U::GetHostByName, "GetHostByName"}, - {0x000E, nullptr, "GetHostByAddr"}, + {0x000E, &SOC_U::GetHostByAddr, "GetHostByAddr"}, {0x000F, &SOC_U::GetAddrInfoImpl, "GetAddrInfo"}, {0x0010, &SOC_U::GetNameInfoImpl, "GetNameInfo"}, {0x0011, &SOC_U::GetSockOpt, "GetSockOpt"}, {0x0012, &SOC_U::SetSockOpt, "SetSockOpt"}, {0x0013, &SOC_U::Fcntl, "Fcntl"}, {0x0014, &SOC_U::Poll, "Poll"}, - {0x0015, nullptr, "SockAtMark"}, + {0x0015, &SOC_U::SockAtMark, "SockAtMark"}, {0x0016, &SOC_U::GetHostId, "GetHostId"}, {0x0017, &SOC_U::GetSockName, "GetSockName"}, {0x0018, &SOC_U::GetPeerName, "GetPeerName"}, @@ -1824,8 +2170,9 @@ SOC_U::SOC_U() : ServiceFramework("soc:U", 18) { {0x001D, nullptr, "ICMPCancel"}, {0x001E, nullptr, "ICMPClose"}, {0x001F, nullptr, "GetResolverInfo"}, - {0x0021, nullptr, "CloseSockets"}, - {0x0023, nullptr, "AddGlobalSocket"}, + {0x0020, &SOC_U::SendToMultiple, "SendToMultiple"}, + {0x0021, &SOC_U::CloseSockets, "CloseSockets"}, + {0x0023, &SOC_U::AddGlobalSocket, "AddGlobalSocket"}, // clang-format on }; @@ -1838,7 +2185,7 @@ SOC_U::SOC_U() : ServiceFramework("soc:U", 18) { } SOC_U::~SOC_U() { - CleanupSockets(); + CloseAndDeleteAllSockets(); #ifdef _WIN32 WSACleanup(); #endif diff --git a/src/core/hle/service/soc/soc_u.h b/src/core/hle/service/soc/soc_u.h index 56329e1ed..0aebd723c 100644 --- a/src/core/hle/service/soc/soc_u.h +++ b/src/core/hle/service/soc/soc_u.h @@ -14,6 +14,10 @@ namespace Core { class System; } +namespace IPC { +class RequestParser; +} + namespace Service::SOC { /// Holds information about a particular socket @@ -26,12 +30,18 @@ struct SocketHolder { #endif // _WIN32 bool blocking = true; ///< Whether the socket is blocking or not. + bool isGlobal = false; + bool shutdown_rd = false; + + u32 ownerProcess = 0; private: template void serialize(Archive& ar, const unsigned int) { ar& socket_fd; ar& blocking; + ar& isGlobal; + ar& ownerProcess; } friend class boost::serialization::access; }; @@ -51,9 +61,15 @@ public: std::optional GetDefaultInterfaceInfo(); private: - static constexpr ResultCode ERR_INVALID_HANDLE = - ResultCode(ErrorDescription::InvalidHandle, ErrorModule::SOC, ErrorSummary::InvalidArgument, - ErrorLevel::Permanent); + static constexpr ResultCode ERR_WRONG_PROCESS = + ResultCode(4, ErrorModule::SOC, ErrorSummary::InvalidState, ErrorLevel::Status); + static constexpr ResultCode ERR_NOT_INITIALIZED = + ResultCode(6, ErrorModule::SOC, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + static constexpr ResultCode ERR_INVALID_SOCKET_DESCRIPTOR = + ResultCode(7, ErrorModule::SOC, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + static constexpr ResultCode ERR_ALREADY_INITIALIZED = + ResultCode(11, ErrorModule::SOC, ErrorSummary::InvalidState, ErrorLevel::Status); + static constexpr u32 SOC_ERR_INAVLID_ENUM_VALUE = 0xFFFF8025; static constexpr u32 SOC_SOL_IP = 0x0000; @@ -70,6 +86,19 @@ private: bool GetSocketBlocking(const SocketHolder& socket_holder); u32 SetSocketBlocking(SocketHolder& socket_holder, bool blocking); + std::optional> GetSocketHolder(u32 ctr_socket_fd, + u32 process_id, + IPC::RequestParser& rp); + + // Close and delete all sockets, optinally only the ones owned by the + // specified process ID. + void CloseAndDeleteAllSockets(s32 process_id = -1); + + s32 SendToImpl(SocketHolder& holder, u32 len, u32 flags, u32 addr_len, + const std::vector& input_buff, const u8* dest_addr_buff); + + static void RecvBusyWaitForEvent(SocketHolder& holder); + // From // https://github.com/devkitPro/libctru/blob/1de86ea38aec419744149daf692556e187d4678a/libctru/include/3ds/services/soc.h#L15 enum class NetworkOpt { @@ -86,33 +115,22 @@ private: NETOPT_DHCP_LEASE_TIME = 0xC001, ///< The DHCP lease time remaining, in seconds }; - struct HostByNameData { - static const u32 max_entries = 24; - - u16_le addr_type; - u16_le addr_len; - u16_le addr_count; - u16_le alias_count; - std::array h_name; - std::array, max_entries> aliases; - std::array, max_entries> addresses; - }; - static_assert(sizeof(HostByNameData) == 0x1A88, "Invalid HostByNameData size"); - void Socket(Kernel::HLERequestContext& ctx); void Bind(Kernel::HLERequestContext& ctx); void Fcntl(Kernel::HLERequestContext& ctx); void Listen(Kernel::HLERequestContext& ctx); void Accept(Kernel::HLERequestContext& ctx); + void SockAtMark(Kernel::HLERequestContext& ctx); void GetHostId(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); void SendToOther(Kernel::HLERequestContext& ctx); - void SendTo(Kernel::HLERequestContext& ctx); + void SendToSingle(Kernel::HLERequestContext& ctx); void RecvFromOther(Kernel::HLERequestContext& ctx); void RecvFrom(Kernel::HLERequestContext& ctx); void Poll(Kernel::HLERequestContext& ctx); void GetSockName(Kernel::HLERequestContext& ctx); void Shutdown(Kernel::HLERequestContext& ctx); + void GetHostByAddr(Kernel::HLERequestContext& ctx); void GetHostByName(Kernel::HLERequestContext& ctx); void GetPeerName(Kernel::HLERequestContext& ctx); void Connect(Kernel::HLERequestContext& ctx); @@ -121,6 +139,9 @@ private: void GetSockOpt(Kernel::HLERequestContext& ctx); void SetSockOpt(Kernel::HLERequestContext& ctx); void GetNetworkOpt(Kernel::HLERequestContext& ctx); + void SendToMultiple(Kernel::HLERequestContext& ctx); + void CloseSockets(Kernel::HLERequestContext& ctx); + void AddGlobalSocket(Kernel::HLERequestContext& ctx); // Some platforms seem to have GetAddrInfo and GetNameInfo defined as macros, // so we have to use a different name here. @@ -133,17 +154,10 @@ private: return next_socket_id++; } - // System timer adjust - std::chrono::time_point adjust_value_last; - void PreTimerAdjust(); - void PostTimerAdjust(Kernel::HLERequestContext& ctx, const std::string& caller_method); - - /// Close all open sockets - void CleanupSockets(); - /// Holds info about the currently open sockets friend struct CTRPollFD; - std::unordered_map open_sockets; + std::unordered_map created_sockets; + std::set initialized_processes; /// Cache interface info for the current session /// These two fields are not saved to savestates on purpose @@ -155,7 +169,8 @@ private: template void serialize(Archive& ar, const unsigned int) { ar& boost::serialization::base_object(*this); - ar& open_sockets; + ar& created_sockets; + ar& initialized_processes; } friend class boost::serialization::access; };