diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 06444bda7..aa648c200 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -74,6 +74,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/socket.hpp b/libraries/libstratosphere/include/stratosphere/socket.hpp index d43f92384..fc9333788 100644 --- a/libraries/libstratosphere/include/stratosphere/socket.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket.hpp @@ -14,4 +14,8 @@ * along with this program. If not, see . */ #pragma once +#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 new file mode 100644 index 000000000..4e13549fe --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp @@ -0,0 +1,27 @@ +/* + * 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 +#include + +namespace ams::socket { + + u32 InetHtonl(u32 host); + u16 InetHtons(u16 host); + u32 InetNtohl(u32 net); + u16 InetNtohs(u16 net); + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp index bac58080f..4a6f1f9cb 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp @@ -27,7 +27,7 @@ namespace ams::socket { constexpr inline unsigned int FdSetSize = 0x400; template - constexpr inline InAddrT EncodeInAddr = (A << 24) | (B << 16) | (C << 8) | (D << 0); + constexpr inline InAddrT EncodeInAddr = util::ConvertToBigEndian(InAddrT{(A << 24) | (B << 16) | (C << 8) | (D << 0)}); constexpr inline InAddrT InAddr_Any = EncodeInAddr< 0, 0, 0, 0>; constexpr inline InAddrT InAddr_Broadcast = EncodeInAddr<255, 255, 255, 255>; @@ -62,4 +62,8 @@ namespace ams::socket { char **h_addr_list; }; + struct InAddr { + InAddrT s_addr; + }; + } diff --git a/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp b/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp new file mode 100644 index 000000000..d65838676 --- /dev/null +++ b/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp @@ -0,0 +1,25 @@ +/* + * 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::impl { + + void *Alloc(size_t size); + void *Calloc(size_t num, size_t size); + void Free(void *ptr); + +} diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.hpp b/libraries/libstratosphere/source/socket/impl/socket_api.hpp new file mode 100644 index 000000000..f370515ed --- /dev/null +++ b/libraries/libstratosphere/source/socket/impl/socket_api.hpp @@ -0,0 +1,25 @@ +/* + * 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 + +namespace ams::socket::impl { + + u32 InetHtonl(u32 host); + u16 InetHtons(u16 host); + u32 InetNtohl(u32 net); + u16 InetNtohs(u16 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 new file mode 100644 index 000000000..572130850 --- /dev/null +++ b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp @@ -0,0 +1,73 @@ +/* + * 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 "socket_api.hpp" +#include "socket_allocator.hpp" + +namespace ams::socket::impl { + + void *Alloc(size_t size) { + /* TODO: expheap, heap generation. */ + return ams::Malloc(size); + } + + void *Calloc(size_t num, size_t size) { + if (void *ptr = Alloc(size * num); ptr != nullptr) { + std::memset(ptr, 0, size * num); + return ptr; + } else { + return nullptr; + } + } + + void Free(void *ptr) { + /* TODO: expheap, heap generation. */ + return ams::Free(ptr); + } + + u32 InetHtonl(u32 host) { + if constexpr (util::IsBigEndian()) { + return host; + } else { + return util::SwapBytes(host); + } + } + + u16 InetHtons(u16 host) { + if constexpr (util::IsBigEndian()) { + return host; + } else { + return util::SwapBytes(host); + } + } + + u32 InetNtohl(u32 net) { + if constexpr (util::IsBigEndian()) { + return net; + } else { + return util::SwapBytes(net); + } + } + + u16 InetNtohs(u16 net) { + if constexpr (util::IsBigEndian()) { + return net; + } else { + return util::SwapBytes(net); + } + } + +} diff --git a/libraries/libstratosphere/source/socket/socket_api.cpp b/libraries/libstratosphere/source/socket/socket_api.cpp new file mode 100644 index 000000000..28f405502 --- /dev/null +++ b/libraries/libstratosphere/source/socket/socket_api.cpp @@ -0,0 +1,37 @@ +/* + * 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 "impl/socket_api.hpp" + +namespace ams::socket { + + u32 InetHtonl(u32 host) { + return impl::InetHtonl(host); + } + + u16 InetHtons(u16 host) { + return impl::InetHtons(host); + } + + u32 InetNtohl(u32 net) { + return impl::InetNtohl(net); + } + + u16 InetNtohs(u16 net) { + return impl::InetNtohs(net); + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp new file mode 100644 index 000000000..dcfbc5b67 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp @@ -0,0 +1,33 @@ +/* + * 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 "../amsmitm_fs_utils.hpp" +#include "dnsmitm_debug.hpp" +#include "dnsmitm_host_redirection.hpp" + +namespace ams::mitm::socket::resolver { + + bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) { + /* TODO: Real implementation */ + if (std::strcmp(hostname, "receive-lp1.dg.srv.nintendo.net") == 0) { + *out = ams::socket::InAddr_Loopback; + return true; + } + + return false; + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp new file mode 100644 index 000000000..2c339668e --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.hpp @@ -0,0 +1,23 @@ +/* + * 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::mitm::socket::resolver { + + bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname); + +} 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 9ab72463f..8ccab4352 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp @@ -16,12 +16,45 @@ #include #include "dnsmitm_resolver_impl.hpp" #include "dnsmitm_debug.hpp" +#include "dnsmitm_host_redirection.hpp" +#include "serializer/serializer.hpp" +#include "sfdnsres_shim.h" namespace ams::mitm::socket::resolver { + ssize_t SerializeRedirectedHostEnt(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr) { + struct in_addr addr = { .s_addr = redirect_addr }; + struct in_addr *addr_list[2] = { &addr, nullptr }; + + struct hostent ent = { + .h_name = const_cast(hostname), + .h_aliases = nullptr, + .h_addrtype = AF_INET, + .h_length = sizeof(u32), + .h_addr_list = (char **)addr_list, + }; + + const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ent); + 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) { - LogDebug("[%016lx]: GetHostByNameRequest(%s)\n", this->client_info.program_id.value, reinterpret_cast(name.GetPointer())); - return sm::mitm::ResultShouldForwardToSession(); + const char *hostname = reinterpret_cast(name.GetPointer()); + + LogDebug("[%016lx]: GetHostByNameRequest(%s)\n", this->client_info.program_id.value, hostname); + + ams::socket::InAddrT redirect_addr = {}; + R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession()); + + LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF); + const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr); + + *out_host_error = 0; + *out_errno = 0; + *out_size = size; + + return ResultSuccess(); } 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) { @@ -30,8 +63,21 @@ namespace ams::mitm::socket::resolver { } 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) { - LogDebug("[%016lx]: GetHostByNameRequestWithOptions(%s)\n", this->client_info.program_id.value, reinterpret_cast(name.GetPointer())); - return sm::mitm::ResultShouldForwardToSession(); + const char *hostname = reinterpret_cast(name.GetPointer()); + + LogDebug("[%016lx]: GetHostByNameRequestWithOptions(%s)\n", this->client_info.program_id.value, hostname); + + ams::socket::InAddrT redirect_addr = {}; + R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession()); + + LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF); + const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr); + + *out_host_error = 0; + *out_errno = 0; + *out_size = size; + + return ResultSuccess(); } 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) { diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.cpp new file mode 100644 index 000000000..bf6efc567 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.cpp @@ -0,0 +1,49 @@ +/* + * 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 "serializer.hpp" + +namespace ams::mitm::socket::resolver::serializer { + + ssize_t DNSSerializer::CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id) { + if (dst == nullptr) { + return -1; + } else if (dst_size < required) { + return -1; + } + + return 0; + } + + u32 DNSSerializer::InternalHton(const u32 &v) { + return ams::socket::InetHtonl(v); + } + + u16 DNSSerializer::InternalHton(const u16 &v) { + return ams::socket::InetHtons(v); + } + + u32 DNSSerializer::InternalNtoh(const u32 &v) { + return ams::socket::InetNtohl(v); + } + + u16 DNSSerializer::InternalNtoh(const u16 &v) { + return ams::socket::InetNtohs(v); + } + + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp new file mode 100644 index 000000000..848aac6ca --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer.hpp @@ -0,0 +1,76 @@ +/* + * 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::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); + static u16 InternalHton(const u16 &v); + static u32 InternalNtoh(const u32 &v); + static u16 InternalNtoh(const u16 &v); + public: + template + static size_t SizeOf(const T &in); + + template + static size_t SizeOf(const T *in); + + template + static size_t SizeOf(const T *in, size_t count); + + template + static size_t SizeOf(const T **arr, u32 &out_count); + + template + static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T &in); + + template + static ssize_t FromBuffer(T &out, const u8 *src, size_t src_size); + + template + static ssize_t ToBuffer(u8 * const dst, size_t dst_Size, T *in); + + template + static ssize_t ToBuffer(u8 * const dst, size_t dst_size, T **arr); + + template + static ssize_t FromBuffer(T *&out, const u8 *src, size_t src_size); + + template + static ssize_t FromBuffer(T **&out_arr, const u8 *src, size_t src_size); + + template + static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T * const arr, size_t count); + + template + static ssize_t FromBuffer(T * const arr, size_t arr_size, const u8 *src, size_t src_size, size_t count); + }; + + void FreeHostent(ams::socket::HostEnt &ent); + void FreeHostent(struct hostent &ent); + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_hostent.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_hostent.cpp new file mode 100644 index 000000000..b26991173 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_hostent.cpp @@ -0,0 +1,234 @@ +/* + * 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 IsHostEnt = std::same_as || std::same_as; + + template requires IsHostEnt + using InAddrType = typename std::conditional, ams::socket::InAddr, struct in_addr>::type; + + template requires IsHostEnt + constexpr bool IsAfInet(const auto h_addrtype) { + if constexpr (std::same_as) { + return h_addrtype == ams::socket::Family::Af_Inet; + } else { + return h_addrtype == AF_INET; + } + } + + template requires IsHostEnt + constexpr bool IsAfInet6(const auto h_addrtype) { + if constexpr (std::same_as) { + return h_addrtype == ams::socket::Family::Af_Inet6; + } else { + return h_addrtype == AF_INET; + } + } + + template requires IsHostEnt + size_t SizeOfImpl(const T &in) { + size_t rc = 0; + u32 dummy = 0; + + rc += DNSSerializer::SizeOf((const char *)(in.h_name)); + rc += DNSSerializer::SizeOf((const char **)(in.h_aliases), dummy); + rc += sizeof(u32); + rc += sizeof(u32); + rc += DNSSerializer::SizeOf((const InAddrType **)(in.h_addr_list), dummy); + + return rc; + } + + template requires IsHostEnt + ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) { + ssize_t rc = -1; + u8 *cur = dst; + + const size_t required = DNSSerializer::SizeOf(in); + if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) { + return rc; + } + + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_name)) == -1) { + return rc; + } + cur += rc; + + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_aliases)) == -1) { + return rc; + } + cur += rc; + + const u16 h_addrtype = static_cast(in.h_addrtype); + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_addrtype)) == -1) { + return rc; + } + cur += rc; + + const u16 h_length = in.h_length; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_length)) == -1) { + return rc; + } + cur += rc; + + if (IsAfInet(in.h_addrtype)) { + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType **)(in.h_addr_list))) == -1) { + return rc; + } + } else if (IsAfInet6(in.h_addrtype)) { + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType **)(in.h_addr_list))) == -1) { + return rc; + } + } else { + const u32 null = 0; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) { + return rc; + } + } + cur += rc; + + rc = cur - dst; + return rc; + } + + template requires IsHostEnt + ssize_t FromBufferImpl(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) { + FreeHostent(out); + } + }; + + if ((rc = DNSSerializer::FromBuffer(out.h_name, cur, src_size - (cur - src))) == -1) { + return rc; + } + cur += rc; + + if ((rc = DNSSerializer::FromBuffer(out.h_aliases, cur, src_size - (cur - src))) == -1) { + return rc; + } + cur += rc; + + u16 h_addrtype = 0; + if ((rc = DNSSerializer::FromBuffer(h_addrtype, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.h_addrtype = static_cast(h_addrtype); + cur += rc; + + u16 h_length = 0; + if ((rc = DNSSerializer::FromBuffer(h_length, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.h_length = h_length; + cur += rc; + + InAddrType **addrs = nullptr; + if ((rc = DNSSerializer::FromBuffer(addrs, cur, src_size - (cur - src))) == -1) { + return rc; + } + out.h_addr_list = (char **)addrs; + cur += rc; + + rc = cur - src; + return rc; + } + + template requires IsHostEnt + void FreeHostentImpl(T &ent) { + if (ent.h_name != nullptr) { + ams::socket::impl::Free(ent.h_name); + ent.h_name = nullptr; + } + + if (ent.h_aliases != nullptr) { + u32 i = 0; + for (char *str = ent.h_aliases[i]; str != nullptr; str = ent.h_aliases[++i]) { + ams::socket::impl::Free(str); + ent.h_aliases[i] = nullptr; + } + + ams::socket::impl::Free(ent.h_aliases); + ent.h_aliases = nullptr; + } + + if (ent.h_addr_list != nullptr) { + AMS_ASSERT(ent.h_length == sizeof(u32)); + if (ent.h_length == sizeof(u32)) { + auto **addr_list = reinterpret_cast **>(ent.h_addr_list); + + u32 i = 0; + for (auto *addr = addr_list[i]; addr != nullptr; addr = addr_list[++i]) { + ams::socket::impl::Free(addr); + addr_list[i] = nullptr; + } + + ams::socket::impl::Free(ent.h_addr_list); + ent.h_addr_list = nullptr; + } + } + + std::memset(std::addressof(ent), 0, sizeof(ent)); + } + + } + + template<> size_t DNSSerializer::SizeOf(const struct hostent &in) { + return SizeOfImpl(in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct hostent &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct hostent &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + template<> size_t DNSSerializer::SizeOf(const ams::socket::HostEnt &in) { + return SizeOfImpl(in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::HostEnt &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::FromBuffer(ams::socket::HostEnt &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + void FreeHostent(ams::socket::HostEnt &ent) { + return FreeHostentImpl(ent); + } + + void FreeHostent(struct hostent &ent) { + return FreeHostentImpl(ent); + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_in_addr.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_in_addr.cpp new file mode 100644 index 000000000..c990f2aad --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_in_addr.cpp @@ -0,0 +1,237 @@ +/* + * 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 IsInAddr = std::same_as || std::same_as; + + template requires IsInAddr + size_t SizeOfImpl(const T &in) { + return sizeof(u32); + } + + template requires IsInAddr + size_t SizeOfImpl(const T **in, u32 &out_count) { + size_t rc = sizeof(u32); + out_count = 0; + + if (in != nullptr) { + for (const T ** cur_addr = in; *cur_addr != nullptr; ++cur_addr) { + ++out_count; + rc += sizeof(u32); + } + } + + return rc; + } + + template requires IsInAddr + ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) { + ssize_t rc = -1; + u8 *cur = dst; + + const u32 val = DNSSerializer::InternalHton(in.s_addr); + + if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) { + return rc; + } + + std::memcpy(cur, std::addressof(val), sizeof(val)); + rc += sizeof(val); + + return rc; + } + + template requires IsInAddr + ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) { + ssize_t rc = -1; + if (src_size < sizeof(out)) { + return rc; + } + + std::memset(std::addressof(out), 0, sizeof(out)); + out.s_addr = DNSSerializer::InternalNtoh(*reinterpret_cast(src)); + + rc = sizeof(u32); + return rc; + } + + template requires IsInAddr + ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, T **arr) { + ssize_t rc = -1; + u8 *cur = dst; + + if (arr == nullptr && dst_size < sizeof(u32)) { + return rc; + } else if (arr == nullptr) { + const u32 null = 0; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) { + return rc; + } + cur += rc; + } else { + u32 count = 0; + for (auto *tmp = arr; *tmp != nullptr; ++tmp) { + ++count; + } + + if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, (count + 1) * sizeof(**arr), __LINE__)) == -1) { + return rc; + } + + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), count)) == -1) { + return rc; + } + cur += rc; + + rc = 0; + for (auto i = 0; arr[i] != nullptr; ++i) { + const T addr = *arr[i]; + if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr)) == -1) { + return rc; + } + + cur += rc; + } + + rc = cur - dst; + } + + return rc; + } + + template requires IsInAddr + ssize_t FromBufferImpl(T **&out, const u8 *src, size_t src_size) { + ssize_t rc = -1; + const u8 *cur = src; + + out = nullptr; + + ON_SCOPE_EXIT { + if (rc == -1 && out != nullptr) { + for (auto i = 0; out[i] != nullptr; ++i) { + ams::socket::impl::Free(out[i]); + out[i] = nullptr; + } + + ams::socket::impl::Free(out); + out = nullptr; + } + }; + + if (src == nullptr) { + rc = 0; + return rc; + } else if (src_size == 0) { + rc = 0; + return rc; + } + + u32 count = 0; + if ((rc = DNSSerializer::FromBuffer(count, cur, src_size)) == -1) { + return rc; + } + cur += rc; + + if (count == 0) { + return rc; + } + + out = static_cast(ams::socket::impl::Alloc((count + 1) * sizeof(T *))); + if (out == nullptr) { + rc = -1; + return rc; + } + std::memset(out, 0, (count + 1) * sizeof(T *)); + + for (u32 i = 0; i < count; ++i) { + out[i] = static_cast(ams::socket::impl::Alloc(sizeof(T))); + if (out[i] == nullptr) { + rc = -1; + return rc; + } + + u32 s_addr = 0; + if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) { + return rc; + } + out[i]->s_addr = s_addr; + cur += rc; + } + out[count] = nullptr; + + rc = cur - src; + return rc; + } + + } + + template<> size_t DNSSerializer::SizeOf(const struct in_addr &in) { + return SizeOfImpl(in); + } + + template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr &in) { + return SizeOfImpl(in); + } + + template<> size_t DNSSerializer::SizeOf(const struct in_addr **in, u32 &out_count) { + return SizeOfImpl(in, out_count); + } + + template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr **in, u32 &out_count) { + return SizeOfImpl(in, out_count); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct in_addr &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::InAddr &in) { + return ToBufferImpl(dst, dst_size, in); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct in_addr &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr &out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, struct in_addr **arr) { + return ToBufferImpl(dst, dst_size, arr); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, ams::socket::InAddr **arr) { + return ToBufferImpl(dst, dst_size, arr); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct in_addr **&out, const u8 *src, size_t src_size) { + return FromBufferImpl(out, src, src_size); + } + + template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr **&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_ints.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_ints.cpp new file mode 100644 index 000000000..3077f7645 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_ints.cpp @@ -0,0 +1,74 @@ +/* + * 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 "serializer.hpp" + +namespace ams::mitm::socket::resolver::serializer { + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u16 &in) { + /* Convert the value. */ + u8 *cur = dst; + const u16 val = InternalHton(in); + + /* Check arguments. */ + ssize_t rc = -1; + if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u16), __LINE__)) == -1) { + return rc; + } + + std::memcpy(cur, std::addressof(val), sizeof(u16)); + rc += sizeof(u16); + + return rc; + } + + template<> ssize_t DNSSerializer::FromBuffer(u16 &out, const u8 *src, size_t src_size) { + if (src_size < sizeof(u16)) { + return -1; + } + + out = InternalNtoh(*reinterpret_cast(src)); + return sizeof(u16); + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u32 &in) { + /* Convert the value. */ + u8 *cur = dst; + const u32 val = InternalHton(in); + + /* Check arguments. */ + ssize_t rc = -1; + if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) { + return rc; + } + + std::memcpy(cur, std::addressof(val), sizeof(u32)); + rc += sizeof(u32); + + return rc; + } + + template<> ssize_t DNSSerializer::FromBuffer(u32 &out, const u8 *src, size_t src_size) { + if (src_size < sizeof(u32)) { + return -1; + } + + out = InternalNtoh(*reinterpret_cast(src)); + return sizeof(u32); + } + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_string.cpp b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_string.cpp new file mode 100644 index 000000000..ab9f2c889 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/serializer/serializer_impls_string.cpp @@ -0,0 +1,180 @@ +/* + * 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 { + + template<> size_t DNSSerializer::SizeOf(const char *str) { + if (str == nullptr) { + return sizeof(char); + } + return std::strlen(str) + 1; + } + + template<> size_t DNSSerializer::SizeOf(const char **str, u32 &out_count) { + size_t rc = sizeof(u32); + out_count = 0; + + if (str != nullptr) { + for (const char **cur = str; *cur != nullptr; ++cur) { + ++out_count; + rc += SizeOf(*cur); + } + } + + return rc; + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char *str) { + ssize_t rc = -1; + u8 *cur = dst; + + if (str == nullptr && dst_size == 0) { + return -1; + } else if (str == nullptr) { + *cur = '\x00'; + return 1; + } else if ((rc = SizeOf(static_cast(str))) == 0) { + *cur = '\x00'; + return 1; + } else if (CheckToBufferArguments(cur, dst_size, rc + 1, __LINE__) == -1) { + return -1; + } + + std::memmove(cur, str, rc); + return rc; + } + + template<> ssize_t DNSSerializer::FromBuffer(char *&out, const u8 *src, size_t src_size) { + size_t len = 0; + + if (src == nullptr) { + return 0; + } else if (src_size == 0) { + return 0; + } else if (src_size < (len = SizeOf(reinterpret_cast(src)))) { + return 1; + } else if (src[0] == '\x00') { + return 1; + } + + out = static_cast(ams::socket::impl::Alloc(len)); + if (out == nullptr) { + return -1; + } + + std::memmove(out, src, len); + return len; + } + + template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char **str) { + ssize_t rc = -1; + u8 *cur = dst; + u32 count = 0; + + if (dst_size == 0) { + return -1; + } + + const size_t total_size = SizeOf(const_cast(str), count); + AMS_UNUSED(total_size); + + if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) { + return rc; + } else if ((rc = ToBuffer(cur, dst_size, count)) == -1) { + return rc; + } + cur += rc; + dst_size -= rc; + + if (str != nullptr) { + for (char **cur_str = str; *cur_str != nullptr; ++cur_str) { + const auto tmp = ToBuffer(cur, dst_size, *cur_str); + if (tmp == -1) { + return rc; + } + + cur += tmp; + dst_size -= tmp; + rc += tmp; + } + } + + rc = cur - dst; + return rc; + } + + template<> ssize_t DNSSerializer::FromBuffer(char **&out, const u8 *src, size_t src_size) { + ssize_t rc = -1; + const u8 *cur = src; + u32 count = 0; + + out = nullptr; + + ON_SCOPE_EXIT { + if (rc < 0 && out != nullptr) { + u32 i = 0; + for (char *str = *out; str != nullptr; str = out[++i]) { + ams::socket::impl::Free(str); + } + ams::socket::impl::Free(out); + out = nullptr; + } + }; + + if (src == nullptr) { + rc = 0; + return rc; + } else if (src_size == 0) { + rc = 0; + return rc; + } else if ((rc = FromBuffer(count, cur, src_size)) == -1) { + rc = -1; + return rc; + } + cur += rc; + + out = static_cast(ams::socket::impl::Alloc((count + 1) * sizeof(char *))); + if (out == nullptr) { + rc = -1; + return rc; + } + std::memset(out, 0, (count + 1) * sizeof(char *)); + + u32 i; + for (i = 0; i < count; ++i) { + const size_t len = std::strlen(reinterpret_cast(cur)); + out[i] = static_cast(ams::socket::impl::Alloc(len + 1)); + if (out[i] == nullptr) { + rc = -1; + return rc; + } + + std::memmove(out[i], cur, len + 1); + cur += len + 1; + } + + out[i] = nullptr; + rc = cur - src; + + return rc; + } + + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c new file mode 100644 index 000000000..0479605f1 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c @@ -0,0 +1,54 @@ +/* + * 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 "sfdnsres_shim.h" +#include + +/* 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) { + const struct { + u32 options_version; + u32 num_options; + u64 process_id; + } in = { options_version, num_options, process_id }; + struct { + u32 size; + s32 host_error; + s32 errno; + } out; + + Result rc = serviceMitmDispatchInOut(s, 10, in, out, + .buffer_attrs = { + SfBufferAttr_HipcAutoSelect | SfBufferAttr_In, + SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out, + SfBufferAttr_HipcAutoSelect | SfBufferAttr_In + }, + .buffers = { + { name, name_size }, + { out_hostent, out_hostent_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_host_error) *out_host_error = out.host_error; + if (out_errno) *out_errno = out.errno; + } + + return rc; +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h new file mode 100644 index 000000000..75aa4c763 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h @@ -0,0 +1,19 @@ +/** + * @file sfdnsres_shim.h + * @brief IPC wrapper for dns.mitm. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/dns_mitm/socket_allocator.hpp b/stratosphere/ams_mitm/source/dns_mitm/socket_allocator.hpp new file mode 100644 index 000000000..d65838676 --- /dev/null +++ b/stratosphere/ams_mitm/source/dns_mitm/socket_allocator.hpp @@ -0,0 +1,25 @@ +/* + * 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::impl { + + void *Alloc(size_t size); + void *Calloc(size_t num, size_t size); + void Free(void *ptr); + +}