From 1306d0313641b9126471b61cba982dc39cd8296e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 1 Feb 2021 13:40:23 -0800 Subject: [PATCH] dns.mitm: add GetAddrInfo redir, AtmosphereReloadHostsFile, debug logging control --- config_templates/system_settings.ini | 3 + .../include/stratosphere/socket.hpp | 1 + .../stratosphere/socket/socket_api.hpp | 4 + .../stratosphere/socket/socket_errno.hpp | 48 ++ .../stratosphere/socket/socket_types.hpp | 75 +++ .../source/socket/impl/socket_api.hpp | 3 + .../socket/impl/socket_api.os.horizon.cpp | 10 + .../source/socket/socket_api.cpp | 8 + .../source/dns_mitm/dnsmitm_debug.cpp | 66 ++- .../source/dns_mitm/dnsmitm_debug.hpp | 2 +- .../dns_mitm/dnsmitm_host_redirection.cpp | 13 +- .../dns_mitm/dnsmitm_host_redirection.hpp | 2 +- .../source/dns_mitm/dnsmitm_module.cpp | 8 +- .../source/dns_mitm/dnsmitm_resolver_impl.cpp | 114 ++++- .../source/dns_mitm/dnsmitm_resolver_impl.hpp | 14 +- .../source/dns_mitm/serializer/serializer.hpp | 9 +- .../serializer/serializer_impls_addrinfo.cpp | 444 ++++++++++++++++++ .../serializer_impls_sockaddrin_4.cpp | 141 ++++++ .../serializer_impls_sockaddrin_6.cpp | 133 ++++++ .../ams_mitm/source/dns_mitm/sfdnsres_shim.c | 45 +- .../ams_mitm/source/dns_mitm/sfdnsres_shim.h | 2 + .../source/set_mitm/settings_sd_kvs.cpp | 4 + 22 files changed, 1093 insertions(+), 56 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp create mode 100644 stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_addrinfo.cpp create mode 100644 stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_4.cpp create mode 100644 stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_6.cpp diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index f67210883..3059cb839 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -49,6 +49,9 @@ ; whatever is specified in the user's hosts file. ; 0 = Disabled (use hosts file contents), 1 = Enabled (use defaults and hosts file contents) ; add_defaults_to_dns_hosts = u8!0x1 +; Controls whether dns.mitm logs to the sd card for debugging +; 0 = Disabled, 1 = Enabled +; enable_dns_mitm_debug_log = u8!0x0 [hbloader] ; Controls the size of the homebrew heap when running as applet. ; If set to zero, all available applet memory is used as heap. diff --git a/libraries/libstratosphere/include/stratosphere/socket.hpp b/libraries/libstratosphere/include/stratosphere/socket.hpp index fc9333788..f42d359b9 100644 --- a/libraries/libstratosphere/include/stratosphere/socket.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket.hpp @@ -18,4 +18,5 @@ #include #include #include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp index 4e13549fe..cc99f0aa8 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp @@ -16,9 +16,13 @@ #pragma once #include #include +#include namespace ams::socket { + Errno GetLastError(); + void SetLastError(Errno err); + u32 InetHtonl(u32 host); u16 InetHtons(u16 host); u32 InetNtohl(u32 net); diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp new file mode 100644 index 000000000..faf8b2f9d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::socket { + + enum class Errno : u32 { + ESuccess = 0, + /* ... */ + ENoSpc = 28, + /* ... */ + }; + + enum class HErrno : s32 { + Netdb_Internal = -1, + Netdb_Success = 0, + Host_Not_Found = 1, + Try_Again = 2, + No_Recovery = 3, + No_Data = 4, + + No_Address = No_Data, + }; + + enum class AiErrno : u32 { + EAi_Success = 0, + /* ... */ + }; + + constexpr inline bool operator!(Errno e) { return e == Errno::ESuccess; } + constexpr inline bool operator!(HErrno e) { return e == HErrno::Netdb_Success; } + constexpr inline bool operator!(AiErrno e) { return e == AiErrno::EAi_Success; } + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp index 4a6f1f9cb..c8bbb9ddf 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp @@ -34,6 +34,31 @@ namespace ams::socket { constexpr inline InAddrT InAddr_None = EncodeInAddr<255, 255, 255, 255>; constexpr inline InAddrT InAddr_Loopback = EncodeInAddr<127, 0, 0, 1>; + enum class Protocol : s32 { + IpProto_Ip = 0, + IpProto_Icmp = 1, + + IpProto_Tcp = 6, + + IpProto_Udp = 17, + + IpProto_UdpLite = 136, + + IpProto_Raw = 255, + + IpProto_Max = 256, + }; + + enum class Type : u32 { + Sock_Default = 0, + Sock_Stream = 1, + Sock_Dgram = 2, + Sock_Raw = 3, + Sock_SeqPacket = 5, + + Sock_NonBlock = 0x20000000, + }; + enum class Family : u8 { Af_Unspec = 0, Pf_Unspec = Af_Unspec, @@ -66,4 +91,54 @@ namespace ams::socket { InAddrT s_addr; }; + enum class AddrInfoFlag : u32 { + Ai_None = (0 << 0), + Ai_Passive = (1 << 0), + Ai_CanonName = (1 << 1), + Ai_NumericHost = (1 << 2), + Ai_NumericServ = (1 << 3), + + Ai_AddrConfig = (1 << 10), + }; + + struct SockAddr { + u8 sa_len; + Family sa_family; + char sa_data[14]; + }; + + struct SockAddrIn { + u8 sin_len; + Family sin_family; + InPortT sin_port; + InAddr sin_addr; + u8 sin_zero[8]; + }; + static_assert(sizeof(SockAddr) == sizeof(SockAddrIn)); + + struct AddrInfo { + AddrInfoFlag ai_flags; + Family ai_family; + Type ai_socktype; + Protocol ai_protocol; + SockLenT ai_addrlen; + SockAddr *ai_addr; + char *ai_canonname; + AddrInfo *ai_next; + }; + + #define AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(__ENUM__) \ + constexpr inline __ENUM__ operator | (__ENUM__ lhs, __ENUM__ rhs) { return static_cast<__ENUM__>(static_cast>(lhs) | static_cast>(rhs)); } \ + constexpr inline __ENUM__ operator |=(__ENUM__ &lhs, __ENUM__ rhs) { return lhs = lhs | rhs; } \ + constexpr inline __ENUM__ operator & (__ENUM__ lhs, __ENUM__ rhs) { return static_cast<__ENUM__>(static_cast>(lhs) & static_cast>(rhs)); } \ + constexpr inline __ENUM__ operator &=(__ENUM__ &lhs, __ENUM__ rhs) { return lhs = lhs & rhs; } \ + constexpr inline __ENUM__ operator ^ (__ENUM__ lhs, __ENUM__ rhs) { return static_cast<__ENUM__>(static_cast>(lhs) ^ static_cast>(rhs)); } \ + constexpr inline __ENUM__ operator ^=(__ENUM__ &lhs, __ENUM__ rhs) { return lhs = lhs ^ rhs; } \ + constexpr inline __ENUM__ operator ~ (__ENUM__ e) { return static_cast<__ENUM__>(~static_cast>(e)); } + + AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(Type) + AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(AddrInfoFlag) + + #undef AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS + } diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.hpp b/libraries/libstratosphere/source/socket/impl/socket_api.hpp index f370515ed..292b4bdc9 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.hpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.hpp @@ -17,6 +17,9 @@ namespace ams::socket::impl { + Errno GetLastError(); + void SetLastError(Errno err); + u32 InetHtonl(u32 host); u16 InetHtons(u16 host); u32 InetNtohl(u32 net); diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp index 572130850..ed4fa9873 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp @@ -38,6 +38,16 @@ namespace ams::socket::impl { return ams::Free(ptr); } + Errno GetLastError() { + /* TODO: check that client library is initialized. */ + return static_cast(errno); + } + + void SetLastError(Errno err) { + /* TODO: check that client library is initialized. */ + errno = static_cast(err); + } + u32 InetHtonl(u32 host) { if constexpr (util::IsBigEndian()) { return host; diff --git a/libraries/libstratosphere/source/socket/socket_api.cpp b/libraries/libstratosphere/source/socket/socket_api.cpp index 28f405502..f1171c2c3 100644 --- a/libraries/libstratosphere/source/socket/socket_api.cpp +++ b/libraries/libstratosphere/source/socket/socket_api.cpp @@ -18,6 +18,14 @@ namespace ams::socket { + Errno GetLastError() { + return impl::GetLastError(); + } + + void SetLastError(Errno err) { + return impl::SetLastError(err); + } + u32 InetHtonl(u32 host) { return impl::InetHtonl(host); } diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.cpp index 81d67a9f3..3bc04a8f1 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.cpp @@ -19,28 +19,36 @@ namespace ams::mitm::socket::resolver { - #if 1 - namespace { - ::FsFile g_log_file; - s64 g_log_ofs; + constinit os::SdkMutex g_log_mutex; + constinit bool g_log_enabled; - os::SdkMutex g_log_mutex; + constinit ::FsFile g_log_file; + constinit s64 g_log_ofs; - char g_log_buf[0x400]; + + constinit char g_log_buf[0x400]; } - void InitializeDebug() { - /* Create the log file. */ - mitm::fs::CreateAtmosphereSdFile("dns_log.txt", 0, ams::fs::CreateOption_None); + void InitializeDebug(bool enable_log) { + { + std::scoped_lock lk(g_log_mutex); - /* Open the log file. */ - R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "dns_log.txt", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend)); + g_log_enabled = enable_log; - /* Get the current log offset. */ - R_ABORT_UNLESS(::fsFileGetSize(std::addressof(g_log_file), std::addressof(g_log_ofs))); + if (g_log_enabled) { + /* Create the log file. */ + mitm::fs::CreateAtmosphereSdFile("dns_log.txt", 0, ams::fs::CreateOption_None); + + /* Open the log file. */ + R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "dns_log.txt", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend)); + + /* Get the current log offset. */ + R_ABORT_UNLESS(::fsFileGetSize(std::addressof(g_log_file), std::addressof(g_log_ofs))); + } + } /* Start a new log. */ LogDebug("\n---\n"); @@ -49,28 +57,18 @@ namespace ams::mitm::socket::resolver { void LogDebug(const char *fmt, ...) { std::scoped_lock lk(g_log_mutex); - int len = 0; - { - std::va_list vl; - va_start(vl, fmt); - len = util::VSNPrintf(g_log_buf, sizeof(g_log_buf), fmt, vl); - va_end(vl); + if (g_log_enabled) { + int len = 0; + { + std::va_list vl; + va_start(vl, fmt); + len = util::VSNPrintf(g_log_buf, sizeof(g_log_buf), fmt, vl); + va_end(vl); + } + + R_ABORT_UNLESS(::fsFileWrite(std::addressof(g_log_file), g_log_ofs, g_log_buf, len, FsWriteOption_Flush)); + g_log_ofs += len; } - - R_ABORT_UNLESS(::fsFileWrite(std::addressof(g_log_file), g_log_ofs, g_log_buf, len, FsWriteOption_Flush)); - g_log_ofs += len; } - #else - - void InitializeDebug() { - /* ... */ - } - - void LogDebug(const char *fmt, ...) { - /* ... */ - } - - #endif - } diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.hpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.hpp index ce6848fdf..b1beef8da 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.hpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.hpp @@ -18,7 +18,7 @@ namespace ams::mitm::socket::resolver { - void InitializeDebug(); + void InitializeDebug(bool enable_log); void LogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2))); diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp index deb7ffb20..f4057dddc 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp @@ -264,9 +264,20 @@ namespace ams::mitm::socket::resolver { return "/hosts/default.txt"; } + bool ShouldAddDefaultResolverRedirections() { + u8 en = 0; + if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "add_defaults_to_dns_hosts") == sizeof(en)) { + return (en != 0); + } + return false; + } + } - void InitializeResolverRedirections(bool add_defaults) { + void InitializeResolverRedirections() { + /* Get whether we should add defaults. */ + const bool add_defaults = ShouldAddDefaultResolverRedirections(); + /* Acquire exclusive access to the map. */ std::scoped_lock lk(g_redirection_lock); diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp index 33da75102..d492c50f5 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp @@ -18,7 +18,7 @@ namespace ams::mitm::socket::resolver { - void InitializeResolverRedirections(bool add_defaults); + void InitializeResolverRedirections(); bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname); diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp index 6bf18931d..2aeeb93e6 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp @@ -102,9 +102,9 @@ namespace ams::mitm::socket::resolver { return false; } - bool ShouldAddDefaultResolverRedirections() { + bool ShouldEnableDebugLog() { u8 en = 0; - if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "add_defaults_to_dns_hosts") == sizeof(en)) { + if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm_debug_log") == sizeof(en)) { return (en != 0); } return false; @@ -122,10 +122,10 @@ namespace ams::mitm::socket::resolver { } /* Initialize debug. */ - resolver::InitializeDebug(); + resolver::InitializeDebug(ShouldEnableDebugLog()); /* Initialize redirection map. */ - resolver::InitializeResolverRedirections(ShouldAddDefaultResolverRedirections()); + resolver::InitializeResolverRedirections(); /* Create mitm servers. */ R_ABORT_UNLESS((g_server_manager.RegisterMitmServer(PortIndex_Mitm, DnsMitmServiceName))); diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp index 8ccab4352..457b7073d 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp @@ -39,6 +39,51 @@ namespace ams::mitm::socket::resolver { return result; } + ssize_t SerializeRedirectedAddrInfo(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct addrinfo *hint) { + struct addrinfo ai = { + .ai_flags = 0, + .ai_family = AF_UNSPEC, + .ai_socktype = 0, + .ai_protocol = 0, + .ai_addrlen = 0, + .ai_canonname = nullptr, + .ai_next = nullptr, + }; + + if (hint != nullptr) { + ai = *hint; + } + + switch (ai.ai_family) { + case AF_UNSPEC: ai.ai_family = AF_INET; break; + case AF_INET: ai.ai_family = AF_INET; break; + case AF_INET6: AMS_ABORT_UNLESS("Redirected INET6 not supported"); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + if (ai.ai_socktype == 0) { + ai.ai_socktype = SOCK_STREAM; + } + + if (ai.ai_protocol == 0) { + ai.ai_protocol = IPPROTO_TCP; + } + + const struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_port = ams::socket::InetHtons(redirect_port), + .sin_addr = { .s_addr = redirect_addr }, + .sin_zero = {}, + }; + + ai.ai_addrlen = sizeof(sin); + ai.ai_addr = (struct sockaddr *)(std::addressof(sin)); + + const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ai); + AMS_ABORT_UNLESS(result >= 0); + return result; + } + Result ResolverImpl::GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out out_host_error, sf::Out out_errno, const sf::OutBuffer &out_hostent, sf::Out out_size) { const char *hostname = reinterpret_cast(name.GetPointer()); @@ -58,8 +103,36 @@ namespace ams::mitm::socket::resolver { } Result ResolverImpl::GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out out_errno, sf::Out out_retval, sf::Out out_size) { + const char *hostname = reinterpret_cast(node.GetPointer()); + LogDebug("[%016lx]: GetAddrInfoRequest(%s, %s)\n", this->client_info.program_id.value, reinterpret_cast(node.GetPointer()), reinterpret_cast(srv.GetPointer())); - return sm::mitm::ResultShouldForwardToSession(); + + ams::socket::InAddrT redirect_addr = {}; + R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession()); + + u16 port = 0; + for (const char *cur = reinterpret_cast(srv.GetPointer()); *cur != 0; ++cur) { + AMS_ABORT_UNLESS(std::isdigit(static_cast(*cur))); + port *= 10; + port += *cur - '0'; + } + + LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF); + + const bool use_hint = serialized_hint.GetPointer() != nullptr; + struct addrinfo hint = {}; + if (use_hint) { + AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0); + } + ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } }; + + const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr); + + *out_retval = 0; + *out_errno = 0; + *out_size = size; + + return ResultSuccess(); } Result ResolverImpl::GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno) { @@ -81,8 +154,43 @@ namespace ams::mitm::socket::resolver { } Result ResolverImpl::GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out out_size, sf::Out out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno) { - LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", this->client_info.program_id.value, reinterpret_cast(node.GetPointer()), reinterpret_cast(srv.GetPointer())); - return sm::mitm::ResultShouldForwardToSession(); + const char *hostname = reinterpret_cast(node.GetPointer()); + + LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", this->client_info.program_id.value, hostname, reinterpret_cast(srv.GetPointer())); + + ams::socket::InAddrT redirect_addr = {}; + R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession()); + + u16 port = 0; + for (const char *cur = reinterpret_cast(srv.GetPointer()); *cur != 0; ++cur) { + AMS_ABORT_UNLESS(std::isdigit(static_cast(*cur))); + port *= 10; + port += *cur - '0'; + } + + LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF); + + const bool use_hint = serialized_hint.GetPointer() != nullptr; + struct addrinfo hint; + if (use_hint) { + AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0); + } + ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } }; + + const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr); + + *out_retval = 0; + *out_host_error = 0; + *out_errno = 0; + *out_size = size; + + return ResultSuccess(); + } + + Result ResolverImpl::AtmosphereReloadHostsFile() { + /* Perform a hosts file reload. */ + InitializeResolverRedirections(); + return ResultSuccess(); } } diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.hpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.hpp index 90c472bc3..5b95af8af 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.hpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.hpp @@ -16,11 +16,12 @@ #pragma once #include -#define AMS_DNS_MITM_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostByNameRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out out_host_error, sf::Out out_errno, const sf::OutBuffer &out_hostent, sf::Out out_size), (cancel_handle, client_pid, use_nsd_resolve, name, out_host_error, out_errno, out_hostent, out_size)) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, GetAddrInfoRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out out_errno, sf::Out out_retval, sf::Out out_size), (cancel_handle, client_pid, use_nsd_resolve, node, srv, serialized_hint, out_addrinfo, out_errno, out_retval, out_size)) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, GetHostByNameRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno), (client_pid, name, out_hostent, out_size, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \ - AMS_SF_METHOD_INFO(C, H, 12, Result, GetAddrInfoRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out out_size, sf::Out out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno), (client_pid, node, srv, serialized_hint, out_addrinfo, out_size, out_retval, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) +#define AMS_DNS_MITM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostByNameRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out out_host_error, sf::Out out_errno, const sf::OutBuffer &out_hostent, sf::Out out_size), (cancel_handle, client_pid, use_nsd_resolve, name, out_host_error, out_errno, out_hostent, out_size)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetAddrInfoRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out out_errno, sf::Out out_retval, sf::Out out_size), (cancel_handle, client_pid, use_nsd_resolve, node, srv, serialized_hint, out_addrinfo, out_errno, out_retval, out_size)) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, GetHostByNameRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno), (client_pid, name, out_hostent, out_size, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, GetAddrInfoRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out out_size, sf::Out out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno), (client_pid, node, srv, serialized_hint, out_addrinfo, out_size, out_retval, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \ + AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereReloadHostsFile, (), ()) AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::socket::resolver, IResolver, AMS_DNS_MITM_INTERFACE_INFO) @@ -42,6 +43,9 @@ namespace ams::mitm::socket::resolver { Result GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out out_errno, sf::Out out_retval, sf::Out out_size); Result GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno); Result GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out out_size, sf::Out out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_host_error, sf::Out out_errno); + + /* Extension commands. */ + Result AtmosphereReloadHostsFile(); }; static_assert(IsIResolver); diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp index 848aac6ca..36d329562 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp @@ -20,12 +20,6 @@ namespace ams::mitm::socket::resolver::serializer { class DNSSerializer { public: - template - static ssize_t ToBufferInternal(u8 * const dst, size_t dst_size, const T &in); - - template - static ssize_t FromBufferInternal(T &out, const u8 *src, size_t src_size); - static ssize_t CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id); static u32 InternalHton(const u32 &v); @@ -73,4 +67,7 @@ namespace ams::mitm::socket::resolver::serializer { void FreeHostent(ams::socket::HostEnt &ent); void FreeHostent(struct hostent &ent); + void FreeAddrInfo(ams::socket::AddrInfo &addr_info); + void FreeAddrInfo(struct addrinfo &addr_info); + } diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_addrinfo.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_addrinfo.cpp new file mode 100644 index 000000000..f9f11479d --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_addrinfo.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../dnsmitm_debug.hpp" +#include "../socket_allocator.hpp" +#include "serializer.hpp" + +namespace ams::mitm::socket::resolver::serializer { + + namespace { + + constexpr inline u32 AddrInfoMagic = 0xBEEFCAFE; + + template + concept IsAddrInfo = std::same_as || std::same_as; + + template requires IsAddrInfo + using SockAddrType = typename std::conditional, ams::socket::SockAddr, struct sockaddr>::type; + + template requires IsAddrInfo + using SockAddrInType = typename std::conditional, ams::socket::SockAddrIn, struct sockaddr_in>::type; + + template requires IsAddrInfo + using SockAddrIn6Type = typename std::conditional, struct sockaddr_in6, struct sockaddr_in6>::type; + + template requires IsAddrInfo + constexpr bool IsAfInet(const auto ai_family) { + if constexpr (std::same_as) { + return ai_family == ams::socket::Family::Af_Inet; + } else { + return ai_family == AF_INET; + } + } + + template requires IsAddrInfo + constexpr bool IsAfInet6(const auto ai_family) { + if constexpr (std::same_as) { + return ai_family == ams::socket::Family::Af_Inet6; + } else { + return ai_family == AF_INET; + } + } + + template requires IsAddrInfo + void FreeAddrInfoImpl(T &addr_info) { + T *next = nullptr; + for (T *cur = std::addressof(addr_info); cur != nullptr; cur = next) { + next = cur->ai_next; + + if (cur->ai_addr != nullptr) { + if (IsAfInet(cur->ai_family)) { + ams::socket::impl::Free(reinterpret_cast *>(cur->ai_addr)); + } else if (IsAfInet6(cur->ai_family)) { + ams::socket::impl::Free(reinterpret_cast *>(cur->ai_addr)); + } else { + ams::socket::impl::Free(cur->ai_addr); + } + cur->ai_addr = nullptr; + } + + if (cur->ai_canonname != nullptr) { + ams::socket::impl::Free(cur->ai_canonname); + cur->ai_canonname = nullptr; + } + + if (cur != std::addressof(addr_info)) { + ams::socket::impl::Free(cur); + } + } + } + + template requires IsAddrInfo + size_t AddrInfoSingleSizeOf(const T *addr_info) { + size_t rc = 6 * sizeof(u32); + + if (addr_info->ai_addr == nullptr) { + rc += sizeof(u32); + } else if (IsAfInet(addr_info->ai_family)) { + rc += DNSSerializer::SizeOf(*reinterpret_cast *>(addr_info->ai_addr)); + } else if (IsAfInet6(addr_info->ai_family)) { + rc += DNSSerializer::SizeOf(*reinterpret_cast *>(addr_info->ai_addr)); + } else if (addr_info->ai_addrlen == 0) { + rc += sizeof(u32); + } else { + rc += addr_info->ai_addrlen; + } + + if (addr_info->ai_canonname != nullptr) { + rc += DNSSerializer::SizeOf(static_cast(addr_info->ai_canonname)); + } else { + rc += sizeof(u8); + } + + if (addr_info->ai_next == nullptr) { + rc += sizeof(u32); + } + + return rc; + } + + template requires IsAddrInfo + size_t SizeOfImpl(const T &in) { + size_t rc = 0; + + for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) { + rc += AddrInfoSingleSizeOf(addr_info); + } + + return rc; + } + + template requires IsAddrInfo + ssize_t ToBufferInternalImpl(u8 * const dst, size_t dst_size, const T &addr_info) { + ssize_t rc = -1; + u8 *cur = dst; + + { + const u32 value = AddrInfoMagic; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + { + const u32 value = static_cast(addr_info.ai_flags); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + { + const u32 value = static_cast(addr_info.ai_family); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + { + const u32 value = static_cast(addr_info.ai_socktype); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + { + const u32 value = static_cast(addr_info.ai_protocol); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + { + const u32 value = static_cast(addr_info.ai_addrlen); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + { + if (addr_info.ai_addr == nullptr || addr_info.ai_addrlen == 0) { + const u32 value = 0; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + } else if (IsAfInet(addr_info.ai_family)) { + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast *>(addr_info.ai_addr))) == -1) { + return rc; + } + } else if (IsAfInet6(addr_info.ai_family)) { + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast *>(addr_info.ai_addr))) == -1) { + return rc; + } + } else { + if (dst_size - (cur - dst) < addr_info.ai_addrlen) { + rc = -1; + return rc; + } + + /* NOTE: This is clearly a nintendo bug, see the accompanying note in FromBufferInternalImpl */ + std::memmove(cur, std::addressof(addr_info.ai_addr), addr_info.ai_addrlen); + rc = addr_info.ai_addrlen; + } + cur += rc; + } + + { + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr_info.ai_canonname)) == -1) { + return rc; + } + cur += rc; + } + + if (addr_info.ai_next == nullptr) { + const u32 value = 0; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) { + return rc; + } + cur += rc; + } + + rc = cur - dst; + return rc; + } + + template requires IsAddrInfo + ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) { + ssize_t rc = -1; + u8 *cur = dst; + std::memset(dst, 0, dst_size); + + const size_t required = DNSSerializer::SizeOf(in); + if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) { + return rc; + } + + for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) { + if ((rc = ToBufferInternalImpl(cur, dst_size, *addr_info)) == -1) { + return rc; + } + cur += rc; + } + + rc = cur - dst; + return rc; + } + + template requires IsAddrInfo + ssize_t FromBufferInternalImpl(T &out, const u8 *src, size_t src_size) { + ssize_t rc = -1; + const u8 *cur = src; + + std::memset(std::addressof(out), 0, sizeof(out)); + + ON_SCOPE_EXIT { if (rc < 0) { FreeAddrInfo(out); } }; + + u32 tmp_value; + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } else if (tmp_value != AddrInfoMagic) { + return rc; + } + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.ai_flags = static_cast(tmp_value); + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.ai_family = static_cast(tmp_value); + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.ai_socktype = static_cast(tmp_value); + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.ai_protocol = static_cast(tmp_value); + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.ai_addrlen = static_cast(tmp_value); + cur += rc; + } + + { + if (out.ai_addrlen == 0) { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } + + if (tmp_value != 0) { + rc = -1; + return rc; + } + out.ai_addr = nullptr; + } else if (IsAfInet(out.ai_family)) { + out.ai_addr = static_cast *>(ams::socket::impl::Alloc(sizeof(SockAddrInType))); + if (out.ai_addr == nullptr) { + rc = -1; + return rc; + } + std::memset(out.ai_addr, 0, sizeof(SockAddrInType)); + + if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast *>(out.ai_addr), cur, src_size - (cur - src))) == -1) { + return rc; + } + } else if (IsAfInet6(out.ai_family)) { + out.ai_addr = static_cast *>(ams::socket::impl::Alloc(sizeof(SockAddrIn6Type))); + if (out.ai_addr == nullptr) { + rc = -1; + return rc; + } + std::memset(out.ai_addr, 0, sizeof(SockAddrIn6Type)); + + if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast *>(out.ai_addr), cur, src_size - (cur - src))) == -1) { + return rc; + } + } else { + out.ai_addr = static_cast(ams::socket::impl::Alloc(out.ai_addrlen)); + if (out.ai_addr == nullptr) { + rc = -1; + return rc; + } + + /* NOTE: This is *clearly* a nintendo bug. */ + /* They obviously intend to copy to the buffer they just allocated, but instead they copy to the addrinfo structure itself. */ + /* Probably &out.ai_addr instead of &out.ai_addr[0]? Either way, we'll implement what they do, but... */ + std::memcpy(std::addressof(out.ai_addr), cur, out.ai_addrlen); + rc = out.ai_addrlen; + } + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(out.ai_canonname, cur, src_size - (cur - src))) == -1) { + return rc; + } + cur += rc; + } + + { + if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) { + return rc; + } else if (tmp_value == 0) { + out.ai_next = nullptr; + cur += rc; + } else if (tmp_value == AddrInfoMagic) { + out.ai_next = static_cast(ams::socket::impl::Alloc(sizeof(T))); + if (out.ai_next == nullptr) { + rc = -1; + return rc; + } + + std::memset(out.ai_next, 0, sizeof(T)); + } else { + rc = -1; + return rc; + } + } + + rc = cur - src; + return rc; + } + + template requires IsAddrInfo + ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) { + ssize_t rc = 0; + const u8 *cur = src; + + const size_t required = DNSSerializer::SizeOf(out); + if (src_size < required) { + ams::socket::SetLastError(ams::socket::Errno::ENoSpc); + rc = -1; + return rc; + } + + for (T *addr_info = std::addressof(out); addr_info != nullptr; addr_info = addr_info->ai_next) { + if ((rc = FromBufferInternalImpl(*addr_info, cur, src_size - (cur - src))) == -1) { + rc = -1; + return rc; + } + cur += rc; + } + + rc = cur - src; + return rc; + } + + } + + template<> size_t DNSSerializer::SizeOf(const struct addrinfo &in) { + return SizeOfImpl(in); + } + + template<> size_t DNSSerializer::SizeOf(const ams::socket::AddrInfo &in) { + return SizeOfImpl(in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::AddrInfo &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct addrinfo &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::FromBuffer(ams::socket::AddrInfo &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct addrinfo &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + void FreeAddrInfo(ams::socket::AddrInfo &addr_info) { + return FreeAddrInfoImpl(addr_info); + } + + void FreeAddrInfo(struct addrinfo &addr_info) { + return FreeAddrInfoImpl(addr_info); + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_4.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_4.cpp new file mode 100644 index 000000000..3c4cff1a0 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_4.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../dnsmitm_debug.hpp" +#include "../socket_allocator.hpp" +#include "serializer.hpp" + +namespace ams::mitm::socket::resolver::serializer { + + namespace { + + template + concept IsSockAddrIn = std::same_as || std::same_as; + + template requires IsSockAddrIn + size_t SizeOfImpl(const T &in) { + size_t rc = 0; + rc += sizeof(u16); + rc += sizeof(u16); + rc += DNSSerializer::SizeOf(in.sin_addr); + rc += sizeof(in.sin_zero); + return rc; + } + + template requires IsSockAddrIn + ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) { + ssize_t rc = -1; + u8 *cur = dst; + + if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) { + return rc; + } + + const u16 sin_family = static_cast(in.sin_family); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_family)) == -1) { + return rc; + } + cur += rc; + + const u16 sin_port = static_cast(in.sin_port); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_port)) == -1) { + return rc; + } + cur += rc; + + const u32 s_addr = static_cast(in.sin_addr.s_addr); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), s_addr)) == -1) { + return rc; + } + cur += rc; + + if (dst_size - (cur - dst) < sizeof(in.sin_zero)) { + rc = -1; + return rc; + } + + std::memcpy(cur, in.sin_zero, sizeof(in.sin_zero)); + cur += sizeof(in.sin_zero); + + rc = cur - dst; + return rc; + } + + template requires IsSockAddrIn + ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) { + ssize_t rc = -1; + const u8 *cur = src; + + u16 sin_family; + if ((rc = DNSSerializer::FromBuffer(sin_family, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin_family = static_cast(sin_family); + cur += rc; + + u16 sin_port; + if ((rc = DNSSerializer::FromBuffer(sin_port, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin_port = static_cast(sin_port); + cur += rc; + + u32 s_addr; + if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin_addr.s_addr = static_cast(s_addr); + cur += rc; + + if (src_size - (cur - src) < sizeof(out.sin_zero)) { + rc = -1; + return rc; + } + + std::memcpy(out.sin_zero, cur, sizeof(out.sin_zero)); + cur += sizeof(out.sin_zero); + + rc = cur - src; + return rc; + } + + } + + template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in &in) { + return SizeOfImpl(in); + } + + template<> size_t DNSSerializer::SizeOf(const ams::socket::SockAddrIn &in) { + return SizeOfImpl(in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::SockAddrIn &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::SockAddrIn &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_6.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_6.cpp new file mode 100644 index 000000000..24f7990a0 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_sockaddrin_6.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../dnsmitm_debug.hpp" +#include "../socket_allocator.hpp" +#include "serializer.hpp" + +namespace ams::mitm::socket::resolver::serializer { + + namespace { + + template + concept IsSockAddrIn6 = std::same_as; + + template requires IsSockAddrIn6 + size_t SizeOfImpl(const T &in) { + size_t rc = 0; + rc += sizeof(u16); + rc += sizeof(u16); + rc += sizeof(u32); + rc += DNSSerializer::SizeOf(in.sin6_addr); + rc += sizeof(u32); + return rc; + } + + template requires IsSockAddrIn6 + ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) { + ssize_t rc = -1; + u8 *cur = dst; + + if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) { + return rc; + } + + const u16 sin6_family = static_cast(in.sin6_family); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_family)) == -1) { + return rc; + } + cur += rc; + + const u16 sin6_port = static_cast(in.sin6_port); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_port)) == -1) { + return rc; + } + cur += rc; + + const u32 sin6_flowinfo = static_cast(in.sin6_flowinfo); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_flowinfo)) == -1) { + return rc; + } + cur += rc; + + std::memcpy(cur, std::addressof(in.sin6_addr), sizeof(in.sin6_addr)); + cur += sizeof(in.sin6_addr); + + const u32 sin6_scope_id = static_cast(in.sin6_scope_id); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_scope_id)) == -1) { + return rc; + } + cur += rc; + + rc = cur - dst; + return rc; + } + + template requires IsSockAddrIn6 + ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) { + ssize_t rc = -1; + const u8 *cur = src; + + u16 sin6_family; + if ((rc = DNSSerializer::FromBuffer(sin6_family, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin6_family = static_cast(sin6_family); + cur += rc; + + u16 sin6_port; + if ((rc = DNSSerializer::FromBuffer(sin6_port, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin6_port = static_cast(sin6_port); + cur += rc; + + u32 sin6_flowinfo; + if ((rc = DNSSerializer::FromBuffer(sin6_flowinfo, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin6_flowinfo = static_cast(sin6_flowinfo); + cur += rc; + + std::memcpy(std::addressof(out.sin6_addr), cur, sizeof(out.sin6_addr)); + cur += sizeof(out.sin6_addr); + + u32 sin6_scope_id; + if ((rc = DNSSerializer::FromBuffer(sin6_scope_id, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.sin6_scope_id = static_cast(sin6_scope_id); + cur += rc; + + rc = cur - src; + return rc; + } + + } + + template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in6 &in) { + return SizeOfImpl(in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in6 &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in6 &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c index 0479605f1..dbd9660c2 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c +++ b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c @@ -51,4 +51,47 @@ Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, co } return rc; -} \ No newline at end of file +} + +Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) { + const struct { + u32 options_version; + u32 num_options; + u64 process_id; + } in = { options_version, num_options, process_id }; + struct { + u32 size; + s32 rv; + s32 host_error; + s32 errno; + } out; + + Result rc = serviceMitmDispatchInOut(s, 12, in, out, + .buffer_attrs = { + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out, + SfBufferAttr_HipcAutoSelect | SfBufferAttr_In + }, + .buffers = { + { node, node_size }, + { srv, srv_size }, + { hint, hint_size }, + { out_ai, out_ai_size }, + { option, option_size } + }, + .in_send_pid = true, + .override_pid = process_id, + ); + + if (R_SUCCEEDED(rc)) { + if (out_size) *out_size = out.size; + if (out_rv) *out_rv = out.rv; + if (out_host_error) *out_host_error = out.host_error; + if (out_errno) *out_errno = out.errno; + } + + return rc; +} + diff --git a/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h index 75aa4c763..07f3fa94e 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h +++ b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h @@ -14,6 +14,8 @@ extern "C" { /* Command forwarders. */ Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno); +Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 790b42e36..1ed8312e5 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -358,6 +358,10 @@ namespace ams::settings::fwdbg { /* 0 = Disabled (use hosts file contents), 1 = Enabled (use defaults and hosts file contents) */ R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "add_defaults_to_dns_hosts", "u8!0x1")); + /* Controls whether dns.mitm logs to the sd card for debugging. */ + /* 0 = Disabled, 1 = Enabled */ + R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm_debug_log", "u8!0x0")); + /* Hbloader custom settings. */ /* Controls the size of the homebrew heap when running as applet. */