mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-09-30 22:57:08 +02:00
Merge branch 'master' of https://github.com/Atmosphere-NX/Atmosphere into logmanager
This commit is contained in:
commit
eee6f92556
2
.gitignore
vendored
2
.gitignore
vendored
@ -95,3 +95,5 @@ sept/sept-secondary/KEYS.py
|
||||
**/build_nintendo_nx_arm
|
||||
**/build_nintendo_nx_x64
|
||||
**/build_nintendo_nx_x86
|
||||
|
||||
stratosphere/test/
|
||||
|
@ -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.
|
||||
|
@ -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).
|
||||
|
@ -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
53
docs/features/dns_mitm.md
Normal 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`.
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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++) {
|
||||
|
@ -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. */
|
||||
|
@ -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. */
|
||||
|
@ -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());
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
18
libraries/libstratosphere/include/stratosphere/nsd.hpp
Normal file
18
libraries/libstratosphere/include/stratosphere/nsd.hpp
Normal 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>
|
@ -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();
|
||||
|
||||
}
|
@ -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"};
|
||||
|
||||
}
|
22
libraries/libstratosphere/include/stratosphere/socket.hpp
Normal file
22
libraries/libstratosphere/include/stratosphere/socket.hpp
Normal 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>
|
@ -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);
|
||||
|
||||
}
|
@ -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; }
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
28
libraries/libstratosphere/source/socket/impl/socket_api.hpp
Normal file
28
libraries/libstratosphere/source/socket/impl/socket_api.hpp
Normal 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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
libraries/libstratosphere/source/socket/socket_api.cpp
Normal file
45
libraries/libstratosphere/source/socket/socket_api.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
69
libraries/libvapours/include/vapours/util/util_fixed_map.hpp
Normal file
69
libraries/libvapours/include/vapours/util/util_fixed_map.hpp
Normal 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(); }
|
||||
};
|
||||
|
||||
}
|
28
libraries/libvapours/include/vapours/util/util_fixed_set.hpp
Normal file
28
libraries/libvapours/include/vapours/util/util_fixed_set.hpp
Normal 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> {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
}
|
998
libraries/libvapours/include/vapours/util/util_fixed_tree.hpp
Normal file
998
libraries/libvapours/include/vapours/util/util_fixed_tree.hpp
Normal 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};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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>(),
|
||||
};
|
||||
|
||||
|
77
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.cpp
Normal file
77
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.hpp
Normal file
25
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_debug.hpp
Normal 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)));
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
137
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp
Normal file
137
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
24
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.hpp
Normal file
24
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.hpp
Normal 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);
|
||||
|
||||
}
|
200
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp
Normal file
200
stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
@ -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>);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
97
stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c
Normal file
97
stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.c
Normal 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;
|
||||
}
|
||||
|
21
stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h
Normal file
21
stratosphere/ams_mitm/source/dns_mitm/sfdnsres_shim.h
Normal 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
|
25
stratosphere/ams_mitm/source/dns_mitm/socket_allocator.hpp
Normal file
25
stratosphere/ams_mitm/source/dns_mitm/socket_allocator.hpp
Normal 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);
|
||||
|
||||
}
|
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user