summaryrefslogtreecommitdiff
path: root/src/core/hle/service/sockets
diff options
context:
space:
mode:
authorGravatar comex2023-06-19 18:17:43 -0700
committerGravatar comex2023-06-25 12:53:31 -0700
commit8e703e08dfcf735a08df2ceff6a05221b7cc981f (patch)
tree771ebe71883ff9e179156f2b38b21b05070d7667 /src/core/hle/service/sockets
parentMerge pull request #10825 from 8bitDream/vcpkg-zlib (diff)
downloadyuzu-8e703e08dfcf735a08df2ceff6a05221b7cc981f.tar.gz
yuzu-8e703e08dfcf735a08df2ceff6a05221b7cc981f.tar.xz
yuzu-8e703e08dfcf735a08df2ceff6a05221b7cc981f.zip
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 <typename T> static void Append(std::vector<u8>& vec, T t); void AppendNulTerminated(std::vector<u8>& 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<u8>` rather than `std::vector<u8>&` to avoid needing to copy the data to/from a vector when those methods are called from the TLS implementation.
Diffstat (limited to 'src/core/hle/service/sockets')
-rw-r--r--src/core/hle/service/sockets/bsd.cpp107
-rw-r--r--src/core/hle/service/sockets/bsd.h13
-rw-r--r--src/core/hle/service/sockets/nsd.cpp58
-rw-r--r--src/core/hle/service/sockets/nsd.h4
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp345
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h3
-rw-r--r--src/core/hle/service/sockets/sockets.h33
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp114
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h17
9 files changed, 508 insertions, 186 deletions
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 @@
20#include "core/internal_network/sockets.h" 20#include "core/internal_network/sockets.h"
21#include "network/network.h" 21#include "network/network.h"
22 22
23using Common::Expected;
24using Common::Unexpected;
25
23namespace Service::Sockets { 26namespace Service::Sockets {
24 27
25namespace { 28namespace {
@@ -265,16 +268,19 @@ void BSD::GetSockOpt(HLERequestContext& ctx) {
265 const u32 level = rp.Pop<u32>(); 268 const u32 level = rp.Pop<u32>();
266 const auto optname = static_cast<OptName>(rp.Pop<u32>()); 269 const auto optname = static_cast<OptName>(rp.Pop<u32>());
267 270
268 LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname);
269
270 std::vector<u8> optval(ctx.GetWriteBufferSize()); 271 std::vector<u8> optval(ctx.GetWriteBufferSize());
271 272
273 LOG_WARNING(Service, "called. fd={} level={} optname=0x{:x} len=0x{:x}", fd, level, optname,
274 optval.size());
275
276 Errno err = GetSockOptImpl(fd, level, optname, optval);
277
272 ctx.WriteBuffer(optval); 278 ctx.WriteBuffer(optval);
273 279
274 IPC::ResponseBuilder rb{ctx, 5}; 280 IPC::ResponseBuilder rb{ctx, 5};
275 rb.Push(ResultSuccess); 281 rb.Push(ResultSuccess);
276 rb.Push<s32>(-1); 282 rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
277 rb.PushEnum(Errno::NOTCONN); 283 rb.PushEnum(err);
278 rb.Push<u32>(static_cast<u32>(optval.size())); 284 rb.Push<u32>(static_cast<u32>(optval.size()));
279} 285}
280 286
@@ -436,6 +442,18 @@ void BSD::Close(HLERequestContext& ctx) {
436 BuildErrnoResponse(ctx, CloseImpl(fd)); 442 BuildErrnoResponse(ctx, CloseImpl(fd));
437} 443}
438 444
445void BSD::DuplicateSocket(HLERequestContext& ctx) {
446 IPC::RequestParser rp{ctx};
447 const s32 fd = rp.Pop<s32>();
448 [[maybe_unused]] const u64 unused = rp.Pop<u64>();
449
450 Common::Expected<s32, Errno> res = DuplicateSocketImpl(fd);
451 IPC::ResponseBuilder rb{ctx, 4};
452 rb.Push(ResultSuccess);
453 rb.Push(res.value_or(0)); // ret
454 rb.Push(res ? 0 : static_cast<s32>(res.error())); // bsd errno
455}
456
439void BSD::EventFd(HLERequestContext& ctx) { 457void BSD::EventFd(HLERequestContext& ctx) {
440 IPC::RequestParser rp{ctx}; 458 IPC::RequestParser rp{ctx};
441 const u64 initval = rp.Pop<u64>(); 459 const u64 initval = rp.Pop<u64>();
@@ -477,12 +495,12 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
477 495
478 auto room_member = room_network.GetRoomMember().lock(); 496 auto room_member = room_network.GetRoomMember().lock();
479 if (room_member && room_member->IsConnected()) { 497 if (room_member && room_member->IsConnected()) {
480 descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); 498 descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
481 } else { 499 } else {
482 descriptor.socket = std::make_unique<Network::Socket>(); 500 descriptor.socket = std::make_shared<Network::Socket>();
483 } 501 }
484 502
485 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); 503 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
486 descriptor.is_connection_based = IsConnectionBased(type); 504 descriptor.is_connection_based = IsConnectionBased(type);
487 505
488 return {fd, Errno::SUCCESS}; 506 return {fd, Errno::SUCCESS};
@@ -538,7 +556,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
538 std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) { 556 std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
539 Network::PollFD result; 557 Network::PollFD result;
540 result.socket = file_descriptors[pollfd.fd]->socket.get(); 558 result.socket = file_descriptors[pollfd.fd]->socket.get();
541 result.events = TranslatePollEventsToHost(pollfd.events); 559 result.events = Translate(pollfd.events);
542 result.revents = Network::PollEvents{}; 560 result.revents = Network::PollEvents{};
543 return result; 561 return result;
544 }); 562 });
@@ -547,7 +565,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
547 565
548 const size_t num = host_pollfds.size(); 566 const size_t num = host_pollfds.size();
549 for (size_t i = 0; i < num; ++i) { 567 for (size_t i = 0; i < num; ++i) {
550 fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents); 568 fds[i].revents = Translate(host_pollfds[i].revents);
551 } 569 }
552 std::memcpy(write_buffer.data(), fds.data(), length); 570 std::memcpy(write_buffer.data(), fds.data(), length);
553 571
@@ -617,7 +635,8 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
617 } 635 }
618 const SockAddrIn guest_addrin = Translate(addr_in); 636 const SockAddrIn guest_addrin = Translate(addr_in);
619 637
620 ASSERT(write_buffer.size() == sizeof(guest_addrin)); 638 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
639 write_buffer.resize(sizeof(guest_addrin));
621 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 640 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
622 return Translate(bsd_errno); 641 return Translate(bsd_errno);
623} 642}
@@ -633,7 +652,8 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
633 } 652 }
634 const SockAddrIn guest_addrin = Translate(addr_in); 653 const SockAddrIn guest_addrin = Translate(addr_in);
635 654
636 ASSERT(write_buffer.size() == sizeof(guest_addrin)); 655 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
656 write_buffer.resize(sizeof(guest_addrin));
637 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 657 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
638 return Translate(bsd_errno); 658 return Translate(bsd_errno);
639} 659}
@@ -671,13 +691,47 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
671 } 691 }
672} 692}
673 693
674Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { 694Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
675 UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET 695 if (!IsFileDescriptorValid(fd)) {
696 return Errno::BADF;
697 }
698
699 if (level != static_cast<u32>(SocketLevel::SOCKET)) {
700 UNIMPLEMENTED_MSG("Unknown getsockopt level");
701 return Errno::SUCCESS;
702 }
703
704 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
705
706 switch (optname) {
707 case OptName::ERROR_: {
708 auto [pending_err, getsockopt_err] = socket->GetPendingError();
709 if (getsockopt_err == Network::Errno::SUCCESS) {
710 Errno translated_pending_err = Translate(pending_err);
711 ASSERT_OR_EXECUTE_MSG(
712 optval.size() == sizeof(Errno), { return Errno::INVAL; },
713 "Incorrect getsockopt option size");
714 optval.resize(sizeof(Errno));
715 memcpy(optval.data(), &translated_pending_err, sizeof(Errno));
716 }
717 return Translate(getsockopt_err);
718 }
719 default:
720 UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
721 return Errno::SUCCESS;
722 }
723}
676 724
725Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
677 if (!IsFileDescriptorValid(fd)) { 726 if (!IsFileDescriptorValid(fd)) {
678 return Errno::BADF; 727 return Errno::BADF;
679 } 728 }
680 729
730 if (level != static_cast<u32>(SocketLevel::SOCKET)) {
731 UNIMPLEMENTED_MSG("Unknown setsockopt level");
732 return Errno::SUCCESS;
733 }
734
681 Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); 735 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
682 736
683 if (optname == OptName::LINGER) { 737 if (optname == OptName::LINGER) {
@@ -711,6 +765,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
711 return Translate(socket->SetSndTimeo(value)); 765 return Translate(socket->SetSndTimeo(value));
712 case OptName::RCVTIMEO: 766 case OptName::RCVTIMEO:
713 return Translate(socket->SetRcvTimeo(value)); 767 return Translate(socket->SetRcvTimeo(value));
768 case OptName::NOSIGPIPE:
769 LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
770 return Errno::SUCCESS;
714 default: 771 default:
715 UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); 772 UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
716 return Errno::SUCCESS; 773 return Errno::SUCCESS;
@@ -841,6 +898,28 @@ Errno BSD::CloseImpl(s32 fd) {
841 return bsd_errno; 898 return bsd_errno;
842} 899}
843 900
901Expected<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
902 if (!IsFileDescriptorValid(fd)) {
903 return Unexpected(Errno::BADF);
904 }
905
906 const s32 new_fd = FindFreeFileDescriptorHandle();
907 if (new_fd < 0) {
908 LOG_ERROR(Service, "No more file descriptors available");
909 return Unexpected(Errno::MFILE);
910 }
911
912 file_descriptors[new_fd] = file_descriptors[fd];
913 return new_fd;
914}
915
916std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
917 if (!IsFileDescriptorValid(fd)) {
918 return std::nullopt;
919 }
920 return file_descriptors[fd]->socket;
921}
922
844s32 BSD::FindFreeFileDescriptorHandle() noexcept { 923s32 BSD::FindFreeFileDescriptorHandle() noexcept {
845 for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { 924 for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
846 if (!file_descriptors[fd]) { 925 if (!file_descriptors[fd]) {
@@ -911,7 +990,7 @@ BSD::BSD(Core::System& system_, const char* name)
911 {24, &BSD::Write, "Write"}, 990 {24, &BSD::Write, "Write"},
912 {25, &BSD::Read, "Read"}, 991 {25, &BSD::Read, "Read"},
913 {26, &BSD::Close, "Close"}, 992 {26, &BSD::Close, "Close"},
914 {27, nullptr, "DuplicateSocket"}, 993 {27, &BSD::DuplicateSocket, "DuplicateSocket"},
915 {28, nullptr, "GetResourceStatistics"}, 994 {28, nullptr, "GetResourceStatistics"},
916 {29, nullptr, "RecvMMsg"}, 995 {29, nullptr, "RecvMMsg"},
917 {30, nullptr, "SendMMsg"}, 996 {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 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/expected.h"
11#include "common/socket_types.h" 12#include "common/socket_types.h"
12#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
13#include "core/hle/service/sockets/sockets.h" 14#include "core/hle/service/sockets/sockets.h"
@@ -29,12 +30,19 @@ public:
29 explicit BSD(Core::System& system_, const char* name); 30 explicit BSD(Core::System& system_, const char* name);
30 ~BSD() override; 31 ~BSD() override;
31 32
33 // These methods are called from SSL; the first two are also called from
34 // this class for the corresponding IPC methods.
35 // On the real device, the SSL service makes IPC calls to this service.
36 Common::Expected<s32, Errno> DuplicateSocketImpl(s32 fd);
37 Errno CloseImpl(s32 fd);
38 std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
39
32private: 40private:
33 /// Maximum number of file descriptors 41 /// Maximum number of file descriptors
34 static constexpr size_t MAX_FD = 128; 42 static constexpr size_t MAX_FD = 128;
35 43
36 struct FileDescriptor { 44 struct FileDescriptor {
37 std::unique_ptr<Network::SocketBase> socket; 45 std::shared_ptr<Network::SocketBase> socket;
38 s32 flags = 0; 46 s32 flags = 0;
39 bool is_connection_based = false; 47 bool is_connection_based = false;
40 }; 48 };
@@ -138,6 +146,7 @@ private:
138 void Write(HLERequestContext& ctx); 146 void Write(HLERequestContext& ctx);
139 void Read(HLERequestContext& ctx); 147 void Read(HLERequestContext& ctx);
140 void Close(HLERequestContext& ctx); 148 void Close(HLERequestContext& ctx);
149 void DuplicateSocket(HLERequestContext& ctx);
141 void EventFd(HLERequestContext& ctx); 150 void EventFd(HLERequestContext& ctx);
142 151
143 template <typename Work> 152 template <typename Work>
@@ -153,6 +162,7 @@ private:
153 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); 162 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
154 Errno ListenImpl(s32 fd, s32 backlog); 163 Errno ListenImpl(s32 fd, s32 backlog);
155 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); 164 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
165 Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
156 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); 166 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval);
157 Errno ShutdownImpl(s32 fd, s32 how); 167 Errno ShutdownImpl(s32 fd, s32 how);
158 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); 168 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
@@ -161,7 +171,6 @@ private:
161 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message); 171 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
162 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message, 172 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
163 std::span<const u8> addr); 173 std::span<const u8> addr);
164 Errno CloseImpl(s32 fd);
165 174
166 s32 FindFreeFileDescriptorHandle() noexcept; 175 s32 FindFreeFileDescriptorHandle() noexcept;
167 bool IsFileDescriptorValid(s32 fd) const noexcept; 176 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 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/ipc_helpers.h"
4#include "core/hle/service/sockets/nsd.h" 5#include "core/hle/service/sockets/nsd.h"
5 6
7#include "common/string_util.h"
8
6namespace Service::Sockets { 9namespace Service::Sockets {
7 10
11constexpr Result ResultOverflow{ErrorModule::NSD, 6};
12
8NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 13NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
9 // clang-format off 14 // clang-format off
10 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
@@ -15,8 +20,8 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
15 {13, nullptr, "DeleteSettings"}, 20 {13, nullptr, "DeleteSettings"},
16 {14, nullptr, "ImportSettings"}, 21 {14, nullptr, "ImportSettings"},
17 {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"}, 22 {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
18 {20, nullptr, "Resolve"}, 23 {20, &NSD::Resolve, "Resolve"},
19 {21, nullptr, "ResolveEx"}, 24 {21, &NSD::ResolveEx, "ResolveEx"},
20 {30, nullptr, "GetNasServiceSetting"}, 25 {30, nullptr, "GetNasServiceSetting"},
21 {31, nullptr, "GetNasServiceSettingEx"}, 26 {31, nullptr, "GetNasServiceSettingEx"},
22 {40, nullptr, "GetNasRequestFqdn"}, 27 {40, nullptr, "GetNasRequestFqdn"},
@@ -40,6 +45,55 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
40 RegisterHandlers(functions); 45 RegisterHandlers(functions);
41} 46}
42 47
48static ResultVal<std::string> ResolveImpl(const std::string& fqdn_in) {
49 // The real implementation makes various substitutions.
50 // For now we just return the string as-is, which is good enough when not
51 // connecting to real Nintendo servers.
52 LOG_WARNING(Service, "(STUBBED) called({})", fqdn_in);
53 return fqdn_in;
54}
55
56static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>& fqdn_out) {
57 const auto res = ResolveImpl(fqdn_in);
58 if (res.Failed()) {
59 return res.Code();
60 }
61 if (res->size() >= fqdn_out.size()) {
62 return ResultOverflow;
63 }
64 std::memcpy(fqdn_out.data(), res->c_str(), res->size() + 1);
65 return ResultSuccess;
66}
67
68void NSD::Resolve(HLERequestContext& ctx) {
69 const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));
70
71 std::array<char, 0x100> fqdn_out{};
72 Result res = ResolveCommon(fqdn_in, fqdn_out);
73
74 ctx.WriteBuffer(fqdn_out);
75 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(res);
77}
78
79void NSD::ResolveEx(HLERequestContext& ctx) {
80 const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));
81
82 std::array<char, 0x100> fqdn_out;
83 Result res = ResolveCommon(fqdn_in, fqdn_out);
84
85 if (res.IsError()) {
86 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(res);
88 return;
89 }
90
91 ctx.WriteBuffer(fqdn_out);
92 IPC::ResponseBuilder rb{ctx, 4};
93 rb.Push(ResultSuccess);
94 rb.Push(ResultSuccess);
95}
96
43NSD::~NSD() = default; 97NSD::~NSD() = default;
44 98
45} // namespace Service::Sockets 99} // 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<NSD> {
15public: 15public:
16 explicit NSD(Core::System& system_, const char* name); 16 explicit NSD(Core::System& system_, const char* name);
17 ~NSD() override; 17 ~NSD() override;
18
19private:
20 void Resolve(HLERequestContext& ctx);
21 void ResolveEx(HLERequestContext& ctx);
18}; 22};
19 23
20} // namespace Service::Sockets 24} // 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 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/sockets/sfdnsres.h" 12#include "core/hle/service/sockets/sfdnsres.h"
13#include "core/hle/service/sockets/sockets.h"
14#include "core/hle/service/sockets/sockets_translate.h"
15#include "core/internal_network/network.h"
13#include "core/memory.h" 16#include "core/memory.h"
14 17
15#ifdef _WIN32
16#include <ws2tcpip.h>
17#elif YUZU_UNIX
18#include <arpa/inet.h>
19#include <netdb.h>
20#include <netinet/in.h>
21#include <sys/socket.h>
22#ifndef EAI_NODATA
23#define EAI_NODATA EAI_NONAME
24#endif
25#endif
26
27namespace Service::Sockets { 18namespace Service::Sockets {
28 19
29SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} { 20SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} {
30 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
31 {0, nullptr, "SetDnsAddressesPrivateRequest"}, 22 {0, nullptr, "SetDnsAddressesPrivateRequest"},
32 {1, nullptr, "GetDnsAddressPrivateRequest"}, 23 {1, nullptr, "GetDnsAddressPrivateRequest"},
33 {2, nullptr, "GetHostByNameRequest"}, 24 {2, &SFDNSRES::GetHostByNameRequest, "GetHostByNameRequest"},
34 {3, nullptr, "GetHostByAddrRequest"}, 25 {3, nullptr, "GetHostByAddrRequest"},
35 {4, nullptr, "GetHostStringErrorRequest"}, 26 {4, nullptr, "GetHostStringErrorRequest"},
36 {5, nullptr, "GetGaiStringErrorRequest"}, 27 {5, nullptr, "GetGaiStringErrorRequest"},
@@ -38,11 +29,11 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
38 {7, nullptr, "GetNameInfoRequest"}, 29 {7, nullptr, "GetNameInfoRequest"},
39 {8, nullptr, "RequestCancelHandleRequest"}, 30 {8, nullptr, "RequestCancelHandleRequest"},
40 {9, nullptr, "CancelRequest"}, 31 {9, nullptr, "CancelRequest"},
41 {10, nullptr, "GetHostByNameRequestWithOptions"}, 32 {10, &SFDNSRES::GetHostByNameRequestWithOptions, "GetHostByNameRequestWithOptions"},
42 {11, nullptr, "GetHostByAddrRequestWithOptions"}, 33 {11, nullptr, "GetHostByAddrRequestWithOptions"},
43 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, 34 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
44 {13, nullptr, "GetNameInfoRequestWithOptions"}, 35 {13, nullptr, "GetNameInfoRequestWithOptions"},
45 {14, nullptr, "ResolverSetOptionRequest"}, 36 {14, &SFDNSRES::ResolverSetOptionRequest, "ResolverSetOptionRequest"},
46 {15, nullptr, "ResolverGetOptionRequest"}, 37 {15, nullptr, "ResolverGetOptionRequest"},
47 }; 38 };
48 RegisterHandlers(functions); 39 RegisterHandlers(functions);
@@ -59,188 +50,246 @@ enum class NetDbError : s32 {
59 NoData = 4, 50 NoData = 4,
60}; 51};
61 52
62static NetDbError AddrInfoErrorToNetDbError(s32 result) { 53static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
63 // Best effort guess to map errors 54 // These combinations have been verified on console (but are not
55 // exhaustive).
64 switch (result) { 56 switch (result) {
65 case 0: 57 case GetAddrInfoError::SUCCESS:
66 return NetDbError::Success; 58 return NetDbError::Success;
67 case EAI_AGAIN: 59 case GetAddrInfoError::AGAIN:
68 return NetDbError::TryAgain; 60 return NetDbError::TryAgain;
69 case EAI_NODATA: 61 case GetAddrInfoError::NODATA:
70 return NetDbError::NoData; 62 return NetDbError::HostNotFound;
63 case GetAddrInfoError::SERVICE:
64 return NetDbError::Success;
71 default: 65 default:
72 return NetDbError::HostNotFound; 66 return NetDbError::HostNotFound;
73 } 67 }
74} 68}
75 69
76static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, 70static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
71 // These combinations have been verified on console (but are not
72 // exhaustive).
73 switch (result) {
74 case GetAddrInfoError::SUCCESS:
75 // Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
76 // some reason, but that doesn't seem useful to implement.
77 return Errno::SUCCESS;
78 case GetAddrInfoError::AGAIN:
79 return Errno::SUCCESS;
80 case GetAddrInfoError::NODATA:
81 return Errno::SUCCESS;
82 case GetAddrInfoError::SERVICE:
83 return Errno::INVAL;
84 default:
85 return Errno::SUCCESS;
86 }
87}
88
89template <typename T>
90static void Append(std::vector<u8>& vec, T t) {
91 size_t off = vec.size();
92 vec.resize(off + sizeof(T));
93 std::memcpy(vec.data() + off, &t, sizeof(T));
94}
95
96static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
97 size_t off = vec.size();
98 vec.resize(off + str.size() + 1);
99 std::memcpy(vec.data() + off, str.data(), str.size());
100}
101
102// We implement gethostbyname using the host's getaddrinfo rather than the
103// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
104// behaves the same on Unix and Windows, unlike gethostbyname where Windows
105// doesn't implement h_errno.
106static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
107 std::string_view host) {
108
109 std::vector<u8> data;
110 // h_name: use the input hostname (append nul-terminated)
111 AppendNulTerminated(data, host);
112 // h_aliases: leave empty
113
114 Append<u32_be>(data, 0); // count of h_aliases
115 // (If the count were nonzero, the aliases would be appended as nul-terminated here.)
116 Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
117 Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
118 // h_addr_list:
119 size_t count = vec.size();
120 ASSERT(count <= UINT32_MAX);
121 Append<u32_be>(data, static_cast<uint32_t>(count));
122 for (const Network::AddrInfo& addrinfo : vec) {
123 // On the Switch, this is passed through htonl despite already being
124 // big-endian, so it ends up as little-endian.
125 Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip));
126
127 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host,
128 Network::IPv4AddressToString(addrinfo.addr.ip));
129 }
130 return data;
131}
132
133static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
134 struct Parameters {
135 u8 use_nsd_resolve;
136 u32 cancel_handle;
137 u64 process_id;
138 };
139
140 IPC::RequestParser rp{ctx};
141 const auto parameters = rp.PopRaw<Parameters>();
142
143 LOG_WARNING(
144 Service,
145 "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}",
146 parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id);
147
148 const auto host_buffer = ctx.ReadBuffer(0);
149 const std::string host = Common::StringFromBuffer(host_buffer);
150 // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
151
152 auto res = Network::GetAddrInfo(host, /*service*/ std::nullopt);
153 if (!res.has_value()) {
154 return {0, Translate(res.error())};
155 }
156
157 std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host);
158 u32 data_size = static_cast<u32>(data.size());
159 ctx.WriteBuffer(data, 0);
160
161 return {data_size, GetAddrInfoError::SUCCESS};
162}
163
164void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
165 auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);
166
167 IPC::ResponseBuilder rb{ctx, 5};
168 rb.Push(ResultSuccess);
169 rb.Push(static_cast<s32>(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code
170 rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
171 rb.Push(data_size); // serialized size
172}
173
174void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
175 auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);
176
177 IPC::ResponseBuilder rb{ctx, 5};
178 rb.Push(ResultSuccess);
179 rb.Push(data_size); // serialized size
180 rb.Push(static_cast<s32>(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code
181 rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
182}
183
184static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
77 std::string_view host) { 185 std::string_view host) {
78 // Adapted from 186 // Adapted from
79 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 187 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
80 std::vector<u8> data; 188 std::vector<u8> data;
81 189
82 auto* current = addrinfo; 190 for (const Network::AddrInfo& addrinfo : vec) {
83 while (current != nullptr) { 191 // serialized addrinfo:
84 struct SerializedResponseHeader { 192 Append<u32_be>(data, 0xBEEFCAFE); // magic
85 u32 magic; 193 Append<u32_be>(data, 0); // ai_flags
86 s32 flags; 194 Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
87 s32 family; 195 Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
88 s32 socket_type; 196 Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
89 s32 protocol; 197 Append<u32_be>(data, sizeof(SockAddrIn)); // ai_addrlen
90 u32 address_length; 198 // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
91 }; 199
92 static_assert(sizeof(SerializedResponseHeader) == 0x18, 200 // ai_addr:
93 "Response header size must be 0x18 bytes"); 201 Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
94 202 // On the Switch, the following fields are passed through htonl despite
95 constexpr auto header_size = sizeof(SerializedResponseHeader); 203 // already being big-endian, so they end up as little-endian.
96 const auto addr_size = 204 Append<u16_le>(data, addrinfo.addr.portno); // sin_port
97 current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; 205 Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); // sin_addr
98 const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; 206 data.resize(data.size() + 8, 0); // sin_zero
99 207
100 const auto last_size = data.size(); 208 if (addrinfo.canon_name.has_value()) {
101 data.resize(last_size + header_size + addr_size + canonname_size); 209 AppendNulTerminated(data, *addrinfo.canon_name);
102
103 // Header in network byte order
104 SerializedResponseHeader header{};
105
106 constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
107 header.magic = htonl(HEADER_MAGIC);
108 header.family = htonl(current->ai_family);
109 header.flags = htonl(current->ai_flags);
110 header.socket_type = htonl(current->ai_socktype);
111 header.protocol = htonl(current->ai_protocol);
112 header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
113
114 auto* header_ptr = data.data() + last_size;
115 std::memcpy(header_ptr, &header, header_size);
116
117 if (header.address_length == 0) {
118 std::memset(header_ptr + header_size, 0, 4);
119 } else {
120 switch (current->ai_family) {
121 case AF_INET: {
122 struct SockAddrIn {
123 s16 sin_family;
124 u16 sin_port;
125 u32 sin_addr;
126 u8 sin_zero[8];
127 };
128
129 SockAddrIn serialized_addr{};
130 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
131 serialized_addr.sin_port = htons(addr.sin_port);
132 serialized_addr.sin_family = htons(addr.sin_family);
133 serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
134 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
135
136 char addr_string_buf[64]{};
137 inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
138 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
139 break;
140 }
141 case AF_INET6: {
142 struct SockAddrIn6 {
143 s16 sin6_family;
144 u16 sin6_port;
145 u32 sin6_flowinfo;
146 u8 sin6_addr[16];
147 u32 sin6_scope_id;
148 };
149
150 SockAddrIn6 serialized_addr{};
151 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
152 serialized_addr.sin6_family = htons(addr.sin6_family);
153 serialized_addr.sin6_port = htons(addr.sin6_port);
154 serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
155 serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
156 std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
157 sizeof(SockAddrIn6::sin6_addr));
158 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
159
160 char addr_string_buf[64]{};
161 inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
162 LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
163 break;
164 }
165 default:
166 std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
167 break;
168 }
169 }
170 if (current->ai_canonname) {
171 std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
172 } else { 210 } else {
173 *(header_ptr + header_size + addr_size) = 0; 211 data.push_back(0);
174 } 212 }
175 213
176 current = current->ai_next; 214 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host,
215 Network::IPv4AddressToString(addrinfo.addr.ip));
177 } 216 }
178 217
179 // 4-byte sentinel value 218 data.resize(data.size() + 4, 0); // 4-byte sentinel value
180 data.push_back(0);
181 data.push_back(0);
182 data.push_back(0);
183 data.push_back(0);
184 219
185 return data; 220 return data;
186} 221}
187 222
188static std::pair<u32, s32> GetAddrInfoRequestImpl(HLERequestContext& ctx) { 223static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
189 struct Parameters { 224 struct Parameters {
190 u8 use_nsd_resolve; 225 u8 use_nsd_resolve;
191 u32 unknown; 226 u32 cancel_handle;
192 u64 process_id; 227 u64 process_id;
193 }; 228 };
194 229
195 IPC::RequestParser rp{ctx}; 230 IPC::RequestParser rp{ctx};
196 const auto parameters = rp.PopRaw<Parameters>(); 231 const auto parameters = rp.PopRaw<Parameters>();
197 232
198 LOG_WARNING(Service, 233 LOG_WARNING(
199 "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", 234 Service,
200 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); 235 "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}",
236 parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id);
237
238 // TODO: If use_nsd_resolve is true, pass the name through NSD::Resolve
239 // before looking up.
201 240
202 const auto host_buffer = ctx.ReadBuffer(0); 241 const auto host_buffer = ctx.ReadBuffer(0);
203 const std::string host = Common::StringFromBuffer(host_buffer); 242 const std::string host = Common::StringFromBuffer(host_buffer);
204 243
205 const auto service_buffer = ctx.ReadBuffer(1); 244 std::optional<std::string> service = std::nullopt;
206 const std::string service = Common::StringFromBuffer(service_buffer); 245 if (ctx.CanReadBuffer(1)) {
207 246 std::span<const u8> service_buffer = ctx.ReadBuffer(1);
208 addrinfo* addrinfo; 247 service = Common::StringFromBuffer(service_buffer);
209 // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now 248 }
210 s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
211 249
212 u32 data_size = 0; 250 // Serialized hints are also passed in a buffer, but are ignored for now.
213 if (result_code == 0 && addrinfo != nullptr) {
214 const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
215 data_size = static_cast<u32>(data.size());
216 freeaddrinfo(addrinfo);
217 251
218 ctx.WriteBuffer(data, 0); 252 auto res = Network::GetAddrInfo(host, service);
253 if (!res.has_value()) {
254 return {0, Translate(res.error())};
219 } 255 }
220 256
221 return std::make_pair(data_size, result_code); 257 std::vector<u8> data = SerializeAddrInfo(res.value(), host);
258 u32 data_size = static_cast<u32>(data.size());
259 ctx.WriteBuffer(data, 0);
260
261 return {data_size, GetAddrInfoError::SUCCESS};
222} 262}
223 263
224void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { 264void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
225 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); 265 auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
226 266
227 IPC::ResponseBuilder rb{ctx, 4}; 267 IPC::ResponseBuilder rb{ctx, 5};
228 rb.Push(ResultSuccess); 268 rb.Push(ResultSuccess);
229 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode 269 rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
230 rb.Push(result_code); // errno 270 rb.Push(static_cast<s32>(emu_gai_err)); // getaddrinfo error code
231 rb.Push(data_size); // serialized size 271 rb.Push(data_size); // serialized size
232} 272}
233 273
234void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) { 274void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
235 // Additional options are ignored 275 // Additional options are ignored
236 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); 276 auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
277
278 IPC::ResponseBuilder rb{ctx, 6};
279 rb.Push(ResultSuccess);
280 rb.Push(data_size); // serialized size
281 rb.Push(static_cast<s32>(emu_gai_err)); // getaddrinfo error code
282 rb.Push(static_cast<s32>(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code
283 rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
284}
285
286void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) {
287 LOG_WARNING(Service, "(STUBBED) called");
288
289 IPC::ResponseBuilder rb{ctx, 3};
237 290
238 IPC::ResponseBuilder rb{ctx, 5};
239 rb.Push(ResultSuccess); 291 rb.Push(ResultSuccess);
240 rb.Push(data_size); // serialized size 292 rb.Push<s32>(0); // bsd errno
241 rb.Push(result_code); // errno
242 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
243 rb.Push(0);
244} 293}
245 294
246} // namespace Service::Sockets 295} // 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:
17 ~SFDNSRES() override; 17 ~SFDNSRES() override;
18 18
19private: 19private:
20 void GetHostByNameRequest(HLERequestContext& ctx);
21 void GetHostByNameRequestWithOptions(HLERequestContext& ctx);
20 void GetAddrInfoRequest(HLERequestContext& ctx); 22 void GetAddrInfoRequest(HLERequestContext& ctx);
21 void GetAddrInfoRequestWithOptions(HLERequestContext& ctx); 23 void GetAddrInfoRequestWithOptions(HLERequestContext& ctx);
24 void ResolverSetOptionRequest(HLERequestContext& ctx);
22}; 25};
23 26
24} // namespace Service::Sockets 27} // 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 {
22 CONNRESET = 104, 22 CONNRESET = 104,
23 NOTCONN = 107, 23 NOTCONN = 107,
24 TIMEDOUT = 110, 24 TIMEDOUT = 110,
25 INPROGRESS = 115,
26};
27
28enum class GetAddrInfoError : s32 {
29 SUCCESS = 0,
30 ADDRFAMILY = 1,
31 AGAIN = 2,
32 BADFLAGS = 3,
33 FAIL = 4,
34 FAMILY = 5,
35 MEMORY = 6,
36 NODATA = 7,
37 NONAME = 8,
38 SERVICE = 9,
39 SOCKTYPE = 10,
40 SYSTEM = 11,
41 BADHINTS = 12,
42 PROTOCOL = 13,
43 OVERFLOW_ = 14, // avoid name collision with Windows macro
44 OTHER = 15,
25}; 45};
26 46
27enum class Domain : u32 { 47enum class Domain : u32 {
48 Unspecified = 0,
28 INET = 2, 49 INET = 2,
29}; 50};
30 51
31enum class Type : u32 { 52enum class Type : u32 {
53 Unspecified = 0,
32 STREAM = 1, 54 STREAM = 1,
33 DGRAM = 2, 55 DGRAM = 2,
34 RAW = 3, 56 RAW = 3,
@@ -36,12 +58,16 @@ enum class Type : u32 {
36}; 58};
37 59
38enum class Protocol : u32 { 60enum class Protocol : u32 {
39 UNSPECIFIED = 0, 61 Unspecified = 0,
40 ICMP = 1, 62 ICMP = 1,
41 TCP = 6, 63 TCP = 6,
42 UDP = 17, 64 UDP = 17,
43}; 65};
44 66
67enum class SocketLevel : u32 {
68 SOCKET = 0xffff, // i.e. SOL_SOCKET
69};
70
45enum class OptName : u32 { 71enum class OptName : u32 {
46 REUSEADDR = 0x4, 72 REUSEADDR = 0x4,
47 KEEPALIVE = 0x8, 73 KEEPALIVE = 0x8,
@@ -51,6 +77,8 @@ enum class OptName : u32 {
51 RCVBUF = 0x1002, 77 RCVBUF = 0x1002,
52 SNDTIMEO = 0x1005, 78 SNDTIMEO = 0x1005,
53 RCVTIMEO = 0x1006, 79 RCVTIMEO = 0x1006,
80 ERROR_ = 0x1007, // avoid name collision with Windows macro
81 NOSIGPIPE = 0x800, // at least according to libnx
54}; 82};
55 83
56enum class ShutdownHow : s32 { 84enum class ShutdownHow : s32 {
@@ -80,6 +108,9 @@ enum class PollEvents : u16 {
80 Err = 1 << 3, 108 Err = 1 << 3,
81 Hup = 1 << 4, 109 Hup = 1 << 4,
82 Nval = 1 << 5, 110 Nval = 1 << 5,
111 RdNorm = 1 << 6,
112 RdBand = 1 << 7,
113 WrBand = 1 << 8,
83}; 114};
84 115
85DECLARE_ENUM_FLAG_OPERATORS(PollEvents); 116DECLARE_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) {
29 return Errno::TIMEDOUT; 29 return Errno::TIMEDOUT;
30 case Network::Errno::CONNRESET: 30 case Network::Errno::CONNRESET:
31 return Errno::CONNRESET; 31 return Errno::CONNRESET;
32 case Network::Errno::INPROGRESS:
33 return Errno::INPROGRESS;
32 default: 34 default:
33 UNIMPLEMENTED_MSG("Unimplemented errno={}", value); 35 UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
34 return Errno::SUCCESS; 36 return Errno::SUCCESS;
@@ -39,8 +41,50 @@ std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
39 return {value.first, Translate(value.second)}; 41 return {value.first, Translate(value.second)};
40} 42}
41 43
44GetAddrInfoError Translate(Network::GetAddrInfoError error) {
45 switch (error) {
46 case Network::GetAddrInfoError::SUCCESS:
47 return GetAddrInfoError::SUCCESS;
48 case Network::GetAddrInfoError::ADDRFAMILY:
49 return GetAddrInfoError::ADDRFAMILY;
50 case Network::GetAddrInfoError::AGAIN:
51 return GetAddrInfoError::AGAIN;
52 case Network::GetAddrInfoError::BADFLAGS:
53 return GetAddrInfoError::BADFLAGS;
54 case Network::GetAddrInfoError::FAIL:
55 return GetAddrInfoError::FAIL;
56 case Network::GetAddrInfoError::FAMILY:
57 return GetAddrInfoError::FAMILY;
58 case Network::GetAddrInfoError::MEMORY:
59 return GetAddrInfoError::MEMORY;
60 case Network::GetAddrInfoError::NODATA:
61 return GetAddrInfoError::NODATA;
62 case Network::GetAddrInfoError::NONAME:
63 return GetAddrInfoError::NONAME;
64 case Network::GetAddrInfoError::SERVICE:
65 return GetAddrInfoError::SERVICE;
66 case Network::GetAddrInfoError::SOCKTYPE:
67 return GetAddrInfoError::SOCKTYPE;
68 case Network::GetAddrInfoError::SYSTEM:
69 return GetAddrInfoError::SYSTEM;
70 case Network::GetAddrInfoError::BADHINTS:
71 return GetAddrInfoError::BADHINTS;
72 case Network::GetAddrInfoError::PROTOCOL:
73 return GetAddrInfoError::PROTOCOL;
74 case Network::GetAddrInfoError::OVERFLOW_:
75 return GetAddrInfoError::OVERFLOW_;
76 case Network::GetAddrInfoError::OTHER:
77 return GetAddrInfoError::OTHER;
78 default:
79 UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
80 return GetAddrInfoError::OTHER;
81 }
82}
83
42Network::Domain Translate(Domain domain) { 84Network::Domain Translate(Domain domain) {
43 switch (domain) { 85 switch (domain) {
86 case Domain::Unspecified:
87 return Network::Domain::Unspecified;
44 case Domain::INET: 88 case Domain::INET:
45 return Network::Domain::INET; 89 return Network::Domain::INET;
46 default: 90 default:
@@ -51,6 +95,8 @@ Network::Domain Translate(Domain domain) {
51 95
52Domain Translate(Network::Domain domain) { 96Domain Translate(Network::Domain domain) {
53 switch (domain) { 97 switch (domain) {
98 case Network::Domain::Unspecified:
99 return Domain::Unspecified;
54 case Network::Domain::INET: 100 case Network::Domain::INET:
55 return Domain::INET; 101 return Domain::INET;
56 default: 102 default:
@@ -61,39 +107,69 @@ Domain Translate(Network::Domain domain) {
61 107
62Network::Type Translate(Type type) { 108Network::Type Translate(Type type) {
63 switch (type) { 109 switch (type) {
110 case Type::Unspecified:
111 return Network::Type::Unspecified;
64 case Type::STREAM: 112 case Type::STREAM:
65 return Network::Type::STREAM; 113 return Network::Type::STREAM;
66 case Type::DGRAM: 114 case Type::DGRAM:
67 return Network::Type::DGRAM; 115 return Network::Type::DGRAM;
116 case Type::RAW:
117 return Network::Type::RAW;
118 case Type::SEQPACKET:
119 return Network::Type::SEQPACKET;
68 default: 120 default:
69 UNIMPLEMENTED_MSG("Unimplemented type={}", type); 121 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
70 return Network::Type{}; 122 return Network::Type{};
71 } 123 }
72} 124}
73 125
74Network::Protocol Translate(Type type, Protocol protocol) { 126Type Translate(Network::Type type) {
127 switch (type) {
128 case Network::Type::Unspecified:
129 return Type::Unspecified;
130 case Network::Type::STREAM:
131 return Type::STREAM;
132 case Network::Type::DGRAM:
133 return Type::DGRAM;
134 case Network::Type::RAW:
135 return Type::RAW;
136 case Network::Type::SEQPACKET:
137 return Type::SEQPACKET;
138 default:
139 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
140 return Type{};
141 }
142}
143
144Network::Protocol Translate(Protocol protocol) {
75 switch (protocol) { 145 switch (protocol) {
76 case Protocol::UNSPECIFIED: 146 case Protocol::Unspecified:
77 LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type"); 147 return Network::Protocol::Unspecified;
78 switch (type) {
79 case Type::DGRAM:
80 return Network::Protocol::UDP;
81 case Type::STREAM:
82 return Network::Protocol::TCP;
83 default:
84 return Network::Protocol::TCP;
85 }
86 case Protocol::TCP: 148 case Protocol::TCP:
87 return Network::Protocol::TCP; 149 return Network::Protocol::TCP;
88 case Protocol::UDP: 150 case Protocol::UDP:
89 return Network::Protocol::UDP; 151 return Network::Protocol::UDP;
90 default: 152 default:
91 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); 153 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
92 return Network::Protocol::TCP; 154 return Network::Protocol::Unspecified;
155 }
156}
157
158Protocol Translate(Network::Protocol protocol) {
159 switch (protocol) {
160 case Network::Protocol::Unspecified:
161 return Protocol::Unspecified;
162 case Network::Protocol::TCP:
163 return Protocol::TCP;
164 case Network::Protocol::UDP:
165 return Protocol::UDP;
166 default:
167 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
168 return Protocol::Unspecified;
93 } 169 }
94} 170}
95 171
96Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { 172Network::PollEvents Translate(PollEvents flags) {
97 Network::PollEvents result{}; 173 Network::PollEvents result{};
98 const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) { 174 const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
99 if (True(flags & from)) { 175 if (True(flags & from)) {
@@ -107,12 +183,15 @@ Network::PollEvents TranslatePollEventsToHost(PollEvents flags) {
107 translate(PollEvents::Err, Network::PollEvents::Err); 183 translate(PollEvents::Err, Network::PollEvents::Err);
108 translate(PollEvents::Hup, Network::PollEvents::Hup); 184 translate(PollEvents::Hup, Network::PollEvents::Hup);
109 translate(PollEvents::Nval, Network::PollEvents::Nval); 185 translate(PollEvents::Nval, Network::PollEvents::Nval);
186 translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
187 translate(PollEvents::RdBand, Network::PollEvents::RdBand);
188 translate(PollEvents::WrBand, Network::PollEvents::WrBand);
110 189
111 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); 190 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
112 return result; 191 return result;
113} 192}
114 193
115PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { 194PollEvents Translate(Network::PollEvents flags) {
116 PollEvents result{}; 195 PollEvents result{};
117 const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) { 196 const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
118 if (True(flags & from)) { 197 if (True(flags & from)) {
@@ -127,13 +206,18 @@ PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) {
127 translate(Network::PollEvents::Err, PollEvents::Err); 206 translate(Network::PollEvents::Err, PollEvents::Err);
128 translate(Network::PollEvents::Hup, PollEvents::Hup); 207 translate(Network::PollEvents::Hup, PollEvents::Hup);
129 translate(Network::PollEvents::Nval, PollEvents::Nval); 208 translate(Network::PollEvents::Nval, PollEvents::Nval);
209 translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
210 translate(Network::PollEvents::RdBand, PollEvents::RdBand);
211 translate(Network::PollEvents::WrBand, PollEvents::WrBand);
130 212
131 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); 213 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
132 return result; 214 return result;
133} 215}
134 216
135Network::SockAddrIn Translate(SockAddrIn value) { 217Network::SockAddrIn Translate(SockAddrIn value) {
136 ASSERT(value.len == 0 || value.len == sizeof(value)); 218 // Note: 6 is incorrect, but can be passed by homebrew (because libnx sets
219 // sin_len to 6 when deserializing getaddrinfo results).
220 ASSERT(value.len == 0 || value.len == sizeof(value) || value.len == 6);
137 221
138 return { 222 return {
139 .family = Translate(static_cast<Domain>(value.family)), 223 .family = Translate(static_cast<Domain>(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);
17/// Translate abstract return value errno pair to guest return value errno pair 17/// Translate abstract return value errno pair to guest return value errno pair
18std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); 18std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
19 19
20/// Translate abstract getaddrinfo error to guest getaddrinfo error
21GetAddrInfoError Translate(Network::GetAddrInfoError value);
22
20/// Translate guest domain to abstract domain 23/// Translate guest domain to abstract domain
21Network::Domain Translate(Domain domain); 24Network::Domain Translate(Domain domain);
22 25
@@ -26,14 +29,20 @@ Domain Translate(Network::Domain domain);
26/// Translate guest type to abstract type 29/// Translate guest type to abstract type
27Network::Type Translate(Type type); 30Network::Type Translate(Type type);
28 31
32/// Translate abstract type to guest type
33Type Translate(Network::Type type);
34
29/// Translate guest protocol to abstract protocol 35/// Translate guest protocol to abstract protocol
30Network::Protocol Translate(Type type, Protocol protocol); 36Network::Protocol Translate(Protocol protocol);
31 37
32/// Translate abstract poll event flags to guest poll event flags 38/// Translate abstract protocol to guest protocol
33Network::PollEvents TranslatePollEventsToHost(PollEvents flags); 39Protocol Translate(Network::Protocol protocol);
34 40
35/// Translate guest poll event flags to abstract poll event flags 41/// Translate guest poll event flags to abstract poll event flags
36PollEvents TranslatePollEventsToGuest(Network::PollEvents flags); 42Network::PollEvents Translate(PollEvents flags);
43
44/// Translate abstract poll event flags to guest poll event flags
45PollEvents Translate(Network::PollEvents flags);
37 46
38/// Translate guest socket address structure to abstract socket address structure 47/// Translate guest socket address structure to abstract socket address structure
39Network::SockAddrIn Translate(SockAddrIn value); 48Network::SockAddrIn Translate(SockAddrIn value);