Merge branch 'master' of https://github.com/Atmosphere-NX/Atmosphere into logmanager

This commit is contained in:
XorTroll 2021-02-06 16:17:30 +01:00
commit eee6f92556
63 changed files with 4723 additions and 44 deletions

2
.gitignore vendored
View File

@ -95,3 +95,5 @@ sept/sept-secondary/KEYS.py
**/build_nintendo_nx_arm
**/build_nintendo_nx_x64
**/build_nintendo_nx_x86
stratosphere/test/

View File

@ -42,6 +42,16 @@
; enabled or disabled.
; 0 = Disabled (not debug mode), 1 = Enabled (debug mode)
; enable_am_debug_mode = u8!0x0
; Controls whether dns.mitm is enabled
; 0 = Disabled, 1 = Enabled
; enable_dns_mitm = u8!0x1
; Controls whether dns.mitm uses the default redirections in addition to
; 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.

View File

@ -1,4 +1,19 @@
# Changelog
## 0.18.0
+ A new mitm module was added (`dns.mitm`).
+ This provides a highly configurable mechanism for redirecting DNS resolution requests.
+ By default atmosphère redirects resolution requests for official telemetry servers to a loopback address.
+ Documentation on how to configure `dns.mitm` to meet your more specific needs may be found [here](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/features/dns_mitm.md).
+ The service framework API (`sf`) was refactored to be more accurate to official logic and greatly reduce memory requirements.
+ The comparison of atmosphère module memory usage versus Nintendo's found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons) was updated to reflect this.
+ **Please Note**: If you are a developer using the libstratosphere service APIs, some updating may be required. Contact SciresM#0524 on discord for assistance if required.
+ A number of deprecations were removed, following a general codebase cleanup:
+ The `sm` extension to not unregister services on connection close was superseded by official opt-in logic in 11.0.0, and has been removed in favor of official logic.
+ This should have zero impact on users.
+ The temporary `hid-mitm` added in 0.9.0 has finally been removed, following over a year of deprecation.
+ There shouldn't be any homebrew in use still affected by this, but the situation will be monitored.
+ If this is somehow still a real issue, an unaffiliated hid mitm sysmodule providing the same functionality can be created and released, separate from atmosphère itself.
+ Several issues were fixed, and usability and stability were improved.
## 0.17.1
+ A number of atmosphère's modules were using more memory than their Nintendo equivalent's in 0.17.0; a number of code generatio tweaks have been applied to fix this across the board.
+ A detailed comparison of atmosphère module memory usage versus Nintendo's was made and can be found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons).

View File

@ -25,6 +25,12 @@ set_mitm enables intercepting requests to the system settings service. It curren
+ `ns` system module and games (to allow for overriding game locales)
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
## dns_mitm
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
For documentation, see [here](../../features/dns_mitm.md).
### Firmware Version
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
It modifies the `display_version` field of the returned system version, causing the version to display

53
docs/features/dns_mitm.md Normal file
View File

@ -0,0 +1,53 @@
# DNS.mitm
As of 0.18.0, atmosphère provides a mechanism for redirecting DNS resolution requests.
By default, atmosphère redirects resolution requests for official telemetry servers, redirecting them to a loopback address.
## Hosts files
DNS.mitm can be configured through the usage of a slightly-extended `hosts` file format, which is parsed only once on system startup.
In particular, hosts files parsed by DNS.mitm have the following extensions to the usual format:
+ `*` is treated as a wildcard character, matching any collection of 0 or more characters wherever it occurs in a hostname.
+ `%` is treated as a stand-in for the value of `nsd!environment_identifier`. This is always `lp1`, on production devices.
If multiple entries in a host file match a domain, the last-defined match is used.
Please note that homebrew may trigger a hosts file re-parse by sending the extension IPC command 65000 ("AtmosphereReloadHostsFile") to a connected `sfdnsres` session.
### Hosts file selection
Atmosphère will try to read hosts from the following file paths, in order, stopping once it successfully performs a file read:
+ (emummc only) `/atmosphere/hosts/emummc_%04lx.txt`, formatted with the emummc's id number (see `emummc.ini`).
+ (emummc only) `/atmosphere/hosts/emummc.txt`.
+ (sysmmc only) `/atmosphere/hosts/sysmmc.txt`.
+ `/atmosphere/hosts/default.txt`
If `/atmosphere/hosts/default.txt` does not exist, atmosphère will create it to contain the defaults.
### Atmosphère defaults
By default, atmosphère's default redirections are parsed **in addition to** the contents of the loaded hosts file.
This is equivalent to thinking of the loaded hosts file as having the atmosphère defaults prepended to it.
This setting is considered desirable, because it minimizes the telemetry risks if a user forgets to update a custom hosts file on a system update which changes the telemetry servers.
This behavior can be opted-out from by setting `atmosphere!add_defaults_to_dns_hosts = u8!0x0` in `system_settings.ini`.
The current default redirections are:
```
# Nintendo telemetry servers
127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net
```
## Debugging
On startup (or on hosts file re-parse), DNS.mitm will log both what hosts file it selected and the contents of all redirections it parses to `/atmosphere/logs/dns_mitm_startup.log`.
In addition, if the user sets `atmosphere!enable_dns_mitm_debug_log = u8!0x1` in `system_settings.ini`, DNS.mitm will log all requests to GetHostByName/GetAddrInfo to `/atmosphere/logs/dns_mitm_debug.log`. All redirections will be noted when they occur.
## Opting-out of DNS.mitm entirely
If you wish to disable DNS.mitm entirely, `system_settings.ini` can be edited to set `atmosphere!enable_dns_mitm = u8!0x0`.

View File

@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 6c11c07e2a7f03952a4e70eb89b47bf528de39c6
parent = 9e104bb83f1302e9f126542fbf57c7f666aae953
commit = 17960517bad5d2d07effb28b744ac8d907d571e0
parent = ee2e9d50fd93721b365daf0eae1eef17c8ba62e8
method = merge
cmdver = 0.4.1

View File

@ -45,7 +45,21 @@
namespace ams::kern {
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
namespace cpu {
static constexpr inline size_t NumVirtualCores = BITSIZEOF(u64);
static constexpr inline u64 VirtualCoreMask = [] {
u64 mask = 0;
for (size_t i = 0; i < NumVirtualCores; ++i) {
mask |= (UINT64_C(1) << i);
}
return mask;
}();
}
static_assert(cpu::NumCores <= cpu::NumVirtualCores);
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == cpu::NumVirtualCores);
}

View File

@ -314,12 +314,12 @@ namespace ams::kern::board::nintendo::nx {
g_secure_applet_memory_used = false;
}
u64 GetVersionIdentifier() {
u64 value = kern::GetTargetFirmware();
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 32;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 40;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 48;
value |= static_cast<u64>('M') << 56;
u32 GetVersionIdentifier() {
u32 value = 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 8;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 16;
value |= static_cast<u64>('M') << 24;
return value;
}
@ -584,8 +584,8 @@ namespace ams::kern::board::nintendo::nx {
f_ctx->module_base = KMemoryLayout::GetKernelCodeRegionExtents().GetAddress();
/* Set afsr1. */
f_ctx->afsr0 = 0;
f_ctx->afsr1 = GetVersionIdentifier();
f_ctx->afsr0 = GetVersionIdentifier();
f_ctx->afsr1 = static_cast<u32>(kern::GetTargetFirmware());
/* Set efsr/far. */
f_ctx->far = cpu::GetFarEl1();

View File

@ -22,7 +22,7 @@ namespace ams::kern {
/* Most fields have already been cleared by our constructor. */
/* Initial processes may run on all cores. */
m_core_mask = (1ul << cpu::NumCores) - 1;
m_core_mask = cpu::VirtualCoreMask;
/* Initial processes may use any user priority they like. */
m_priority_mask = ~0xFul;
@ -55,18 +55,17 @@ namespace ams::kern {
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
/* Set core mask. */
for (auto core_id = min_core; core_id <= max_core; core_id++) {
m_core_mask |= (1ul << core_id);
}
MESOSPHERE_ASSERT((m_core_mask & ((1ul << cpu::NumCores) - 1)) == m_core_mask);
MESOSPHERE_ASSERT((m_core_mask & cpu::VirtualCoreMask) == m_core_mask);
/* Set priority mask. */
for (auto prio = min_prio; prio <= max_prio; prio++) {

View File

@ -216,7 +216,7 @@ namespace ams::kern::svc {
case ams::svc::InfoType_ThreadTickCount:
{
/* Verify the requested core is valid. */
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < util::size(cpu::VirtualToPhysicalCoreMap));
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < cpu::NumVirtualCores);
R_UNLESS(core_valid, svc::ResultInvalidCombination());
/* Get the thread from its handle. */

View File

@ -21,8 +21,8 @@ namespace ams::kern::svc {
namespace {
constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
}
void ExitProcess() {
@ -275,7 +275,7 @@ namespace ams::kern::svc {
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
/* Validate the core id. */
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
/* Validate the priority. */

View File

@ -21,8 +21,8 @@ namespace ams::kern::svc {
namespace {
constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
}
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
@ -33,7 +33,7 @@ namespace ams::kern::svc {
}
/* Validate arguments. */
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
@ -168,7 +168,7 @@ namespace ams::kern::svc {
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
/* Validate the core id. */
if (IsValidCoreId(core_id)) {
if (IsValidVirtualCoreId(core_id)) {
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
} else {
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());

View File

@ -63,6 +63,7 @@
#include <stratosphere/ncm.hpp>
#include <stratosphere/nim.hpp>
#include <stratosphere/ns.hpp>
#include <stratosphere/nsd.hpp>
#include <stratosphere/patcher.hpp>
#include <stratosphere/pcv.hpp>
#include <stratosphere/pgl.hpp>
@ -76,6 +77,7 @@
#include <stratosphere/settings.hpp>
#include <stratosphere/sf.hpp>
#include <stratosphere/sm.hpp>
#include <stratosphere/socket.hpp>
#include <stratosphere/spl.hpp>
#include <stratosphere/time.hpp>
#include <stratosphere/updater.hpp>

View File

@ -22,6 +22,9 @@ namespace ams::emummc {
/* Get whether emummc is active. */
bool IsActive();
/* Get the active emummc id. */
u32 GetActiveId();
/* Get Nintendo redirection path. */
const char *GetNintendoDirPath();

View File

@ -113,6 +113,9 @@ namespace ams::impl {
AMS_DEFINE_SYSTEM_THREAD(21, erpt, Main);
AMS_DEFINE_SYSTEM_THREAD(21, erpt, IpcServer);
/* socket. */
AMS_DEFINE_SYSTEM_THREAD(29, socket, ResolverIpcServer);
/* jpegdec. */
AMS_DEFINE_SYSTEM_THREAD(21, jpegdec, Main);

View File

@ -0,0 +1,18 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/nsd/nsd_types.hpp>
#include <stratosphere/nsd/impl/device/nsd_device.hpp>

View File

@ -0,0 +1,24 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/nsd/nsd_types.hpp>
namespace ams::nsd::impl::device {
const EnvironmentIdentifier &GetEnvironmentIdentifierFromSettings();
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::nsd {
struct EnvironmentIdentifier {
static constexpr size_t Size = 8;
char value[Size];
constexpr friend bool operator==(const EnvironmentIdentifier &lhs, const EnvironmentIdentifier &rhs) {
return util::Strncmp(lhs.value, rhs.value, Size) == 0;
}
constexpr friend bool operator!=(const EnvironmentIdentifier &lhs, const EnvironmentIdentifier &rhs) {
return !(lhs == rhs);
}
};
constexpr inline const EnvironmentIdentifier EnvironmentIdentifierOfProductDevice = {"lp1"};
constexpr inline const EnvironmentIdentifier EnvironmentIdentifierOfNotProductDevice = {"dd1"};
}

View File

@ -0,0 +1,22 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stratosphere/socket/socket_types.hpp>
#include <stratosphere/socket/socket_errno.hpp>
#include <stratosphere/socket/socket_api.hpp>

View File

@ -0,0 +1,31 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/socket/socket_types.hpp>
#include <stratosphere/socket/socket_errno.hpp>
namespace ams::socket {
Errno GetLastError();
void SetLastError(Errno err);
u32 InetHtonl(u32 host);
u16 InetHtons(u16 host);
u32 InetNtohl(u32 net);
u16 InetNtohs(u16 net);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
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; }
}

View File

@ -0,0 +1,144 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::socket {
using InAddrT = u32;
using InPortT = u16;
using SockLenT = u32;
using NfdsT = u64;
using FdMask = u64;
constexpr inline unsigned int FdSetSize = 0x400;
template<u32 A, u32 B, u32 C, u32 D>
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>;
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,
Af_Inet = 2,
Pf_Inet = Af_Inet,
Af_Route = 17,
Pf_Route = Af_Route,
Af_Link = 18,
Pf_Link = Af_Link,
Af_Inet6 = 28,
Pf_Inet6 = Af_Inet6,
Af_Max = 42,
Pf_Max = Af_Max
};
struct HostEnt {
char *h_name;
char **h_aliases;
Family h_addrtype;
int h_length;
char **h_addr_list;
};
struct InAddr {
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<std::underlying_type_t<__ENUM__>>(lhs) | static_cast<std::underlying_type_t<__ENUM__>>(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<std::underlying_type_t<__ENUM__>>(lhs) & static_cast<std::underlying_type_t<__ENUM__>>(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<std::underlying_type_t<__ENUM__>>(lhs) ^ static_cast<std::underlying_type_t<__ENUM__>>(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<std::underlying_type_t<__ENUM__>>(e)); }
AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(Type)
AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(AddrInfoFlag)
#undef AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS
}

View File

@ -57,10 +57,10 @@ namespace ams::emummc {
};
/* Globals. */
os::Mutex g_lock(false);
ExosphereConfig g_exo_config;
bool g_is_emummc;
bool g_has_cached;
constinit os::SdkMutex g_lock;
constinit ExosphereConfig g_exo_config;
constinit bool g_is_emummc;
constinit bool g_has_cached;
/* Helpers. */
void CacheValues() {
@ -110,6 +110,12 @@ namespace ams::emummc {
return g_is_emummc;
}
/* Get the active emummc id. */
u32 GetActiveId() {
CacheValues();
return g_exo_config.base_cfg.id;
}
/* Get Nintendo redirection path. */
const char *GetNintendoDirPath() {
CacheValues();

View File

@ -73,11 +73,11 @@ namespace ams {
ams_ctx.sp = ctx->sp.x;
ams_ctx.pc = ctx->pc.x;
ams_ctx.pstate = ctx->pstate;
ams_ctx.afsr0 = ctx->afsr0;
ams_ctx.afsr1 = (static_cast<u64>(::ams::exosphere::GetVersion(ATMOSPHERE_RELEASE_VERSION)) << 32) | static_cast<u64>(hos::GetVersion());
ams_ctx.afsr0 = static_cast<u32>(::ams::exosphere::GetVersion(ATMOSPHERE_RELEASE_VERSION));
if (svc::IsKernelMesosphere()) {
ams_ctx.afsr1 |= (static_cast<u64>('M') << (BITSIZEOF(u64) - BITSIZEOF(u8)));
ams_ctx.afsr0 |= (static_cast<u32>('M') << (BITSIZEOF(u32) - BITSIZEOF(u8)));
}
ams_ctx.afsr1 = static_cast<u32>(hos::GetVersion());
ams_ctx.far = ctx->far.x;
ams_ctx.report_identifier = armGetSystemTick();

View File

@ -40,14 +40,12 @@ namespace ams::hid {
/* Helper. */
void InitializeHid() {
R_ABORT_UNLESS(smInitialize());
ON_SCOPE_EXIT { smExit(); };
{
sm::DoWithSession([&]() {
R_ABORT_UNLESS(hidInitialize());
hidInitializeNpad();
R_ABORT_UNLESS(hidSetSupportedNpadIdType(NpadIdTypes, NumNpadIdTypes));
R_ABORT_UNLESS(hidSetSupportedNpadStyleSet(HidNpadStyleSet_NpadStandard | HidNpadStyleTag_NpadSystemExt));
}
});
}
Result EnsureHidInitialized() {

View File

@ -26,7 +26,7 @@ namespace ams::ncm {
MakeContentPathFunction make_content_path_func;
bool disabled;
protected:
ContentStorageImplBase() { /* ... */ }
ContentStorageImplBase() : make_content_path_func(), disabled(false) { /* ... */ }
protected:
/* Helpers. */
Result EnsureEnabled() const {

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::nsd::impl::device {
namespace {
constexpr const char SettingsName[] = "nsd";
constexpr const char SettingsKey[] = "environment_identifier";
constinit os::SdkMutex g_environment_identifier_lock;
constinit EnvironmentIdentifier g_environment_identifier = {};
constinit bool g_determined_environment_identifier = false;
}
const EnvironmentIdentifier &GetEnvironmentIdentifierFromSettings() {
std::scoped_lock lk(g_environment_identifier_lock);
if (!g_determined_environment_identifier) {
/* Check size. */
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValueSize(SettingsName, SettingsKey) != 0);
/* Get value. */
const size_t size = settings::fwdbg::GetSettingsItemValue(g_environment_identifier.value, EnvironmentIdentifier::Size, SettingsName, SettingsKey);
AMS_ABORT_UNLESS(size < EnvironmentIdentifier::Size);
AMS_ABORT_UNLESS(g_environment_identifier == EnvironmentIdentifierOfProductDevice || g_environment_identifier == EnvironmentIdentifierOfNotProductDevice);
g_determined_environment_identifier = true;
}
return g_environment_identifier;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::socket::impl {
void *Alloc(size_t size);
void *Calloc(size_t num, size_t size);
void Free(void *ptr);
}

View File

@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::socket::impl {
Errno GetLastError();
void SetLastError(Errno err);
u32 InetHtonl(u32 host);
u16 InetHtons(u16 host);
u32 InetNtohl(u32 net);
u16 InetNtohs(u16 net);
}

View File

@ -0,0 +1,83 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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);
}
Errno GetLastError() {
/* TODO: check that client library is initialized. */
return static_cast<Errno>(errno);
}
void SetLastError(Errno err) {
/* TODO: check that client library is initialized. */
errno = static_cast<int>(err);
}
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);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/socket_api.hpp"
namespace ams::socket {
Errno GetLastError() {
return impl::GetLastError();
}
void SetLastError(Errno err) {
return impl::SetLastError(err);
}
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);
}
}

View File

@ -16,8 +16,8 @@
#pragma once
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 17
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

@ -46,6 +46,9 @@
#include <vapours/util/util_format_string.hpp>
#include <vapours/util/util_range.hpp>
#include <vapours/util/util_fixed_map.hpp>
#include <vapours/util/util_fixed_set.hpp>
#ifdef ATMOSPHERE_IS_STRATOSPHERE
#include <vapours/util/util_mutex_utils.hpp>
#endif

View File

@ -0,0 +1,220 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_fourcc.hpp>
namespace ams::util::impl {
class AvailableIndexFinder {
private:
static constexpr int MaxDepthOfBox = 5;
struct BitFlags64 {
private:
u64 m_data;
public:
constexpr ALWAYS_INLINE bool GetFlag(int index) const {
AMS_ASSERT(index < 64);
return ((m_data >> index) & UINT64_C(1)) != 0;
}
constexpr ALWAYS_INLINE void SetFlag(int index) {
AMS_ASSERT(index < 64);
m_data |= (UINT64_C(1) << index);
}
constexpr ALWAYS_INLINE void ClearFlag(int index) {
AMS_ASSERT(index < 64);
m_data &= ~(UINT64_C(1) << index);
}
constexpr ALWAYS_INLINE bool IsFull() const {
return m_data == ~(UINT64_C(0));
}
constexpr ALWAYS_INLINE int FindIndexOfBitZero() const {
AMS_ASSERT(!this->IsFull());
return __builtin_ctzll(~m_data);
}
};
private:
int *m_p_current_index;
int *m_p_map_index;
void *m_buffer;
int m_depth;
BitFlags64 *m_flags;
private:
static constexpr int Pow64(int e) {
switch (e) {
case 0: return 0x1;
case 1: return 0x40;
case 2: return 0x1000;
case 3: return 0x40000;
case 4: return 0x1000000;
case 5: return 0x40000000;
default: return -1;
}
}
static constexpr u64 Roundup64(u64 value) {
return (value + (64 - 1)) / 64;
}
static constexpr size_t GetRequiredNumOfBox(int depth, size_t num_elements) {
if (depth == 1) {
return Roundup64(num_elements);
} else if (depth == 2) {
return Roundup64(num_elements) + Pow64(0);
} else if (depth == 3) {
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2);
} else if (depth == 4) {
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3);
} else if (depth == 5) {
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3) + Pow64(4);
} else {
return 0;
}
}
static constexpr int CalcOffset(int *arr, int depth) {
int offset = 0;
for (auto i = 0; i < depth; ++i) {
offset += Pow64(i);
}
for (auto i = 0; i < depth; ++i) {
offset += Pow64(i) - arr[depth - 1 - i];
}
return offset;
}
public:
static consteval int GetSignature() {
return static_cast<int>(util::ReverseFourCC<'B', 'I', 'T', 'S'>::Code);
}
static constexpr int GetNeedDepth(size_t num_elements) {
if (num_elements <= 0x40) {
return 1;
} else if (num_elements <= 0x1000) {
return 2;
} else if (num_elements <= 0x40000) {
return 3;
} else if (num_elements <= 0x1000000) {
return 4;
} else if (num_elements <= 0x40000000) {
return 5;
} else {
return -1;
}
}
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
return sizeof(BitFlags64) * GetRequiredNumOfBox(GetNeedDepth(num_elements), num_elements);
}
public:
void Initialize(int *cur, int *map, u8 *buf) {
const size_t num_elements = static_cast<size_t>(*map);
AMS_ASSERT(num_elements <= static_cast<size_t>(Pow64(MaxDepthOfBox - 1)));
/* Set fields. */
m_p_current_index = cur;
m_p_map_index = map;
m_buffer = buf;
m_depth = GetNeedDepth(num_elements);
/* Validate fields. */
AMS_ASSERT(m_depth > 0);
/* Setup memory. */
std::memset(m_buffer, 0, GetRequiredMemorySize(num_elements));
m_flags = reinterpret_cast<BitFlags64 *>(m_buffer);
}
int AcquireIndex() {
/* Validate pre-conditions. */
AMS_ASSERT(*m_p_current_index < *m_p_map_index);
/* Build up arrays. */
int table[MaxDepthOfBox];
BitFlags64 *pos[MaxDepthOfBox];
for (auto i = 0; i < m_depth; ++i) {
/* Determine the position. */
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
/* Set table entry. */
table[i] = pos[i]->FindIndexOfBitZero();
AMS_ASSERT(table[i] != BITSIZEOF(u64));
}
/* Validate that the index is not acquired. */
AMS_ASSERT(!pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
/* Acquire the index. */
pos[m_depth - 1]->SetFlag(table[m_depth - 1]);
/* Validate that the index was acquired. */
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
/* Update tracking flags. */
for (auto i = m_depth - 1; i > 0; --i) {
if (pos[i]->IsFull()) {
pos[i - 1]->SetFlag(table[i - 1]);
}
}
/* Calculate the index we acquired. */
int index = 0, pow = 0;
for (auto i = m_depth; i > 0; --i, ++pow) {
index += Pow64(pow) * table[i - 1];
}
/* Increment current index. */
++(*m_p_current_index);
return index;
}
void ReleaseIndex(int index) {
/* Convert index to table. */
int table[MaxDepthOfBox];
for (auto i = 0; i < m_depth; ++i) {
table[m_depth - 1 - i] = index % BITSIZEOF(u64);
index /= BITSIZEOF(u64);
}
/* Build up arrays. */
BitFlags64 *pos[MaxDepthOfBox];
for (auto i = 0; i < m_depth; ++i) {
/* Determine the position. */
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
}
/* Validate that the flag is set. */
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
/* Clear the flags. */
for (auto i = m_depth - 1; i >= 0; --i) {
pos[i]->ClearFlag(table[i]);
}
/* Decrement current index. */
--(*m_p_current_index);
}
};
}

View File

@ -0,0 +1,69 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_fixed_tree.hpp>
namespace ams::util {
template<typename Key, typename Value, typename Compare = std::less<Key>, size_t BufferAlignment = 8>
class FixedMap {
private:
using KeyValuePair = std::pair<Key const, Value>;
struct LessTypeForMap {
constexpr ALWAYS_INLINE bool operator()(const KeyValuePair &lhs, const KeyValuePair &rhs) const {
return Compare{}(lhs.first, rhs.first);
}
};
using TreeType = ::ams::util::FixedTree<KeyValuePair, LessTypeForMap, KeyValuePair, BufferAlignment>;
using iterator = typename TreeType::iterator;
using const_iterator = typename TreeType::const_iterator;
public:
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
return TreeType::GetRequiredMemorySize(num_elements);
}
private:
TreeType m_tree;
public:
FixedMap() : m_tree() { /* ... */ }
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
return m_tree.Initialize(num_elements, buffer, buffer_size);
}
ALWAYS_INLINE iterator begin() { return m_tree.begin(); }
ALWAYS_INLINE const_iterator begin() const { return m_tree.begin(); }
ALWAYS_INLINE iterator end() { return m_tree.end(); }
ALWAYS_INLINE const_iterator end() const { return m_tree.end(); }
ALWAYS_INLINE bool erase(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.erase(pair); }
ALWAYS_INLINE iterator find(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
ALWAYS_INLINE const_iterator find(const Key &key) const { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
ALWAYS_INLINE std::pair<iterator, bool> insert(const KeyValuePair &pair) { return m_tree.insert(pair); }
ALWAYS_INLINE size_t size() const { return m_tree.size(); }
ALWAYS_INLINE void clear() { return m_tree.clear(); }
};
}

View File

@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_fixed_tree.hpp>
namespace ams::util {
template<typename Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
/* ... */
};
}

View File

@ -0,0 +1,998 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/impl/util_available_index_finder.hpp>
#include <vapours/util/util_alignment.hpp>
namespace ams::util {
template<typename Member, typename Compare, typename IteratorMember, size_t BufferAlignment = 8> requires std::convertible_to<Member &, IteratorMember &>
class FixedTree {
private:
class IteratorBase;
friend class IteratorBase;
private:
enum class Color : u8 {
Red = 0,
Black = 1,
};
static constexpr inline int Index_Nil = -1;
static constexpr inline int Index_Leaf = -2;
static constexpr inline int Index_BeforeBegin = -3;
static constexpr inline int Index_AfterEnd = -4;
static constexpr inline size_t max_size = 0x40000000;
struct Header {
/* "Nintendo Red-Black tree" */
static constexpr u32 Signature = util::ReverseFourCC<'N','N','R','B'>::Code;
u32 header_size;
u32 header_signature;
u32 _08;
s32 max_elements;
s32 cur_elements;
s32 root_index;
s32 left_most_index;
s32 right_most_index;
s32 index_signature;
u32 buffer_size;
u32 node_size;
u32 element_size;
u32 _30;
u32 _34;
u32 _38;
u32 _3C;
u32 _40;
u32 _44;
u32 _48;
u32 _4C;
void InitializeHeader(u32 _08, s32 max_e, s32 cur_e, u32 ind_sig, u32 buf_sz, u32 node_sz, u32 e_sz, u32 _30, u32 _34, u32 _38, u32 _3C, u32 _40, u32 _44) {
this->header_size = sizeof(Header);
this->header_signature = Signature;
this->_08 = _08;
this->max_elements = max_e;
this->cur_elements = cur_e;
this->root_index = Index_Nil;
this->left_most_index = Index_Nil;
this->right_most_index = Index_Nil;
this->index_signature = ind_sig;
this->buffer_size = buf_sz;
this->node_size = node_sz;
this->element_size = e_sz;
this->_30 = _30;
this->_34 = _34;
this->_38 = _38;
this->_3C = _3C;
this->_40 = _40;
this->_44 = _44;
this->_48 = 0;
this->_4C = 0;
}
};
static_assert(sizeof(Header) == 0x50);
struct IndexPair {
int first;
int last;
};
struct Node {
Member m_data;
int m_parent;
int m_right;
int m_left;
Color m_color;
void SetLeft(int l, Node *n, int p) {
m_left = l;
n->m_parent = p;
}
void SetRight(int r, Node *n, int p) {
m_right = r;
n->m_parent = p;
}
};
class Iterator;
class ConstIterator;
class IteratorBase {
private:
friend class ConstIterator;
private:
const FixedTree *m_this;
int m_index;
protected:
constexpr ALWAYS_INLINE IteratorBase(const FixedTree *tree, int index) : m_this(tree), m_index(index) { /* ... */ }
constexpr bool IsEqualImpl(const IteratorBase &rhs) const {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
/* Check for tree equality. */
if (m_this != rhs.m_this) {
return false;
}
/* Check for nil. */
if (m_this->IsNil(m_index) && m_this->IsNil(rhs.m_index)) {
return true;
}
/* Check for index equality. */
return m_index == rhs.m_index;
}
constexpr IteratorMember &DereferenceImpl() const {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
if (!m_this->IsNil(m_index)) {
return m_this->m_nodes[m_index].m_data;
} else {
AMS_ASSERT(false);
return m_this->GetNode(std::numeric_limits<int>::max())->m_data;
}
}
constexpr ALWAYS_INLINE IteratorBase &IncrementImpl() {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
this->OperateIndex(true);
return *this;
}
constexpr ALWAYS_INLINE IteratorBase &DecrementImpl() {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
this->OperateIndex(false);
return *this;
}
constexpr void OperateIndex(bool increment) {
if (increment) {
/* We're incrementing. */
if (m_index == Index_BeforeBegin) {
m_index = 0;
} else {
m_index = m_this->UncheckedPP(m_index);
if (m_this->IsNil(m_index)) {
m_index = Index_AfterEnd;
}
}
} else {
/* We're decrementing. */
if (m_index == Index_AfterEnd) {
m_index = static_cast<int>(m_this->size()) - 1;
} else {
m_index = m_this->UncheckedMM(m_index);
if (m_this->IsNil(m_index)) {
m_index = Index_BeforeBegin;
}
}
}
}
};
class Iterator : public IteratorBase {
public:
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
constexpr ALWAYS_INLINE Iterator(const Iterator &rhs) = default;
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
return this->IsEqualImpl(rhs);
}
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE IteratorMember &operator*() const {
return static_cast<IteratorMember &>(this->DereferenceImpl());
}
constexpr ALWAYS_INLINE IteratorMember *operator->() const {
return std::addressof(this->operator *());
}
constexpr ALWAYS_INLINE Iterator &operator++() {
return static_cast<Iterator &>(this->IncrementImpl());
}
constexpr ALWAYS_INLINE Iterator &operator--() {
return static_cast<Iterator &>(this->DecrementImpl());
}
};
class ConstIterator : public IteratorBase {
public:
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
constexpr ALWAYS_INLINE ConstIterator(const ConstIterator &rhs) = default;
constexpr ALWAYS_INLINE ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_this, rhs.m_index) { /* ... */ }
constexpr ALWAYS_INLINE bool operator==(const ConstIterator &rhs) const {
return this->IsEqualImpl(rhs);
}
constexpr ALWAYS_INLINE bool operator!=(const ConstIterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE const IteratorMember &operator*() const {
return static_cast<const IteratorMember &>(this->DereferenceImpl());
}
constexpr ALWAYS_INLINE const IteratorMember *operator->() const {
return std::addressof(this->operator *());
}
constexpr ALWAYS_INLINE ConstIterator &operator++() {
return static_cast<ConstIterator &>(this->IncrementImpl());
}
constexpr ALWAYS_INLINE ConstIterator &operator--() {
return static_cast<ConstIterator &>(this->DecrementImpl());
}
};
public:
using iterator = Iterator;
using const_iterator = ConstIterator;
private:
impl::AvailableIndexFinder m_index_finder;
Node m_dummy_leaf;
Node *m_p_dummy_leaf;
u8 *m_buffer;
Header *m_header;
Node *m_nodes;
iterator m_end_iterator;
public:
FixedTree() : m_end_iterator(*this, Index_Nil) {
this->SetDummyMemory();
}
protected:
void InitializeImpl(int num_elements, void *buffer, size_t buffer_size) {
/* Check pre-conditions. */
AMS_ASSERT(num_elements > 0);
AMS_ASSERT(static_cast<size_t>(num_elements) <= max_size);
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), BufferAlignment));
AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements));
/* Set buffer. */
m_buffer = static_cast<u8 *>(buffer);
m_header = reinterpret_cast<Header *>(m_buffer);
/* Setup memory. */
this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
/* Check that buffer was set up correctly. */
AMS_ASSERT(static_cast<u32>(buffer_size) == m_header->buffer_size);
/* Setup dummy leaf. */
this->SetDummyMemory();
}
public:
static constexpr size_t SizeOfNodes(size_t num_elements) {
return util::AlignUp(sizeof(Node) * num_elements, BufferAlignment);
}
static constexpr size_t SizeOfIndex(size_t num_elements) {
return impl::AvailableIndexFinder::GetRequiredMemorySize(num_elements);
}
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
return sizeof(Header) + SizeOfNodes(num_elements) + SizeOfIndex(num_elements);
}
private:
void SetDummyMemory() {
m_dummy_leaf.m_color = Color::Black;
m_dummy_leaf.m_parent = Index_Nil;
m_dummy_leaf.m_left = Index_Leaf;
m_dummy_leaf.m_right = Index_Leaf;
m_p_dummy_leaf = std::addressof(m_dummy_leaf);
}
void InitializeMemory(int num_elements, u32 buffer_size, u32 signature) {
/* Initialize the header. */
m_header->InitializeHeader(1, num_elements, 0, signature, buffer_size, sizeof(Node), sizeof(Member), 4, 4, 4, 4, 4, BufferAlignment);
/* Setup index finder. */
m_index_finder.Initialize(std::addressof(m_header->cur_elements), std::addressof(m_header->max_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements));
/* Set nodes array. */
m_nodes = reinterpret_cast<Node *>(m_buffer + sizeof(*m_header));
}
Node *GetNode(int index) const {
if (index >= 0) {
return m_nodes + index;
} else {
return m_p_dummy_leaf;
}
}
constexpr ALWAYS_INLINE bool IsNil(int index) const {
return index < 0;
}
constexpr ALWAYS_INLINE bool IsLeaf(int index) const {
return index == Index_Leaf;
}
int GetRoot() const { return m_header->root_index; }
void SetRoot(int index) {
if (index == Index_Leaf) {
index = Index_Nil;
}
m_header->root_index = index;
}
int GetLMost() const { return m_header->left_most_index; }
void SetLMost(int index) { m_header->left_most_index = index; }
int GetRMost() const { return m_header->right_most_index; }
void SetRMost(int index) { m_header->right_most_index = index; }
int GetParent(int index) const {
return this->GetNode(index)->m_parent;
}
int AcquireIndex() { return m_index_finder.AcquireIndex(); }
void ReleaseIndex(int index) { return m_index_finder.ReleaseIndex(index); }
int EraseByIndex(int target_index) {
/* Setup tracking variables. */
const auto next_index = this->UncheckedPP(target_index);
auto *target_node = this->GetNode(target_index);
auto a_index = Index_Leaf;
auto *a_node = this->GetNode(a_index);
auto b_index = Index_Leaf;
auto *b_node = this->GetNode(b_index);
auto cur_index = target_index;
auto *cur_node = this->GetNode(cur_index);
if (cur_node->m_left == Index_Leaf) {
a_index = cur_node->m_right;
a_node = this->GetNode(a_index);
m_p_dummy_leaf->m_parent = cur_index;
} else {
if (cur_node->m_right == Index_Leaf) {
a_index = cur_node->m_left;
} else {
cur_index = next_index;
cur_node = this->GetNode(cur_index);
a_index = cur_node->m_right;
}
a_node = this->GetNode(a_index);
m_p_dummy_leaf->m_parent = cur_index;
}
/* Ensure the a node is updated (redundant) */
a_node = this->GetNode(a_index);
/* Update relevant metrics/links. */
if (cur_index == target_index) {
/* No left, but has right. */
b_index = target_node->m_parent;
b_node = this->GetNode(b_index);
if (a_index != Index_Leaf) {
a_node->m_parent = b_index;
}
if (this->GetRoot() == target_index) {
this->SetRoot(a_index);
} else if (b_node->m_left == target_index) {
b_node->m_left = a_index;
} else {
b_node->m_right = a_index;
}
if (this->GetLMost() == target_index) {
this->SetLMost((a_index != Index_Leaf) ? this->FindMinInSubtree(a_index) : b_index);
}
if (this->GetRMost() == target_index) {
this->SetRMost((a_index != Index_Leaf) ? this->FindMaxInSubtree(a_index) : b_index);
}
} else {
/* Has left or doesn't have right. */
/* Fix left links. */
this->GetNode(target_node->m_left)->m_parent = cur_index;
cur_node->m_left = target_node->m_left;
if (cur_index == target_node->m_right) {
b_index = cur_index;
b_node = this->GetNode(b_index);
} else {
b_index = cur_node->m_parent;
b_node = this->GetNode(b_index);
if (!this->IsNil(a_index)) {
a_node->m_parent = b_index;
}
b_node->m_left = a_index;
cur_node->m_right = target_node->m_right;
this->GetNode(target_node->m_right)->m_parent = cur_index;
}
if (this->GetRoot() == target_index) {
this->SetRoot(cur_index);
} else {
if (this->GetNode(target_node->m_parent)->m_left == target_index) {
this->GetNode(target_node->m_parent)->m_left = cur_index;
} else {
this->GetNode(target_node->m_parent)->m_right = cur_index;
}
}
cur_node->m_parent = target_node->m_parent;
std::swap(cur_node->m_color, target_node->m_color);
}
/* Ensure the tree remains balanced. */
if (target_node->m_color == Color::Black) {
while (true) {
if (a_index == this->GetRoot() || a_node->m_color != Color::Black) {
break;
}
if (a_index == b_node->m_left) {
cur_index = b_node->m_right;
cur_node = this->GetNode(cur_index);
if (cur_node->m_color == Color::Red) {
cur_node->m_color = Color::Black;
b_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateLeft(b_index);
cur_index = b_node->m_right;
cur_node = this->GetNode(cur_index);
}
if (this->IsNil(cur_index)) {
a_index = b_index;
a_node = b_node;
} else {
if (this->GetNode(cur_node->m_left)->m_color != Color::Black || this->GetNode(cur_node->m_right)->m_color != Color::Black) {
if (this->GetNode(cur_node->m_right)->m_color == Color::Black) {
this->GetNode(cur_node->m_left)->m_color = Color::Black;
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateRight(cur_index);
cur_index = b_node->m_right;
cur_node = this->GetNode(cur_index);
}
cur_node->m_color = b_node->m_color;
b_node->m_color = Color::Black;
this->GetNode(cur_node->m_right)->m_color = Color::Black;
this->RotateLeft(b_index);
break;
}
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
a_index = b_index;
a_node = b_node;
}
} else {
cur_index = b_node->m_left;
cur_node = this->GetNode(cur_index);
if (cur_node->m_color == Color::Red) {
cur_node->m_color = Color::Black;
b_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateRight(b_index);
cur_index = b_node->m_left;
cur_node = this->GetNode(cur_index);
}
if (this->IsNil(cur_index)) {
a_index = b_index;
a_node = b_node;
} else {
if (this->GetNode(cur_node->m_right)->m_color != Color::Black || this->GetNode(cur_node->m_left)->m_color != Color::Black) {
if (this->GetNode(cur_node->m_left)->m_color == Color::Black) {
this->GetNode(cur_node->m_right)->m_color = Color::Black;
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateLeft(cur_index);
cur_index = b_node->m_left;
cur_node = this->GetNode(cur_index);
}
cur_node->m_color = b_node->m_color;
b_node->m_color = Color::Black;
this->GetNode(cur_node->m_left)->m_color = Color::Black;
this->RotateRight(b_index);
break;
}
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
a_index = b_index;
a_node = b_node;
}
}
b_index = a_node->m_parent;
b_node = this->GetNode(b_index);
}
a_node->m_color = Color::Black;
}
/* Release the index. */
this->ReleaseIndex(target_index);
return target_index;
}
int FindIndex(const Member &elem) const {
return this->FindIndexSub(this->GetRoot(), elem);
}
int FindIndexSub(int index, const Member &elem) const {
if (index != Index_Nil) {
auto *node = this->GetNode(index);
if (Compare{}(elem, node->m_data)) {
if (!this->IsLeaf(node->m_left)) {
return this->FindIndexSub(node->m_left, elem);
}
} else {
if (!Compare{}(node->m_data, elem)) {
return index;
}
if (!this->IsLeaf(node->m_right)) {
return this->FindIndexSub(node->m_right, elem);
}
}
}
return Index_Nil;
}
int FindMaxInSubtree(int index) const {
int max = index;
for (auto *node = this->GetNode(index); !this->IsNil(node->m_right); node = this->GetNode(node->m_right)) {
max = node->m_right;
}
return max;
}
int FindMinInSubtree(int index) const {
int min = index;
for (auto *node = this->GetNode(index); !this->IsNil(node->m_left); node = this->GetNode(node->m_left)) {
min = node->m_left;
}
return min;
}
int InsertAt(bool before, int parent, const Member &elem) {
/* Get an index for the new element. */
const auto index = this->AcquireIndex();
/* Create the node. */
auto *node = this->GetNode(index);
node->m_color = Color::Red;
node->m_parent = parent;
node->m_right = Index_Leaf;
node->m_left = Index_Leaf;
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
/* Fix up the parent node. */
auto *parent_node = this->GetNode(parent);
if (before) {
parent_node->m_left = index;
if (parent == this->GetLMost()) {
this->SetLMost(index);
}
} else {
parent_node->m_right = index;
if (parent == this->GetRMost()) {
this->SetRMost(index);
}
}
/* Ensure the tree is balanced. */
int cur_index = index;
while (true) {
auto *cur_node = this->GetNode(cur_index);
if (this->GetNode(cur_node->m_parent)->m_color != Color::Red) {
break;
}
auto *p_node = this->GetNode(cur_node->m_parent);
auto *g_node = this->GetNode(p_node->m_parent);
if (cur_node->m_parent == g_node->m_left) {
if (auto *gr_node = this->GetNode(g_node->m_right); gr_node->m_color == Color::Red) {
p_node->m_color = Color::Black;
gr_node->m_color = Color::Black;
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
cur_index = p_node->m_parent;
continue;
}
if (cur_index == p_node->m_right) {
cur_index = cur_node->m_parent;
cur_node = this->GetNode(cur_index);
this->RotateLeft(cur_index);
}
p_node = this->GetNode(cur_node->m_parent);
p_node->m_color = Color::Black;
g_node = this->GetNode(p_node->m_parent);
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
this->RotateRight(p_node->m_parent);
} else {
if (auto *gl_node = this->GetNode(g_node->m_left); gl_node->m_color == Color::Red) {
p_node->m_color = Color::Black;
gl_node->m_color = Color::Black;
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
cur_index = p_node->m_parent;
continue;
}
if (cur_index == p_node->m_left) {
cur_index = cur_node->m_parent;
cur_node = this->GetNode(cur_index);
this->RotateRight(cur_index);
}
p_node = this->GetNode(cur_node->m_parent);
p_node->m_color = Color::Black;
g_node = this->GetNode(p_node->m_parent);
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
this->RotateLeft(p_node->m_parent);
}
}
/* Set root color. */
this->GetNode(this->GetRoot())->m_color = Color::Black;
return index;
}
int InsertNoHint(bool before, const Member &elem) {
int cur_index = this->GetRoot();
int prev_index = Index_Nil;
bool less = true;
while (cur_index != Index_Nil && cur_index != Index_Leaf) {
auto *node = this->GetNode(cur_index);
prev_index = cur_index;
if (before) {
less = Compare{}(node->m_data, elem);
} else {
less = Compare{}(elem, node->m_data);
}
if (less) {
cur_index = node->m_left;
} else {
cur_index = node->m_right;
}
}
if (cur_index == Index_Nil) {
/* Create a new node. */
const auto index = this->AcquireIndex();
auto *node = this->GetNode(index);
node->m_color = Color::Black;
node->m_parent = Index_Nil;
node->m_right = Index_Leaf;
node->m_left = Index_Leaf;
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
this->SetRoot(index);
this->SetLMost(index);
this->SetRMost(index);
return index;
} else {
auto *compare_node = this->GetNode(prev_index);
if (less) {
if (prev_index == this->GetLMost()) {
return this->InsertAt(less, prev_index, elem);
} else {
compare_node = this->GetNode(this->UncheckedMM(prev_index));
}
}
if (Compare{}(compare_node->m_data, elem)) {
return this->InsertAt(less, prev_index, elem);
} else {
return Index_Nil;
}
}
}
void RotateLeft(int index) {
/* Determine indices. */
const auto p_index = this->GetParent(index);
const auto r_index = this->GetNode(index)->m_right;
const auto l_index = this->GetNode(index)->m_left;
const auto rl_index = this->GetNode(r_index)->m_left;
const auto rr_index = this->GetNode(r_index)->m_right;
/* Get nodes. */
auto *node = this->GetNode(index);
auto *p_node = this->GetNode(p_index);
auto *r_node = this->GetNode(r_index);
auto *l_node = this->GetNode(l_index);
auto *rl_node = this->GetNode(rl_index);
auto *rr_node = this->GetNode(rr_index);
/* Perform the rotation. */
if (p_index == Index_Nil) {
r_node->m_parent = Index_Nil;
m_header->root_index = r_index;
} else if (p_node->m_left == index) {
p_node->SetLeft(r_index, r_node, p_index);
} else {
p_node->SetRight(r_index, r_node, p_index);
}
r_node->SetLeft(index, node, r_index);
r_node->SetRight(rr_index, rr_node, r_index);
node->SetLeft(l_index, l_node, index);
node->SetRight(rl_index, rl_node, index);
}
void RotateRight(int index) {
/* Determine indices. */
const auto p_index = this->GetParent(index);
const auto l_index = this->GetNode(index)->m_left;
const auto ll_index = this->GetNode(l_index)->m_left;
const auto lr_index = this->GetNode(l_index)->m_right;
const auto r_index = this->GetNode(index)->m_right;
/* Get nodes. */
auto *node = this->GetNode(index);
auto *p_node = this->GetNode(p_index);
auto *l_node = this->GetNode(l_index);
auto *ll_node = this->GetNode(ll_index);
auto *lr_node = this->GetNode(lr_index);
auto *r_node = this->GetNode(r_index);
/* Perform the rotation. */
if (p_index == Index_Nil) {
l_node->m_parent = Index_Nil;
m_header->root_index = l_index;
} else if (p_node->m_left == index) {
p_node->SetLeft(l_index, l_node, p_index);
} else {
p_node->SetRight(l_index, l_node, p_index);
}
l_node->SetLeft(ll_index, ll_node, l_index);
l_node->SetRight(index, node, l_index);
node->SetLeft(lr_index, lr_node, index);
node->SetRight(r_index, r_node, index);
}
int UncheckedMM(int index) const {
auto *node = this->GetNode(index);
if (this->IsNil(index)) {
index = this->GetRMost();
node = this->GetNode(index);
} else if (this->IsNil(node->m_left)) {
int parent = node->m_parent;
Node *p;
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_left; p = this->GetNode(parent)) {
index = parent;
node = p;
parent = p->m_parent;
}
if (!this->IsNil(index)) {
index = parent;
node = p;
}
} else {
index = this->FindMaxInSubtree(node->m_left);
node = this->GetNode(index);
}
if (this->IsNil(index)) {
return Index_Leaf;
} else {
return index;
}
}
int UncheckedPP(int index) const {
auto *node = this->GetNode(index);
if (!this->IsNil(index)) {
if (this->IsNil(node->m_right)) {
int parent = node->m_parent;
Node *p;
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_right; p = this->GetNode(parent)) {
index = parent;
node = p;
parent = p->m_parent;
}
index = parent;
node = p;
} else {
index = this->FindMinInSubtree(node->m_right);
node = this->GetNode(index);
}
}
if (this->IsNil(index)) {
return Index_Leaf;
} else {
return index;
}
}
public:
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
AMS_ASSERT(num_elements <= max_size);
return this->InitializeImpl(static_cast<int>(num_elements), buffer, buffer_size);
}
iterator begin() { return iterator(*this); }
const_iterator begin() const { return const_iterator(*this); }
iterator end() { return m_end_iterator; }
const_iterator end() const { return m_end_iterator; }
size_t size() const { return m_header->cur_elements; }
void clear() {
const auto num_elements = m_header->max_elements;
const auto buffer_size = m_header->buffer_size;
AMS_ASSERT(buffer_size == static_cast<u32>(GetRequiredMemorySize(num_elements)));
return this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
}
bool erase(const Member &elem) {
const auto range = this->equal_range(elem);
if (range.first != range.last) {
this->EraseByIndex(range.first);
return true;
} else {
return false;
}
}
iterator find(const Member &elem) {
if (const auto index = this->FindIndex(elem); index >= 0) {
return iterator(*this, index);
} else {
return this->end();
}
}
const_iterator find(const Member &elem) const {
if (const auto index = this->FindIndex(elem); index >= 0) {
return const_iterator(*this, index);
} else {
return this->end();
}
}
std::pair<iterator, bool> insert(const Member &elem) {
const auto index = this->InsertNoHint(false, elem);
const auto it = iterator(*this, index);
return std::make_pair(it, !this->IsNil(index));
}
IndexPair equal_range(const Member &elem) {
/* Get node to start iteration. */
auto cur_index = this->GetRoot();
auto cur_node = this->GetNode(cur_index);
auto min_index = Index_Leaf;
auto min_node = this->GetNode(min_index);
auto max_index = Index_Leaf;
auto max_node = this->GetNode(max_index);
/* Iterate until current is leaf, to find min/max. */
while (cur_index != Index_Leaf) {
if (Compare{}(cur_node->m_data, elem)) {
cur_index = cur_node->m_right;
cur_node = this->GetNode(cur_index);
} else {
if (max_index == Index_Leaf && Compare{}(elem, cur_node->m_data)) {
max_index = cur_index;
max_node = this->GetNode(max_index);
}
min_index = cur_index;
min_node = this->GetNode(min_index);
cur_index = cur_node->m_left;
cur_node = this->GetNode(cur_index);
}
}
/* Iterate again, to find correct range extent for max. */
cur_index = (max_index == Index_Leaf) ? this->GetRoot() : max_node->m_left;
cur_node = this->GetNode(cur_index);
while (cur_index != Index_Leaf) {
if (Compare{}(elem, cur_node->m_data)) {
max_index = cur_index;
max_node = cur_node;
cur_index = cur_node->m_left;
} else {
cur_index = cur_node->m_right;
}
cur_node = this->GetNode(cur_index);
}
AMS_UNUSED(min_node);
return IndexPair{min_index, max_index};
}
};
}

View File

@ -13,8 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>

View File

@ -31,7 +31,7 @@ namespace ams::util {
}
template<typename T>
int Strncmp(const T *lhs, const T *rhs, int count) {
constexpr int Strncmp(const T *lhs, const T *rhs, int count) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
AMS_ABORT_UNLESS(count >= 0);
@ -50,7 +50,7 @@ namespace ams::util {
}
template<typename T>
int Strnicmp(const T *lhs, const T *rhs, int count) {
constexpr int Strnicmp(const T *lhs, const T *rhs, int count) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
AMS_ABORT_UNLESS(count >= 0);

View File

@ -47,6 +47,36 @@ namespace ams::mitm::fs {
return fsFsCreateFile(&g_sd_filesystem, path, size, option);
}
Result DeleteSdFile(const char *path) {
R_TRY(EnsureSdInitialized());
return fsFsDeleteFile(&g_sd_filesystem, path);
}
bool HasSdFile(const char *path) {
if (R_FAILED(EnsureSdInitialized())) {
return false;
}
FsDirEntryType type;
if (R_FAILED(fsFsGetEntryType(&g_sd_filesystem, path, &type))) {
return false;
}
return type == FsDirEntryType_File;
}
bool HasAtmosphereSdFile(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
return HasSdFile(fixed_path);
}
Result DeleteAtmosphereSdFile(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
return DeleteSdFile(fixed_path);
}
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);

View File

@ -22,6 +22,7 @@ namespace ams::mitm::fs {
void OpenGlobalSdCardFileSystem();
/* Utilities. */
Result DeleteAtmosphereSdFile(const char *path);
Result CreateSdFile(const char *path, s64 size, s32 option);
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option);
Result OpenSdFile(FsFile *out, const char *path, u32 mode);
@ -30,6 +31,9 @@ namespace ams::mitm::fs {
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
bool HasSdFile(const char *path);
bool HasAtmosphereSdFile(const char *path);
Result CreateSdDirectory(const char *path);
Result CreateAtmosphereSdDirectory(const char *path);
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode);

View File

@ -22,6 +22,7 @@
#include "bpc_mitm/bpcmitm_module.hpp"
#include "bpc_mitm/bpc_ams_module.hpp"
#include "ns_mitm/nsmitm_module.hpp"
#include "dns_mitm/dnsmitm_module.hpp"
#include "sysupdater/sysupdater_module.hpp"
namespace ams::mitm {
@ -34,6 +35,7 @@ namespace ams::mitm {
ModuleId_BpcMitm,
ModuleId_BpcAms,
ModuleId_NsMitm,
ModuleId_DnsMitm,
ModuleId_Sysupdater,
ModuleId_Count,
@ -66,6 +68,7 @@ namespace ams::mitm {
GetModuleDefinition<bpc::MitmModule>(),
GetModuleDefinition<bpc_ams::MitmModule>(),
GetModuleDefinition<ns::MitmModule>(),
GetModuleDefinition<socket::resolver::MitmModule>(),
GetModuleDefinition<sysupdater::MitmModule>(),
};

View File

@ -0,0 +1,77 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../amsmitm_fs_utils.hpp"
#include "dnsmitm_debug.hpp"
namespace ams::mitm::socket::resolver {
namespace {
constinit os::SdkMutex g_log_mutex;
constinit bool g_log_enabled;
constinit ::FsFile g_log_file;
constinit s64 g_log_ofs;
constinit char g_log_buf[0x400];
}
void InitializeDebug(bool enable_log) {
{
std::scoped_lock lk(g_log_mutex);
g_log_enabled = enable_log;
if (g_log_enabled) {
/* Create the logs directory. */
mitm::fs::CreateAtmosphereSdDirectory("/logs");
/* Create the log file. */
mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_debug.log", 0, ams::fs::CreateOption_None);
/* Open the log file. */
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "/logs/dns_mitm_debug.log", 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");
}
void LogDebug(const char *fmt, ...) {
std::scoped_lock lk(g_log_mutex);
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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::socket::resolver {
void InitializeDebug(bool enable_log);
void LogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
}

View File

@ -0,0 +1,399 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../amsmitm_fs_utils.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_host_redirection.hpp"
#include "socket_allocator.hpp"
namespace ams::mitm::socket::resolver {
namespace {
/* https://github.com/clibs/wildcardcmp */
constexpr int wildcardcmp(const char *pattern, const char *string) {
const char *w = nullptr; /* last `*` */
const char *s = nullptr; /* last checked char */
/* malformed */
if (!pattern || !string) return 0;
/* loop 1 char at a time */
while (1) {
if (!*string) {
if (!*pattern) return 1;
if ('*' == *pattern) return 1;
if (!*s) return 0;
string = s++;
pattern = w;
continue;
} else {
if (*pattern != *string) {
if ('*' == *pattern) {
w = ++pattern;
s = string;
/* "*" -> "foobar" */
if (*pattern) continue;
return 1;
} else if (w) {
string++;
/* "*ooba*" -> "foobar" */
continue;
}
return 0;
}
}
string++;
pattern++;
}
return 1;
}
constexpr const char DefaultHostsFile[] =
"# Nintendo telemetry servers\n"
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
constinit os::SdkMutex g_redirection_lock;
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
void RemoveRedirection(const char *hostname) {
for (auto it = g_redirection_list.begin(); it != g_redirection_list.end(); ++it) {
if (std::strcmp(it->first.c_str(), hostname) == 0) {
g_redirection_list.erase(it);
break;
}
}
}
void AddRedirection(const char *hostname, ams::socket::InAddrT addr) {
RemoveRedirection(hostname);
g_redirection_list.emplace(g_redirection_list.begin(), std::string(hostname), addr);
}
constinit char g_specific_emummc_hosts_path[0x40] = {};
void ParseHostsFile(const char *file_data) {
/* Get the environment identifier from settings. */
const auto env = ams::nsd::impl::device::GetEnvironmentIdentifierFromSettings();
const auto env_len = std::strlen(env.value);
/* Parse the file. */
enum class State {
IgnoredLine,
BeginLine,
Ip1,
IpDot1,
Ip2,
IpDot2,
Ip3,
IpDot3,
Ip4,
WhiteSpace,
HostName,
};
ams::socket::InAddrT current_address;
char current_hostname[0x200];
u32 work;
State state = State::BeginLine;
for (const char *cur = file_data; *cur != '\x00'; ++cur) {
const char c = *cur;
switch (state) {
case State::IgnoredLine:
if (c == '\n') {
state = State::BeginLine;
}
break;
case State::BeginLine:
if (std::isdigit(static_cast<unsigned char>(c))) {
current_address = 0;
work = static_cast<u32>(c - '0');
state = State::Ip1;
} else if (c == '\n') {
state = State::BeginLine;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip1:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 0;
work = 0;
state = State::IpDot1;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot1:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip2;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip2:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 8;
work = 0;
state = State::IpDot2;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot2:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip3;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip3:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 16;
work = 0;
state = State::IpDot3;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot3:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip4;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip4:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == ' ' || c == '\t') {
current_address |= (work & 0xFF) << 24;
work = 0;
state = State::WhiteSpace;
} else {
state = State::IgnoredLine;
}
break;
case State::WhiteSpace:
if (c == '\n') {
state = State::BeginLine;
} else if (c != ' ' && c != '\r' && c != '\t') {
if (c == '%') {
std::memcpy(current_hostname, env.value, env_len);
work = env_len;
} else {
current_hostname[0] = c;
work = 1;
}
state = State::HostName;
}
break;
case State::HostName:
if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
AddRedirection(current_hostname, current_address);
work = 0;
if (c == '\n') {
state = State::BeginLine;
} else {
state = State::WhiteSpace;
}
} else if (c == '%') {
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - env_len);
std::memcpy(current_hostname + work, env.value, env_len);
work += env_len;
} else {
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - 1);
current_hostname[work++] = c;
}
}
}
if (state == State::HostName) {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
AddRedirection(current_hostname, current_address);
}
}
void Log(::FsFile &f, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void Log(::FsFile &f, const char *fmt, ...) {
char log_buf[0x100];
int len = 0;
{
std::va_list vl;
va_start(vl, fmt);
len = util::VSNPrintf(log_buf, sizeof(log_buf), fmt, vl);
va_end(vl);
}
s64 ofs;
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(f), std::addressof(ofs)));
R_ABORT_UNLESS(::fsFileWrite(std::addressof(f), ofs, log_buf, len, FsWriteOption_Flush));
}
const char *SelectHostsFile(::FsFile &log_file) {
Log(log_file, "Selecting hosts file...\n");
const bool is_emummc = emummc::IsActive();
const u32 emummc_id = emummc::GetActiveId();
util::SNPrintf(g_specific_emummc_hosts_path, sizeof(g_specific_emummc_hosts_path), "/hosts/emummc_%04x.txt", emummc_id);
if (is_emummc) {
if (mitm::fs::HasAtmosphereSdFile(g_specific_emummc_hosts_path)) {
return g_specific_emummc_hosts_path;
}
Log(log_file, "Skipping %s because it does not exist...\n", g_specific_emummc_hosts_path);
if (mitm::fs::HasAtmosphereSdFile("/hosts/emummc.txt")) {
return "/hosts/emummc.txt";
}
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/emummc.txt");
} else {
if (mitm::fs::HasAtmosphereSdFile("/hosts/sysmmc.txt")) {
return "/hosts/sysmmc.txt";
}
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/sysmmc.txt");
}
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() {
/* Get whether we should add defaults. */
const bool add_defaults = ShouldAddDefaultResolverRedirections();
/* Acquire exclusive access to the map. */
std::scoped_lock lk(g_redirection_lock);
/* Clear the redirections map. */
g_redirection_list.clear();
/* Open log file. */
::FsFile log_file;
mitm::fs::DeleteAtmosphereSdFile("/logs/dns_mitm_startup.log");
mitm::fs::CreateAtmosphereSdDirectory("/logs");
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_startup.log", 0, ams::fs::CreateOption_None));
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(log_file), "/logs/dns_mitm_startup.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(log_file)); };
Log(log_file, "DNS Mitm:\n");
/* If a default hosts file doesn't exist on the sd card, create one. */
if (!mitm::fs::HasAtmosphereSdFile("/hosts/default.txt")) {
Log(log_file, "Creating /hosts/default.txt because it does not exist.\n");
mitm::fs::CreateAtmosphereSdDirectory("/hosts");
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/hosts/default.txt", sizeof(DefaultHostsFile) - 1, ams::fs::CreateOption_None));
::FsFile default_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(default_file), "/hosts/default.txt", ams::fs::OpenMode_ReadWrite));
R_ABORT_UNLESS(::fsFileWrite(std::addressof(default_file), 0, DefaultHostsFile, sizeof(DefaultHostsFile) - 1, ::FsWriteOption_Flush));
::fsFileClose(std::addressof(default_file));
}
/* If we should, add the defaults. */
if (add_defaults) {
Log(log_file, "Adding defaults to redirection list.\n");
ParseHostsFile(DefaultHostsFile);
}
/* Select the hosts file. */
const char *hosts_path = SelectHostsFile(log_file);
Log(log_file, "Selected %s\n", hosts_path);
/* Load the hosts file. */
{
char *hosts_file_data = nullptr;
ON_SCOPE_EXIT { if (hosts_file_data != nullptr) { ams::Free(hosts_file_data); } };
{
::FsFile hosts_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(hosts_file), hosts_path, ams::fs::OpenMode_Read));
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(hosts_file)); };
/* Get the hosts file size. */
s64 hosts_size;
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(hosts_file), std::addressof(hosts_size)));
/* Validate we can read the file. */
AMS_ABORT_UNLESS(0 <= hosts_size && hosts_size < 0x8000);
/* Read the data. */
hosts_file_data = static_cast<char *>(ams::Malloc(0x8000));
AMS_ABORT_UNLESS(hosts_file_data != nullptr);
u64 br;
R_ABORT_UNLESS(::fsFileRead(std::addressof(hosts_file), 0, hosts_file_data, hosts_size, ::FsReadOption_None, std::addressof(br)));
AMS_ABORT_UNLESS(br == static_cast<u64>(hosts_size));
/* Null-terminate. */
hosts_file_data[hosts_size] = '\x00';
}
/* Parse the hosts file. */
ParseHostsFile(hosts_file_data);
}
/* Print the redirections. */
Log(log_file, "Redirections:\n");
for (const auto &[host, address] : g_redirection_list) {
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
}
}
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
std::scoped_lock lk(g_redirection_lock);
for (const auto &[host, address] : g_redirection_list) {
if (wildcardcmp(host.c_str(), hostname)) {
*out = address;
return true;
}
}
return false;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::socket::resolver {
void InitializeResolverRedirections();
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname);
}

View File

@ -0,0 +1,137 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../amsmitm_initialization.hpp"
#include "dnsmitm_module.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_resolver_impl.hpp"
#include "dnsmitm_host_redirection.hpp"
namespace ams::mitm::socket::resolver {
namespace {
enum PortIndex {
PortIndex_Mitm,
PortIndex_Count,
};
constexpr sm::ServiceName DnsMitmServiceName = sm::ServiceName::Encode("sfdnsres");
constexpr size_t MaxSessions = 30;
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_Mitm:
return this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IResolver, ResolverImpl>(decltype(fsrv)(fsrv), client_info), fsrv);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constexpr size_t TotalThreads = 8;
static_assert(TotalThreads >= 1, "TotalThreads");
constexpr size_t NumExtraThreads = TotalThreads - 1;
constexpr size_t ThreadStackSize = mitm::ModuleTraits<socket::resolver::MitmModule>::StackSize;
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
os::ThreadType g_extra_threads[NumExtraThreads];
void LoopServerThread(void *arg) {
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
void ProcessForServerOnAllThreads() {
/* Initialize threads. */
if constexpr (NumExtraThreads > 0) {
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
for (size_t i = 0; i < NumExtraThreads; i++) {
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
}
}
/* Start extra threads. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::StartThread(g_extra_threads + i);
}
}
/* Loop this thread. */
LoopServerThread(nullptr);
/* Wait for extra threads to finish. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::WaitThread(g_extra_threads + i);
}
}
}
bool ShouldMitmDns() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm") == sizeof(en)) {
return (en != 0);
}
return false;
}
bool ShouldEnableDebugLog() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm_debug_log") == sizeof(en)) {
return (en != 0);
}
return false;
}
}
void MitmModule::ThreadFunction(void *arg) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* If we shouldn't mitm dns, don't do anything at all. */
if (!ShouldMitmDns()) {
return;
}
/* Initialize debug. */
resolver::InitializeDebug(ShouldEnableDebugLog());
/* Initialize redirection map. */
resolver::InitializeResolverRedirections();
/* Create mitm servers. */
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<ResolverImpl>(PortIndex_Mitm, DnsMitmServiceName)));
/* Loop forever, servicing our services. */
ProcessForServerOnAllThreads();
}
}

View File

@ -0,0 +1,24 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../amsmitm_module.hpp"
namespace ams::mitm::socket::resolver {
DEFINE_MITM_MODULE_CLASS(0x2000, AMS_GET_SYSTEM_THREAD_PRIORITY(socket, ResolverIpcServer) - 1);
}

View File

@ -0,0 +1,200 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<char *>(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;
}
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("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<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size) {
const char *hostname = reinterpret_cast<const char *>(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<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size) {
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
LogDebug("[%016lx]: GetAddrInfoRequest(%s, %s)\n", this->client_info.program_id.value, reinterpret_cast<const char *>(node.GetPointer()), reinterpret_cast<const char *>(srv.GetPointer()));
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*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<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
const char *hostname = reinterpret_cast<const char *>(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<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", this->client_info.program_id.value, hostname, reinterpret_cast<const char *>(srv.GetPointer()));
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*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();
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> 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<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> 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<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> 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<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> 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)
namespace ams::mitm::socket::resolver {
class ResolverImpl : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - everything.
*/
return true;
}
public:
/* Overridden commands. */
Result GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size);
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<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size);
Result GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> 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<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
/* Extension commands. */
Result AtmosphereReloadHostsFile();
};
static_assert(IsIResolver<ResolverImpl>);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::socket::resolver::serializer {
class DNSSerializer {
public:
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<typename T>
static size_t SizeOf(const T &in);
template<typename T>
static size_t SizeOf(const T *in);
template<typename T>
static size_t SizeOf(const T *in, size_t count);
template<typename T>
static size_t SizeOf(const T **arr, u32 &out_count);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T &in);
template<typename T>
static ssize_t FromBuffer(T &out, const u8 *src, size_t src_size);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_Size, T *in);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, T **arr);
template<typename T>
static ssize_t FromBuffer(T *&out, const u8 *src, size_t src_size);
template<typename T>
static ssize_t FromBuffer(T **&out_arr, const u8 *src, size_t src_size);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T * const arr, size_t count);
template<typename T>
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);
void FreeAddrInfo(ams::socket::AddrInfo &addr_info);
void FreeAddrInfo(struct addrinfo &addr_info);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
constexpr inline u32 AddrInfoMagic = 0xBEEFCAFE;
template<typename T>
concept IsAddrInfo = std::same_as<T, ams::socket::AddrInfo> || std::same_as<T, struct addrinfo>;
template<typename T> requires IsAddrInfo<T>
using SockAddrType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddr, struct sockaddr>::type;
template<typename T> requires IsAddrInfo<T>
using SockAddrInType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddrIn, struct sockaddr_in>::type;
template<typename T> requires IsAddrInfo<T>
using SockAddrIn6Type = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, struct sockaddr_in6, struct sockaddr_in6>::type;
template<typename T> requires IsAddrInfo<T>
constexpr bool IsAfInet(const auto ai_family) {
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
return ai_family == ams::socket::Family::Af_Inet;
} else {
return ai_family == AF_INET;
}
}
template<typename T> requires IsAddrInfo<T>
constexpr bool IsAfInet6(const auto ai_family) {
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
return ai_family == ams::socket::Family::Af_Inet6;
} else {
return ai_family == AF_INET;
}
}
template<typename T> requires IsAddrInfo<T>
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<T>(cur->ai_family)) {
ams::socket::impl::Free(reinterpret_cast<SockAddrInType<T> *>(cur->ai_addr));
} else if (IsAfInet6<T>(cur->ai_family)) {
ams::socket::impl::Free(reinterpret_cast<SockAddrIn6Type<T> *>(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<typename T> requires IsAddrInfo<T>
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<T>(addr_info->ai_family)) {
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrInType<T> *>(addr_info->ai_addr));
} else if (IsAfInet6<T>(addr_info->ai_family)) {
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrIn6Type<T> *>(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<const char *>(addr_info->ai_canonname));
} else {
rc += sizeof(u8);
}
if (addr_info->ai_next == nullptr) {
rc += sizeof(u32);
}
return rc;
}
template<typename T> requires IsAddrInfo<T>
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<typename T> requires IsAddrInfo<T>
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<u32>(addr_info.ai_flags);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_socktype);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_protocol);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(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<T>(addr_info.ai_family)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrInType<T> *>(addr_info.ai_addr))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(addr_info.ai_family)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrIn6Type<T> *>(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<typename T> requires IsAddrInfo<T>
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<typename T> requires IsAddrInfo<T>
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<decltype(out.ai_flags)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_family = static_cast<decltype(out.ai_family)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_socktype = static_cast<decltype(out.ai_socktype)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_protocol = static_cast<decltype(out.ai_protocol)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_addrlen = static_cast<decltype(out.ai_addrlen)>(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<T>(out.ai_family)) {
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrInType<T>)));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_addr, 0, sizeof(SockAddrInType<T>));
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrInType<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(out.ai_family)) {
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrIn6Type<T>)));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_addr, 0, sizeof(SockAddrIn6Type<T>));
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrIn6Type<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
return rc;
}
} else {
out.ai_addr = static_cast<decltype(out.ai_addr)>(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<T *>(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<typename T> requires IsAddrInfo<T>
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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsHostEnt = std::same_as<T, ams::socket::HostEnt> || std::same_as<T, struct hostent>;
template<typename T> requires IsHostEnt<T>
using InAddrType = typename std::conditional<std::same_as<T, ams::socket::HostEnt>, ams::socket::InAddr, struct in_addr>::type;
template<typename T> requires IsHostEnt<T>
constexpr bool IsAfInet(const auto h_addrtype) {
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
return h_addrtype == ams::socket::Family::Af_Inet;
} else {
return h_addrtype == AF_INET;
}
}
template<typename T> requires IsHostEnt<T>
constexpr bool IsAfInet6(const auto h_addrtype) {
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
return h_addrtype == ams::socket::Family::Af_Inet6;
} else {
return h_addrtype == AF_INET;
}
}
template<typename T> requires IsHostEnt<T>
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<T> **)(in.h_addr_list), dummy);
return rc;
}
template<typename T> requires IsHostEnt<T>
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<u16>(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<T>(in.h_addrtype)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(in.h_addrtype)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(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<typename T> requires IsHostEnt<T>
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<decltype(out.h_addrtype)>(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<T> **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<typename T> requires IsHostEnt<T>
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<InAddrType<T> **>(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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsInAddr = std::same_as<T, ams::socket::InAddr> || std::same_as<T, struct in_addr>;
template<typename T> requires IsInAddr<T>
size_t SizeOfImpl(const T &in) {
return sizeof(u32);
}
template<typename T> requires IsInAddr<T>
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<typename T> requires IsInAddr<T>
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<typename T> requires IsInAddr<T>
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<const u32 *>(src));
rc = sizeof(u32);
return rc;
}
template<typename T> requires IsInAddr<T>
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<typename T> requires IsInAddr<T>
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<T **>(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<T *>(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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<const u16 *>(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<const u32 *>(src));
return sizeof(u32);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsSockAddrIn = std::same_as<T, ams::socket::SockAddrIn> || std::same_as<T, struct sockaddr_in>;
template<typename T> requires IsSockAddrIn<T>
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<typename T> requires IsSockAddrIn<T>
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<u16>(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<u16>(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<u32>(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<typename T> requires IsSockAddrIn<T>
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<decltype(out.sin_family)>(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<decltype(out.sin_port)>(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<decltype(out.sin_addr.s_addr)>(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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsSockAddrIn6 = std::same_as<T, struct sockaddr_in6>;
template<typename T> requires IsSockAddrIn6<T>
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<typename T> requires IsSockAddrIn6<T>
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<u16>(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<u16>(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<u32>(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<u32>(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<typename T> requires IsSockAddrIn6<T>
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<decltype(out.sin6_family)>(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<decltype(out.sin6_port)>(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<decltype(out.sin6_flowinfo)>(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<decltype(out.sin6_scope_id)>(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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<const char *>(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<const char *>(src)))) {
return 1;
} else if (src[0] == '\x00') {
return 1;
}
out = static_cast<char *>(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<const char **>(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<char **>(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<const char *>(cur));
out[i] = static_cast<char *>(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;
}
}

View File

@ -0,0 +1,97 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "sfdnsres_shim.h"
#include <stratosphere/sf/sf_mitm_dispatch.h>
/* 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;
}
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;
}

View File

@ -0,0 +1,21 @@
/**
* @file sfdnsres_shim.h
* @brief IPC wrapper for dns.mitm.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#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);
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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::socket::impl {
void *Alloc(size_t size);
void *Calloc(size_t num, size_t size);
void Free(void *ptr);
}

View File

@ -349,6 +349,19 @@ namespace ams::settings::fwdbg {
/* 0 = Disabled (not debug mode), 1 = Enabled (debug mode) */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_am_debug_mode", "u8!0x0"));
/* Controls whether dns.mitm is enabled. */
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm", "u8!0x1"));
/* Controls whether dns.mitm uses the default redirections in addition to */
/* whatever is specified in the user's hosts file. */
/* 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. */