From 8e703e08dfcf735a08df2ceff6a05221b7cc981f Mon Sep 17 00:00:00 2001 From: comex Date: Mon, 19 Jun 2023 18:17:43 -0700 Subject: Implement SSL service This implements some missing network APIs including a large chunk of the SSL service, enough for Mario Maker (with an appropriate mod applied) to connect to the fan server [Open Course World](https://opencourse.world/). Connecting to first-party servers is out of scope of this PR and is a minefield I'd rather not step into. ## TLS TLS is implemented with multiple backends depending on the system's 'native' TLS library. Currently there are two backends: Schannel for Windows, and OpenSSL for Linux. (In reality Linux is a bit of a free-for-all where there's no one 'native' library, but OpenSSL is the closest it gets.) On macOS the 'native' library is SecureTransport but that isn't implemented in this PR. (Instead, all non-Windows OSes will use OpenSSL unless disabled with `-DENABLE_OPENSSL=OFF`.) Why have multiple backends instead of just using a single library, especially given that Yuzu already embeds mbedtls for cryptographic algorithms? Well, I tried implementing this on mbedtls first, but the problem is TLS policies - mainly trusted certificate policies, and to a lesser extent trusted algorithms, SSL versions, etc. ...In practice, the chance that someone is going to conduct a man-in-the-middle attack on a third-party game server is pretty low, but I'm a security nerd so I like to do the right security things. My base assumption is that we want to use the host system's TLS policies. An alternative would be to more closely emulate the Switch's TLS implementation (which is based on NSS). But for one thing, I don't feel like reverse engineering it. And I'd argue that for third-party servers such as Open Course World, it's theoretically preferable to use the system's policies rather than the Switch's, for two reasons 1. Someday the Switch will stop being updated, and the trusted cert list, algorithms, etc. will start to go stale, but users will still want to connect to third-party servers, and there's no reason they shouldn't have up-to-date security when doing so. At that point, homebrew users on actual hardware may patch the TLS implementation, but for emulators it's simpler to just use the host's stack. 2. Also, it's good to respect any custom certificate policies the user may have added systemwide. For example, they may have added custom trusted CAs in order to use TLS debugging tools or pass through corporate MitM middleboxes. Or they may have removed some CAs that are normally trusted out of paranoia. Note that this policy wouldn't work as-is for connecting to first-party servers, because some of them serve certificates based on Nintendo's own CA rather than a publicly trusted one. However, this could probably be solved easily by using appropriate APIs to adding Nintendo's CA as an alternate trusted cert for Yuzu's connections. That is not implemented in this PR because, again, first-party servers are out of scope. (If anything I'd rather have an option to _block_ connections to Nintendo servers, but that's not implemented here.) To use the host's TLS policies, there are three theoretical options: a) Import the host's trusted certificate list into a cross-platform TLS library (presumably mbedtls). b) Use the native TLS library to verify certificates but use a cross-platform TLS library for everything else. c) Use the native TLS library for everything. Two problems with option a). First, importing the trusted certificate list at minimum requires a bunch of platform-specific code, which mbedtls does not have built in. Interestingly, OpenSSL recently gained the ability to import the Windows certificate trust store... but that leads to the second problem, which is that a list of trusted certificates is [not expressive enough](https://bugs.archlinux.org/task/41909) to express a modern certificate trust policy. For example, Windows has the concept of [explicitly distrusted certificates](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn265983(v=ws.11)), and macOS requires Certificate Transparency validation for some certificates with complex rules for when it's required. Option b) (using native library just to verify certs) is probably feasible, but it would miss aspects of TLS policy other than trusted certs (like allowed algorithms), and in any case it might well require writing more code, not less, compared to using the native library for everything. So I ended up at option c), using the native library for everything. What I'd *really* prefer would be to use a third-party library that does option c) for me. Rust has a good library for this, [native-tls](https://docs.rs/native-tls/latest/native_tls/). I did search, but I couldn't find a good option in the C or C++ ecosystem, at least not any that wasn't part of some much larger framework. I was surprised - isn't this a pretty common use case? Well, many applications only need TLS for HTTPS, and they can use libcurl, which has a TLS abstraction layer internally but doesn't expose it. Other applications only support a single TLS library, or use one of the aforementioned larger frameworks, or are platform-specific to begin with, or of course are written in a non-C/C++ language, most of which have some canonical choice for TLS. But there are also many applications that have a set of TLS backends just like this; it's just that nobody has gone ahead and abstracted the pattern into a library, at least not a widespread one. Amusingly, there is one TLS abstraction layer that Yuzu already bundles: the one in ffmpeg. But it is missing some features that would be needed to use it here (like reusing an existing socket rather than managing the socket itself). Though, that does mean that the wiki's build instructions for Linux (and macOS for some reason?) already recommend installing OpenSSL, so no need to update those. ## Other APIs implemented - Sockets: - GetSockOpt(`SO_ERROR`) - SetSockOpt(`SO_NOSIGPIPE`) (stub, I have no idea what this does on Switch) - `DuplicateSocket` (because the SSL sysmodule calls it internally) - More `PollEvents` values - NSD: - `Resolve` and `ResolveEx` (stub, good enough for Open Course World and probably most third-party servers, but not first-party) - SFDNSRES: - `GetHostByNameRequest` and `GetHostByNameRequestWithOptions` - `ResolverSetOptionRequest` (stub) ## Fixes - Parts of the socket code were previously allocating a `sockaddr` object on the stack when calling functions that take a `sockaddr*` (e.g. `accept`). This might seem like the right thing to do to avoid illegal aliasing, but in fact `sockaddr` is not guaranteed to be large enough to hold any particular type of address, only the header. This worked in practice because in practice `sockaddr` is the same size as `sockaddr_in`, but it's not how the API is meant to be used. I changed this to allocate an `sockaddr_in` on the stack and `reinterpret_cast` it. I could try to do something cleverer with `aligned_storage`, but casting is the idiomatic way to use these particular APIs, so it's really the system's responsibility to avoid any aliasing issues. - I rewrote most of the `GetAddrInfoRequest[WithOptions]` implementation. The old implementation invoked the host's getaddrinfo directly from sfdnsres.cpp, and directly passed through the host's socket type, protocol, etc. values rather than looking up the corresponding constants on the Switch. To be fair, these constants don't tend to actually vary across systems, but still... I added a wrapper for `getaddrinfo` in `internal_network/network.cpp` similar to the ones for other socket APIs, and changed the `GetAddrInfoRequest` implementation to use it. While I was at it, I rewrote the serialization to use the same approach I used to implement `GetHostByNameRequest`, because it reduces the number of size calculations. While doing so I removed `AF_INET6` support because the Switch doesn't support IPv6; it might be nice to support IPv6 anyway, but that would have to apply to all of the socket APIs. I also corrected the IPC wrappers for `GetAddrInfoRequest` and `GetAddrInfoRequestWithOptions` based on reverse engineering and hardware testing. Every call to `GetAddrInfoRequestWithOptions` returns *four* different error codes (IPC status, getaddrinfo error code, netdb error code, and errno), and `GetAddrInfoRequest` returns three of those but in a different order, and it doesn't really matter but the existing implementation was a bit off, as I discovered while testing `GetHostByNameRequest`. - The new serialization code is based on two simple helper functions: ```cpp template static void Append(std::vector& vec, T t); void AppendNulTerminated(std::vector& vec, std::string_view str); ``` I was thinking there must be existing functions somewhere that assist with serialization/deserialization of binary data, but all I could find was the helper methods in `IOFile` and `HLERequestContext`, not anything that could be used with a generic byte buffer. If I'm not missing something, then maybe I should move the above functions to a new header in `common`... right now they're just sitting in `sfdnsres.cpp` where they're used. - Not a fix, but `SocketBase::Recv`/`Send` is changed to use `std::span` rather than `std::vector&` to avoid needing to copy the data to/from a vector when those methods are called from the TLS implementation. --- src/core/hle/service/sockets/bsd.cpp | 107 ++++++- src/core/hle/service/sockets/bsd.h | 13 +- src/core/hle/service/sockets/nsd.cpp | 58 +++- src/core/hle/service/sockets/nsd.h | 4 + src/core/hle/service/sockets/sfdnsres.cpp | 345 ++++++++++++--------- src/core/hle/service/sockets/sfdnsres.h | 3 + src/core/hle/service/sockets/sockets.h | 33 +- src/core/hle/service/sockets/sockets_translate.cpp | 114 ++++++- src/core/hle/service/sockets/sockets_translate.h | 17 +- 9 files changed, 508 insertions(+), 186 deletions(-) (limited to 'src/core/hle/service/sockets') diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index bce45d321..6677689dc 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -20,6 +20,9 @@ #include "core/internal_network/sockets.h" #include "network/network.h" +using Common::Expected; +using Common::Unexpected; + namespace Service::Sockets { namespace { @@ -265,16 +268,19 @@ void BSD::GetSockOpt(HLERequestContext& ctx) { const u32 level = rp.Pop(); const auto optname = static_cast(rp.Pop()); - LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname); - std::vector optval(ctx.GetWriteBufferSize()); + LOG_WARNING(Service, "called. fd={} level={} optname=0x{:x} len=0x{:x}", fd, level, optname, + optval.size()); + + Errno err = GetSockOptImpl(fd, level, optname, optval); + ctx.WriteBuffer(optval); IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.Push(-1); - rb.PushEnum(Errno::NOTCONN); + rb.Push(err == Errno::SUCCESS ? 0 : -1); + rb.PushEnum(err); rb.Push(static_cast(optval.size())); } @@ -436,6 +442,18 @@ void BSD::Close(HLERequestContext& ctx) { BuildErrnoResponse(ctx, CloseImpl(fd)); } +void BSD::DuplicateSocket(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop(); + [[maybe_unused]] const u64 unused = rp.Pop(); + + Common::Expected res = DuplicateSocketImpl(fd); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(res.value_or(0)); // ret + rb.Push(res ? 0 : static_cast(res.error())); // bsd errno +} + void BSD::EventFd(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 initval = rp.Pop(); @@ -477,12 +495,12 @@ std::pair BSD::SocketImpl(Domain domain, Type type, Protocol protoco auto room_member = room_network.GetRoomMember().lock(); if (room_member && room_member->IsConnected()) { - descriptor.socket = std::make_unique(room_network); + descriptor.socket = std::make_shared(room_network); } else { - descriptor.socket = std::make_unique(); + descriptor.socket = std::make_shared(); } - descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); + descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol)); descriptor.is_connection_based = IsConnectionBased(type); return {fd, Errno::SUCCESS}; @@ -538,7 +556,7 @@ std::pair BSD::PollImpl(std::vector& write_buffer, std::spansocket.get(); - result.events = TranslatePollEventsToHost(pollfd.events); + result.events = Translate(pollfd.events); result.revents = Network::PollEvents{}; return result; }); @@ -547,7 +565,7 @@ std::pair BSD::PollImpl(std::vector& write_buffer, std::span& write_buffer) { } const SockAddrIn guest_addrin = Translate(addr_in); - ASSERT(write_buffer.size() == sizeof(guest_addrin)); + ASSERT(write_buffer.size() >= sizeof(guest_addrin)); + write_buffer.resize(sizeof(guest_addrin)); std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); return Translate(bsd_errno); } @@ -633,7 +652,8 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector& write_buffer) { } const SockAddrIn guest_addrin = Translate(addr_in); - ASSERT(write_buffer.size() == sizeof(guest_addrin)); + ASSERT(write_buffer.size() >= sizeof(guest_addrin)); + write_buffer.resize(sizeof(guest_addrin)); std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); return Translate(bsd_errno); } @@ -671,13 +691,47 @@ std::pair BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { } } -Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { - UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET +Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector& optval) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + if (level != static_cast(SocketLevel::SOCKET)) { + UNIMPLEMENTED_MSG("Unknown getsockopt level"); + return Errno::SUCCESS; + } + + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); + + switch (optname) { + case OptName::ERROR_: { + auto [pending_err, getsockopt_err] = socket->GetPendingError(); + if (getsockopt_err == Network::Errno::SUCCESS) { + Errno translated_pending_err = Translate(pending_err); + ASSERT_OR_EXECUTE_MSG( + optval.size() == sizeof(Errno), { return Errno::INVAL; }, + "Incorrect getsockopt option size"); + optval.resize(sizeof(Errno)); + memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); + } + return Translate(getsockopt_err); + } + default: + UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); + return Errno::SUCCESS; + } +} +Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } + if (level != static_cast(SocketLevel::SOCKET)) { + UNIMPLEMENTED_MSG("Unknown setsockopt level"); + return Errno::SUCCESS; + } + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { @@ -711,6 +765,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con return Translate(socket->SetSndTimeo(value)); case OptName::RCVTIMEO: return Translate(socket->SetRcvTimeo(value)); + case OptName::NOSIGPIPE: + LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value); + return Errno::SUCCESS; default: UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); return Errno::SUCCESS; @@ -841,6 +898,28 @@ Errno BSD::CloseImpl(s32 fd) { return bsd_errno; } +Expected BSD::DuplicateSocketImpl(s32 fd) { + if (!IsFileDescriptorValid(fd)) { + return Unexpected(Errno::BADF); + } + + const s32 new_fd = FindFreeFileDescriptorHandle(); + if (new_fd < 0) { + LOG_ERROR(Service, "No more file descriptors available"); + return Unexpected(Errno::MFILE); + } + + file_descriptors[new_fd] = file_descriptors[fd]; + return new_fd; +} + +std::optional> BSD::GetSocket(s32 fd) { + if (!IsFileDescriptorValid(fd)) { + return std::nullopt; + } + return file_descriptors[fd]->socket; +} + s32 BSD::FindFreeFileDescriptorHandle() noexcept { for (s32 fd = 0; fd < static_cast(file_descriptors.size()); ++fd) { if (!file_descriptors[fd]) { @@ -911,7 +990,7 @@ BSD::BSD(Core::System& system_, const char* name) {24, &BSD::Write, "Write"}, {25, &BSD::Read, "Read"}, {26, &BSD::Close, "Close"}, - {27, nullptr, "DuplicateSocket"}, + {27, &BSD::DuplicateSocket, "DuplicateSocket"}, {28, nullptr, "GetResourceStatistics"}, {29, nullptr, "RecvMMsg"}, {30, nullptr, "SendMMsg"}, diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 30ae9c140..430edb97c 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -8,6 +8,7 @@ #include #include "common/common_types.h" +#include "common/expected.h" #include "common/socket_types.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" @@ -29,12 +30,19 @@ public: explicit BSD(Core::System& system_, const char* name); ~BSD() override; + // These methods are called from SSL; the first two are also called from + // this class for the corresponding IPC methods. + // On the real device, the SSL service makes IPC calls to this service. + Common::Expected DuplicateSocketImpl(s32 fd); + Errno CloseImpl(s32 fd); + std::optional> GetSocket(s32 fd); + private: /// Maximum number of file descriptors static constexpr size_t MAX_FD = 128; struct FileDescriptor { - std::unique_ptr socket; + std::shared_ptr socket; s32 flags = 0; bool is_connection_based = false; }; @@ -138,6 +146,7 @@ private: void Write(HLERequestContext& ctx); void Read(HLERequestContext& ctx); void Close(HLERequestContext& ctx); + void DuplicateSocket(HLERequestContext& ctx); void EventFd(HLERequestContext& ctx); template @@ -153,6 +162,7 @@ private: Errno GetSockNameImpl(s32 fd, std::vector& write_buffer); Errno ListenImpl(s32 fd, s32 backlog); std::pair FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); + Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector& optval); Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); Errno ShutdownImpl(s32 fd, s32 how); std::pair RecvImpl(s32 fd, u32 flags, std::vector& message); @@ -161,7 +171,6 @@ private: std::pair SendImpl(s32 fd, u32 flags, std::span message); std::pair SendToImpl(s32 fd, u32 flags, std::span message, std::span addr); - Errno CloseImpl(s32 fd); s32 FindFreeFileDescriptorHandle() noexcept; bool IsFileDescriptorValid(s32 fd) const noexcept; diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index 6491a73be..22c3a31a0 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp @@ -1,10 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/sockets/nsd.h" +#include "common/string_util.h" + namespace Service::Sockets { +constexpr Result ResultOverflow{ErrorModule::NSD, 6}; + NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { // clang-format off static const FunctionInfo functions[] = { @@ -15,8 +20,8 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na {13, nullptr, "DeleteSettings"}, {14, nullptr, "ImportSettings"}, {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"}, - {20, nullptr, "Resolve"}, - {21, nullptr, "ResolveEx"}, + {20, &NSD::Resolve, "Resolve"}, + {21, &NSD::ResolveEx, "ResolveEx"}, {30, nullptr, "GetNasServiceSetting"}, {31, nullptr, "GetNasServiceSettingEx"}, {40, nullptr, "GetNasRequestFqdn"}, @@ -40,6 +45,55 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na RegisterHandlers(functions); } +static ResultVal ResolveImpl(const std::string& fqdn_in) { + // The real implementation makes various substitutions. + // For now we just return the string as-is, which is good enough when not + // connecting to real Nintendo servers. + LOG_WARNING(Service, "(STUBBED) called({})", fqdn_in); + return fqdn_in; +} + +static Result ResolveCommon(const std::string& fqdn_in, std::array& fqdn_out) { + const auto res = ResolveImpl(fqdn_in); + if (res.Failed()) { + return res.Code(); + } + if (res->size() >= fqdn_out.size()) { + return ResultOverflow; + } + std::memcpy(fqdn_out.data(), res->c_str(), res->size() + 1); + return ResultSuccess; +} + +void NSD::Resolve(HLERequestContext& ctx) { + const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0)); + + std::array fqdn_out{}; + Result res = ResolveCommon(fqdn_in, fqdn_out); + + ctx.WriteBuffer(fqdn_out); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void NSD::ResolveEx(HLERequestContext& ctx) { + const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0)); + + std::array fqdn_out; + Result res = ResolveCommon(fqdn_in, fqdn_out); + + if (res.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + return; + } + + ctx.WriteBuffer(fqdn_out); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(ResultSuccess); +} + NSD::~NSD() = default; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h index 5cc12b855..a7379a8a9 100644 --- a/src/core/hle/service/sockets/nsd.h +++ b/src/core/hle/service/sockets/nsd.h @@ -15,6 +15,10 @@ class NSD final : public ServiceFramework { public: explicit NSD(Core::System& system_, const char* name); ~NSD() override; + +private: + void Resolve(HLERequestContext& ctx); + void ResolveEx(HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index 132dd5797..1196fb86c 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -10,27 +10,18 @@ #include "core/core.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/sockets/sfdnsres.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/hle/service/sockets/sockets_translate.h" +#include "core/internal_network/network.h" #include "core/memory.h" -#ifdef _WIN32 -#include -#elif YUZU_UNIX -#include -#include -#include -#include -#ifndef EAI_NODATA -#define EAI_NODATA EAI_NONAME -#endif -#endif - namespace Service::Sockets { SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} { static const FunctionInfo functions[] = { {0, nullptr, "SetDnsAddressesPrivateRequest"}, {1, nullptr, "GetDnsAddressPrivateRequest"}, - {2, nullptr, "GetHostByNameRequest"}, + {2, &SFDNSRES::GetHostByNameRequest, "GetHostByNameRequest"}, {3, nullptr, "GetHostByAddrRequest"}, {4, nullptr, "GetHostStringErrorRequest"}, {5, nullptr, "GetGaiStringErrorRequest"}, @@ -38,11 +29,11 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" {7, nullptr, "GetNameInfoRequest"}, {8, nullptr, "RequestCancelHandleRequest"}, {9, nullptr, "CancelRequest"}, - {10, nullptr, "GetHostByNameRequestWithOptions"}, + {10, &SFDNSRES::GetHostByNameRequestWithOptions, "GetHostByNameRequestWithOptions"}, {11, nullptr, "GetHostByAddrRequestWithOptions"}, {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, {13, nullptr, "GetNameInfoRequestWithOptions"}, - {14, nullptr, "ResolverSetOptionRequest"}, + {14, &SFDNSRES::ResolverSetOptionRequest, "ResolverSetOptionRequest"}, {15, nullptr, "ResolverGetOptionRequest"}, }; RegisterHandlers(functions); @@ -59,188 +50,246 @@ enum class NetDbError : s32 { NoData = 4, }; -static NetDbError AddrInfoErrorToNetDbError(s32 result) { - // Best effort guess to map errors +static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) { + // These combinations have been verified on console (but are not + // exhaustive). switch (result) { - case 0: + case GetAddrInfoError::SUCCESS: return NetDbError::Success; - case EAI_AGAIN: + case GetAddrInfoError::AGAIN: return NetDbError::TryAgain; - case EAI_NODATA: - return NetDbError::NoData; + case GetAddrInfoError::NODATA: + return NetDbError::HostNotFound; + case GetAddrInfoError::SERVICE: + return NetDbError::Success; default: return NetDbError::HostNotFound; } } -static std::vector SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, +static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) { + // These combinations have been verified on console (but are not + // exhaustive). + switch (result) { + case GetAddrInfoError::SUCCESS: + // Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for + // some reason, but that doesn't seem useful to implement. + return Errno::SUCCESS; + case GetAddrInfoError::AGAIN: + return Errno::SUCCESS; + case GetAddrInfoError::NODATA: + return Errno::SUCCESS; + case GetAddrInfoError::SERVICE: + return Errno::INVAL; + default: + return Errno::SUCCESS; + } +} + +template +static void Append(std::vector& vec, T t) { + size_t off = vec.size(); + vec.resize(off + sizeof(T)); + std::memcpy(vec.data() + off, &t, sizeof(T)); +} + +static void AppendNulTerminated(std::vector& vec, std::string_view str) { + size_t off = vec.size(); + vec.resize(off + str.size() + 1); + std::memcpy(vec.data() + off, str.data(), str.size()); +} + +// We implement gethostbyname using the host's getaddrinfo rather than the +// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo +// behaves the same on Unix and Windows, unlike gethostbyname where Windows +// doesn't implement h_errno. +static std::vector SerializeAddrInfoAsHostEnt(const std::vector& vec, + std::string_view host) { + + std::vector data; + // h_name: use the input hostname (append nul-terminated) + AppendNulTerminated(data, host); + // h_aliases: leave empty + + Append(data, 0); // count of h_aliases + // (If the count were nonzero, the aliases would be appended as nul-terminated here.) + Append(data, static_cast(Domain::INET)); // h_addrtype + Append(data, sizeof(Network::IPv4Address)); // h_length + // h_addr_list: + size_t count = vec.size(); + ASSERT(count <= UINT32_MAX); + Append(data, static_cast(count)); + for (const Network::AddrInfo& addrinfo : vec) { + // On the Switch, this is passed through htonl despite already being + // big-endian, so it ends up as little-endian. + Append(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); + + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, + Network::IPv4AddressToString(addrinfo.addr.ip)); + } + return data; +} + +static std::pair GetHostByNameRequestImpl(HLERequestContext& ctx) { + struct Parameters { + u8 use_nsd_resolve; + u32 cancel_handle; + u64 process_id; + }; + + IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw(); + + LOG_WARNING( + Service, + "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}", + parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id); + + const auto host_buffer = ctx.ReadBuffer(0); + const std::string host = Common::StringFromBuffer(host_buffer); + // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. + + auto res = Network::GetAddrInfo(host, /*service*/ std::nullopt); + if (!res.has_value()) { + return {0, Translate(res.error())}; + } + + std::vector data = SerializeAddrInfoAsHostEnt(res.value(), host); + u32 data_size = static_cast(data.size()); + ctx.WriteBuffer(data, 0); + + return {data_size, GetAddrInfoError::SUCCESS}; +} + +void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) { + auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(ResultSuccess); + rb.Push(static_cast(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code + rb.Push(static_cast(GetAddrInfoErrorToErrno(emu_gai_err))); // errno + rb.Push(data_size); // serialized size +} + +void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) { + auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(ResultSuccess); + rb.Push(data_size); // serialized size + rb.Push(static_cast(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code + rb.Push(static_cast(GetAddrInfoErrorToErrno(emu_gai_err))); // errno +} + +static std::vector SerializeAddrInfo(const std::vector& vec, std::string_view host) { // Adapted from // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 std::vector data; - auto* current = addrinfo; - while (current != nullptr) { - struct SerializedResponseHeader { - u32 magic; - s32 flags; - s32 family; - s32 socket_type; - s32 protocol; - u32 address_length; - }; - static_assert(sizeof(SerializedResponseHeader) == 0x18, - "Response header size must be 0x18 bytes"); - - constexpr auto header_size = sizeof(SerializedResponseHeader); - const auto addr_size = - current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; - const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; - - const auto last_size = data.size(); - data.resize(last_size + header_size + addr_size + canonname_size); - - // Header in network byte order - SerializedResponseHeader header{}; - - constexpr auto HEADER_MAGIC = 0xBEEFCAFE; - header.magic = htonl(HEADER_MAGIC); - header.family = htonl(current->ai_family); - header.flags = htonl(current->ai_flags); - header.socket_type = htonl(current->ai_socktype); - header.protocol = htonl(current->ai_protocol); - header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0; - - auto* header_ptr = data.data() + last_size; - std::memcpy(header_ptr, &header, header_size); - - if (header.address_length == 0) { - std::memset(header_ptr + header_size, 0, 4); - } else { - switch (current->ai_family) { - case AF_INET: { - struct SockAddrIn { - s16 sin_family; - u16 sin_port; - u32 sin_addr; - u8 sin_zero[8]; - }; - - SockAddrIn serialized_addr{}; - const auto addr = *reinterpret_cast(current->ai_addr); - serialized_addr.sin_port = htons(addr.sin_port); - serialized_addr.sin_family = htons(addr.sin_family); - serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr); - std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn)); - - char addr_string_buf[64]{}; - inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf)); - LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf); - break; - } - case AF_INET6: { - struct SockAddrIn6 { - s16 sin6_family; - u16 sin6_port; - u32 sin6_flowinfo; - u8 sin6_addr[16]; - u32 sin6_scope_id; - }; - - SockAddrIn6 serialized_addr{}; - const auto addr = *reinterpret_cast(current->ai_addr); - serialized_addr.sin6_family = htons(addr.sin6_family); - serialized_addr.sin6_port = htons(addr.sin6_port); - serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo); - serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id); - std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr, - sizeof(SockAddrIn6::sin6_addr)); - std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6)); - - char addr_string_buf[64]{}; - inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf)); - LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf); - break; - } - default: - std::memcpy(header_ptr + header_size, current->ai_addr, addr_size); - break; - } - } - if (current->ai_canonname) { - std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size); + for (const Network::AddrInfo& addrinfo : vec) { + // serialized addrinfo: + Append(data, 0xBEEFCAFE); // magic + Append(data, 0); // ai_flags + Append(data, static_cast(Translate(addrinfo.family))); // ai_family + Append(data, static_cast(Translate(addrinfo.socket_type))); // ai_socktype + Append(data, static_cast(Translate(addrinfo.protocol))); // ai_protocol + Append(data, sizeof(SockAddrIn)); // ai_addrlen + // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size + + // ai_addr: + Append(data, static_cast(Translate(addrinfo.addr.family))); // sin_family + // On the Switch, the following fields are passed through htonl despite + // already being big-endian, so they end up as little-endian. + Append(data, addrinfo.addr.portno); // sin_port + Append(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); // sin_addr + data.resize(data.size() + 8, 0); // sin_zero + + if (addrinfo.canon_name.has_value()) { + AppendNulTerminated(data, *addrinfo.canon_name); } else { - *(header_ptr + header_size + addr_size) = 0; + data.push_back(0); } - current = current->ai_next; + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, + Network::IPv4AddressToString(addrinfo.addr.ip)); } - // 4-byte sentinel value - data.push_back(0); - data.push_back(0); - data.push_back(0); - data.push_back(0); + data.resize(data.size() + 4, 0); // 4-byte sentinel value return data; } -static std::pair GetAddrInfoRequestImpl(HLERequestContext& ctx) { +static std::pair GetAddrInfoRequestImpl(HLERequestContext& ctx) { struct Parameters { u8 use_nsd_resolve; - u32 unknown; + u32 cancel_handle; u64 process_id; }; IPC::RequestParser rp{ctx}; const auto parameters = rp.PopRaw(); - LOG_WARNING(Service, - "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", - parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); + LOG_WARNING( + Service, + "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}", + parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id); + + // TODO: If use_nsd_resolve is true, pass the name through NSD::Resolve + // before looking up. const auto host_buffer = ctx.ReadBuffer(0); const std::string host = Common::StringFromBuffer(host_buffer); - const auto service_buffer = ctx.ReadBuffer(1); - const std::string service = Common::StringFromBuffer(service_buffer); - - addrinfo* addrinfo; - // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now - s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo); + std::optional service = std::nullopt; + if (ctx.CanReadBuffer(1)) { + std::span service_buffer = ctx.ReadBuffer(1); + service = Common::StringFromBuffer(service_buffer); + } - u32 data_size = 0; - if (result_code == 0 && addrinfo != nullptr) { - const std::vector& data = SerializeAddrInfo(addrinfo, result_code, host); - data_size = static_cast(data.size()); - freeaddrinfo(addrinfo); + // Serialized hints are also passed in a buffer, but are ignored for now. - ctx.WriteBuffer(data, 0); + auto res = Network::GetAddrInfo(host, service); + if (!res.has_value()) { + return {0, Translate(res.error())}; } - return std::make_pair(data_size, result_code); + std::vector data = SerializeAddrInfo(res.value(), host); + u32 data_size = static_cast(data.size()); + ctx.WriteBuffer(data, 0); + + return {data_size, GetAddrInfoError::SUCCESS}; } void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { - auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx); - IPC::ResponseBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.Push(static_cast(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode - rb.Push(result_code); // errno - rb.Push(data_size); // serialized size + rb.Push(static_cast(GetAddrInfoErrorToErrno(emu_gai_err))); // errno + rb.Push(static_cast(emu_gai_err)); // getaddrinfo error code + rb.Push(data_size); // serialized size } void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) { // Additional options are ignored - auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(data_size); // serialized size + rb.Push(static_cast(emu_gai_err)); // getaddrinfo error code + rb.Push(static_cast(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code + rb.Push(static_cast(GetAddrInfoErrorToErrno(emu_gai_err))); // errno +} + +void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; - IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.Push(data_size); // serialized size - rb.Push(result_code); // errno - rb.Push(static_cast(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode - rb.Push(0); + rb.Push(0); // bsd errno } } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 18e3cd60c..d99a9d560 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -17,8 +17,11 @@ public: ~SFDNSRES() override; private: + void GetHostByNameRequest(HLERequestContext& ctx); + void GetHostByNameRequestWithOptions(HLERequestContext& ctx); void GetAddrInfoRequest(HLERequestContext& ctx); void GetAddrInfoRequestWithOptions(HLERequestContext& ctx); + void ResolverSetOptionRequest(HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index acd2dae7b..77426c46e 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -22,13 +22,35 @@ enum class Errno : u32 { CONNRESET = 104, NOTCONN = 107, TIMEDOUT = 110, + INPROGRESS = 115, +}; + +enum class GetAddrInfoError : s32 { + SUCCESS = 0, + ADDRFAMILY = 1, + AGAIN = 2, + BADFLAGS = 3, + FAIL = 4, + FAMILY = 5, + MEMORY = 6, + NODATA = 7, + NONAME = 8, + SERVICE = 9, + SOCKTYPE = 10, + SYSTEM = 11, + BADHINTS = 12, + PROTOCOL = 13, + OVERFLOW_ = 14, // avoid name collision with Windows macro + OTHER = 15, }; enum class Domain : u32 { + Unspecified = 0, INET = 2, }; enum class Type : u32 { + Unspecified = 0, STREAM = 1, DGRAM = 2, RAW = 3, @@ -36,12 +58,16 @@ enum class Type : u32 { }; enum class Protocol : u32 { - UNSPECIFIED = 0, + Unspecified = 0, ICMP = 1, TCP = 6, UDP = 17, }; +enum class SocketLevel : u32 { + SOCKET = 0xffff, // i.e. SOL_SOCKET +}; + enum class OptName : u32 { REUSEADDR = 0x4, KEEPALIVE = 0x8, @@ -51,6 +77,8 @@ enum class OptName : u32 { RCVBUF = 0x1002, SNDTIMEO = 0x1005, RCVTIMEO = 0x1006, + ERROR_ = 0x1007, // avoid name collision with Windows macro + NOSIGPIPE = 0x800, // at least according to libnx }; enum class ShutdownHow : s32 { @@ -80,6 +108,9 @@ enum class PollEvents : u16 { Err = 1 << 3, Hup = 1 << 4, Nval = 1 << 5, + RdNorm = 1 << 6, + RdBand = 1 << 7, + WrBand = 1 << 8, }; DECLARE_ENUM_FLAG_OPERATORS(PollEvents); diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 594e58f90..2f9a0e39c 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -29,6 +29,8 @@ Errno Translate(Network::Errno value) { return Errno::TIMEDOUT; case Network::Errno::CONNRESET: return Errno::CONNRESET; + case Network::Errno::INPROGRESS: + return Errno::INPROGRESS; default: UNIMPLEMENTED_MSG("Unimplemented errno={}", value); return Errno::SUCCESS; @@ -39,8 +41,50 @@ std::pair Translate(std::pair value) { return {value.first, Translate(value.second)}; } +GetAddrInfoError Translate(Network::GetAddrInfoError error) { + switch (error) { + case Network::GetAddrInfoError::SUCCESS: + return GetAddrInfoError::SUCCESS; + case Network::GetAddrInfoError::ADDRFAMILY: + return GetAddrInfoError::ADDRFAMILY; + case Network::GetAddrInfoError::AGAIN: + return GetAddrInfoError::AGAIN; + case Network::GetAddrInfoError::BADFLAGS: + return GetAddrInfoError::BADFLAGS; + case Network::GetAddrInfoError::FAIL: + return GetAddrInfoError::FAIL; + case Network::GetAddrInfoError::FAMILY: + return GetAddrInfoError::FAMILY; + case Network::GetAddrInfoError::MEMORY: + return GetAddrInfoError::MEMORY; + case Network::GetAddrInfoError::NODATA: + return GetAddrInfoError::NODATA; + case Network::GetAddrInfoError::NONAME: + return GetAddrInfoError::NONAME; + case Network::GetAddrInfoError::SERVICE: + return GetAddrInfoError::SERVICE; + case Network::GetAddrInfoError::SOCKTYPE: + return GetAddrInfoError::SOCKTYPE; + case Network::GetAddrInfoError::SYSTEM: + return GetAddrInfoError::SYSTEM; + case Network::GetAddrInfoError::BADHINTS: + return GetAddrInfoError::BADHINTS; + case Network::GetAddrInfoError::PROTOCOL: + return GetAddrInfoError::PROTOCOL; + case Network::GetAddrInfoError::OVERFLOW_: + return GetAddrInfoError::OVERFLOW_; + case Network::GetAddrInfoError::OTHER: + return GetAddrInfoError::OTHER; + default: + UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error); + return GetAddrInfoError::OTHER; + } +} + Network::Domain Translate(Domain domain) { switch (domain) { + case Domain::Unspecified: + return Network::Domain::Unspecified; case Domain::INET: return Network::Domain::INET; default: @@ -51,6 +95,8 @@ Network::Domain Translate(Domain domain) { Domain Translate(Network::Domain domain) { switch (domain) { + case Network::Domain::Unspecified: + return Domain::Unspecified; case Network::Domain::INET: return Domain::INET; default: @@ -61,39 +107,69 @@ Domain Translate(Network::Domain domain) { Network::Type Translate(Type type) { switch (type) { + case Type::Unspecified: + return Network::Type::Unspecified; case Type::STREAM: return Network::Type::STREAM; case Type::DGRAM: return Network::Type::DGRAM; + case Type::RAW: + return Network::Type::RAW; + case Type::SEQPACKET: + return Network::Type::SEQPACKET; default: UNIMPLEMENTED_MSG("Unimplemented type={}", type); return Network::Type{}; } } -Network::Protocol Translate(Type type, Protocol protocol) { +Type Translate(Network::Type type) { + switch (type) { + case Network::Type::Unspecified: + return Type::Unspecified; + case Network::Type::STREAM: + return Type::STREAM; + case Network::Type::DGRAM: + return Type::DGRAM; + case Network::Type::RAW: + return Type::RAW; + case Network::Type::SEQPACKET: + return Type::SEQPACKET; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", type); + return Type{}; + } +} + +Network::Protocol Translate(Protocol protocol) { switch (protocol) { - case Protocol::UNSPECIFIED: - LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type"); - switch (type) { - case Type::DGRAM: - return Network::Protocol::UDP; - case Type::STREAM: - return Network::Protocol::TCP; - default: - return Network::Protocol::TCP; - } + case Protocol::Unspecified: + return Network::Protocol::Unspecified; case Protocol::TCP: return Network::Protocol::TCP; case Protocol::UDP: return Network::Protocol::UDP; default: UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return Network::Protocol::TCP; + return Network::Protocol::Unspecified; + } +} + +Protocol Translate(Network::Protocol protocol) { + switch (protocol) { + case Network::Protocol::Unspecified: + return Protocol::Unspecified; + case Network::Protocol::TCP: + return Protocol::TCP; + case Network::Protocol::UDP: + return Protocol::UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); + return Protocol::Unspecified; } } -Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { +Network::PollEvents Translate(PollEvents flags) { Network::PollEvents result{}; const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) { if (True(flags & from)) { @@ -107,12 +183,15 @@ Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { translate(PollEvents::Err, Network::PollEvents::Err); translate(PollEvents::Hup, Network::PollEvents::Hup); translate(PollEvents::Nval, Network::PollEvents::Nval); + translate(PollEvents::RdNorm, Network::PollEvents::RdNorm); + translate(PollEvents::RdBand, Network::PollEvents::RdBand); + translate(PollEvents::WrBand, Network::PollEvents::WrBand); UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); return result; } -PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { +PollEvents Translate(Network::PollEvents flags) { PollEvents result{}; const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) { if (True(flags & from)) { @@ -127,13 +206,18 @@ PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { translate(Network::PollEvents::Err, PollEvents::Err); translate(Network::PollEvents::Hup, PollEvents::Hup); translate(Network::PollEvents::Nval, PollEvents::Nval); + translate(Network::PollEvents::RdNorm, PollEvents::RdNorm); + translate(Network::PollEvents::RdBand, PollEvents::RdBand); + translate(Network::PollEvents::WrBand, PollEvents::WrBand); UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); return result; } Network::SockAddrIn Translate(SockAddrIn value) { - ASSERT(value.len == 0 || value.len == sizeof(value)); + // Note: 6 is incorrect, but can be passed by homebrew (because libnx sets + // sin_len to 6 when deserializing getaddrinfo results). + ASSERT(value.len == 0 || value.len == sizeof(value) || value.len == 6); return { .family = Translate(static_cast(value.family)), diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index c93291d3e..694868b37 100644 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -17,6 +17,9 @@ Errno Translate(Network::Errno value); /// Translate abstract return value errno pair to guest return value errno pair std::pair Translate(std::pair value); +/// Translate abstract getaddrinfo error to guest getaddrinfo error +GetAddrInfoError Translate(Network::GetAddrInfoError value); + /// Translate guest domain to abstract domain Network::Domain Translate(Domain domain); @@ -26,14 +29,20 @@ Domain Translate(Network::Domain domain); /// Translate guest type to abstract type Network::Type Translate(Type type); +/// Translate abstract type to guest type +Type Translate(Network::Type type); + /// Translate guest protocol to abstract protocol -Network::Protocol Translate(Type type, Protocol protocol); +Network::Protocol Translate(Protocol protocol); -/// Translate abstract poll event flags to guest poll event flags -Network::PollEvents TranslatePollEventsToHost(PollEvents flags); +/// Translate abstract protocol to guest protocol +Protocol Translate(Network::Protocol protocol); /// Translate guest poll event flags to abstract poll event flags -PollEvents TranslatePollEventsToGuest(Network::PollEvents flags); +Network::PollEvents Translate(PollEvents flags); + +/// Translate abstract poll event flags to guest poll event flags +PollEvents Translate(Network::PollEvents flags); /// Translate guest socket address structure to abstract socket address structure Network::SockAddrIn Translate(SockAddrIn value); -- cgit v1.2.3