mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 00:12:03 +00:00
dns.mitm: parse redirections from hosts file
This commit is contained in:
parent
400f5142ee
commit
d6477cf024
9 changed files with 378 additions and 9 deletions
|
@ -42,6 +42,13 @@
|
|||
; 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
|
||||
; 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)
|
||||
; atmosphere!add_defaults_to_dns_hosts = 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.
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -17,14 +17,297 @@
|
|||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
#include "socket_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char DefaultHostsFile[] =
|
||||
"# Nintendo telemetry servers\n"
|
||||
"127.0.0.1 receive-lp1.dg.srv.nintendo.net\n"
|
||||
"127.0.0.1 receive-lp1.er.srv.nintendo.net\n";
|
||||
|
||||
constinit os::SdkMutex g_redirection_lock;
|
||||
std::unordered_map<std::string, ams::socket::InAddrT> g_redirection_map;
|
||||
|
||||
constinit char g_specific_emummc_hosts_path[0x40] = {};
|
||||
|
||||
void ParseHostsFile(const char *file_data) {
|
||||
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 {
|
||||
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') {
|
||||
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';
|
||||
|
||||
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
|
||||
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else {
|
||||
state = State::WhiteSpace;
|
||||
}
|
||||
} 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';
|
||||
|
||||
g_redirection_map[static_cast<const char *>(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", 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")) {
|
||||
return "/hosts/emummc";
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/emummc");
|
||||
} else {
|
||||
if (mitm::fs::HasAtmosphereSdFile("/hosts/sysmmc")) {
|
||||
return "/hosts/sysmmc";
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/sysmmc");
|
||||
}
|
||||
|
||||
return "/hosts/default";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeResolverRedirections(bool add_defaults) {
|
||||
/* Acquire exclusive access to the map. */
|
||||
std::scoped_lock lk(g_redirection_lock);
|
||||
|
||||
/* Clear the redirections map. */
|
||||
g_redirection_map.clear();
|
||||
|
||||
/* Open log file. */
|
||||
::FsFile log_file;
|
||||
mitm::fs::DeleteAtmosphereSdFile("/dns_mitm_startup.log");
|
||||
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/dns_mitm_startup.log", 0, ams::fs::CreateOption_None));
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(log_file), "/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")) {
|
||||
Log(log_file, "Creating /hosts/default because it does not exist.\n");
|
||||
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/hosts");
|
||||
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/hosts/default", sizeof(DefaultHostsFile) - 1, ams::fs::CreateOption_None));
|
||||
|
||||
::FsFile default_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(default_file), "/hosts/default", 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_map) {
|
||||
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) {
|
||||
/* TODO: Real implementation */
|
||||
if (std::strcmp(hostname, "receive-lp1.dg.srv.nintendo.net") == 0) {
|
||||
*out = ams::socket::InAddr_Loopback;
|
||||
return true;
|
||||
std::scoped_lock lk(g_redirection_lock);
|
||||
|
||||
for (const auto &[host, address] : g_redirection_map) {
|
||||
if (std::strcmp(host.c_str(), hostname) == 0) {
|
||||
*out = address;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
void InitializeResolverRedirections(bool add_defaults);
|
||||
|
||||
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname);
|
||||
|
||||
}
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "dnsmitm_module.hpp"
|
||||
#include "dnsmitm_resolver_impl.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_resolver_impl.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
|
@ -93,15 +94,39 @@ namespace ams::mitm::socket::resolver {
|
|||
}
|
||||
}
|
||||
|
||||
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 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 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();
|
||||
|
||||
/* Initialize redirection map. */
|
||||
resolver::InitializeResolverRedirections(ShouldAddDefaultResolverRedirections());
|
||||
|
||||
/* Create mitm servers. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<ResolverImpl>(PortIndex_Mitm, DnsMitmServiceName)));
|
||||
|
||||
|
|
|
@ -349,6 +349,15 @@ 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!0x0"));
|
||||
|
||||
/* Hbloader custom settings. */
|
||||
|
||||
/* Controls the size of the homebrew heap when running as applet. */
|
||||
|
|
Loading…
Reference in a new issue