[*] Harden glibc resolver
This commit is contained in:
parent
6e9e962c84
commit
e63fb1c996
@ -5,14 +5,25 @@
|
|||||||
Date: 2022-8-26
|
Date: 2022-8-26
|
||||||
Author: Reece
|
Author: Reece
|
||||||
***/
|
***/
|
||||||
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||||
|
#define RESOLVER_IS_VERY_FREETARDED
|
||||||
|
// Compilers compiled for linux should define this for us, including GCC.
|
||||||
|
#if !defined(_GNU_SOURCE)
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
//Defines: __USE_GNU
|
||||||
|
#include <features.h>
|
||||||
|
#endif
|
||||||
#include "Networking.hpp"
|
#include "Networking.hpp"
|
||||||
#include "AuNetResolver.Unix.hpp"
|
#include "AuNetResolver.Unix.hpp"
|
||||||
#include "AuNetEndpoint.hpp"
|
#include "AuNetEndpoint.hpp"
|
||||||
#include <Source/IO/Loop/LSSignalCatcher.Linux.hpp>
|
#include <Source/IO/Loop/LSSignalCatcher.Linux.hpp>
|
||||||
#include <Source/IO/Loop/LSEvent.hpp>
|
#include <Source/IO/Loop/LSEvent.hpp>
|
||||||
|
|
||||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
// https://elixir.bootlin.com/glibc/glibc-2.40.9000/source/bits/types/sigevent_t.h#L8
|
||||||
#define RESOLVER_IS_VERY_FREETARDED
|
// https://elixir.bootlin.com/musl/v1.2.5/source/include/signal.h#L190
|
||||||
|
#if defined(__USE_GNU)
|
||||||
|
#define sigev_notify_thread_id _sigev_un._tid
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Aurora::IO::Net
|
namespace Aurora::IO::Net
|
||||||
@ -147,9 +158,17 @@ namespace Aurora::IO::Net
|
|||||||
}
|
}
|
||||||
|
|
||||||
sigevent event {0};
|
sigevent event {0};
|
||||||
|
|
||||||
|
#if defined(AURORA_PLATFORM_LINUX)
|
||||||
event.sigev_notify = SIGEV_SIGNAL;
|
event.sigev_notify = SIGEV_SIGNAL;
|
||||||
event.sigev_signo = GetGAIAsyncIOSignal();
|
event.sigev_signo = GetGAIAsyncIOSignal();
|
||||||
//event._sigev_un._tid = gettid();
|
event.sigev_notify_thread_id = gettid();
|
||||||
|
// pid_t caller_pid = sig->sigev_notify == SIGEV_SIGNAL ? getpid() : 0;
|
||||||
|
// not required for glibc resolv?
|
||||||
|
#else
|
||||||
|
// TODO: setup threaded callback:
|
||||||
|
return this->StartStandard();
|
||||||
|
#endif
|
||||||
|
|
||||||
auto pQueue = AuMakeShared<ThreadLocalCaughtCompletion>(this);
|
auto pQueue = AuMakeShared<ThreadLocalCaughtCompletion>(this);
|
||||||
if (!pQueue)
|
if (!pQueue)
|
||||||
@ -170,12 +189,6 @@ namespace Aurora::IO::Net
|
|||||||
pBase->ar_request = (addrinfo *)&infoEx;
|
pBase->ar_request = (addrinfo *)&infoEx;
|
||||||
pBase->ar_name = hostname.c_str();
|
pBase->ar_name = hostname.c_str();
|
||||||
|
|
||||||
if (!AuTryInsert(tlsResolvers, AuSharedFromThis()))
|
|
||||||
{
|
|
||||||
SysPushErrorMemory();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int iStatus = pgetaddrinfo_a(GAI_NOWAIT,
|
int iStatus = pgetaddrinfo_a(GAI_NOWAIT,
|
||||||
&pBase,
|
&pBase,
|
||||||
1,
|
1,
|
||||||
@ -183,27 +196,27 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
switch (iStatus)
|
switch (iStatus)
|
||||||
{
|
{
|
||||||
case EAI_AGAIN:
|
case EAI_AGAIN:
|
||||||
{
|
{
|
||||||
SysPushErrorNet("Low Resources | Failed to resolve");
|
SysPushErrorNet("Low Resources | Failed to resolve");
|
||||||
this->uOsError = iStatus;
|
this->uOsError = iStatus;
|
||||||
this->error_ = this->ToError();
|
this->error_ = this->ToError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case EAI_MEMORY:
|
case EAI_MEMORY:
|
||||||
{
|
{
|
||||||
SysPushErrorNet("Out of Memory | Failed to resolve");
|
SysPushErrorNet("Out of Memory | Failed to resolve");
|
||||||
this->uOsError = iStatus;
|
this->uOsError = iStatus;
|
||||||
this->error_ = this->ToError();
|
this->error_ = this->ToError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case EAI_SYSTEM:
|
case EAI_SYSTEM:
|
||||||
{
|
{
|
||||||
SysPushErrorNet("Invalid | Failed to resolve: {}", errno);
|
SysPushErrorNet("Invalid | Failed to resolve: {}", errno);
|
||||||
this->uOsError = errno;
|
this->uOsError = errno;
|
||||||
this->error_ = this->ToError();
|
this->error_ = this->ToError();
|
||||||
return false;
|
return this->StartStandard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iStatus != 0)
|
if (iStatus != 0)
|
||||||
@ -214,6 +227,13 @@ namespace Aurora::IO::Net
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!AuTryInsert(tlsResolvers, AuSharedFromThis()))
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
this->Cancel();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return this->BeginOperation(AuSharedFromThis(),
|
return this->BeginOperation(AuSharedFromThis(),
|
||||||
this->pWorker_);
|
this->pWorker_);
|
||||||
|
|
||||||
@ -468,13 +488,86 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
#if defined(AURORA_HAS_GLIBC_RESOLVER_PROXY_STUBS)
|
#if defined(AURORA_HAS_GLIBC_RESOLVER_PROXY_STUBS)
|
||||||
|
|
||||||
|
// Our name-resolution signaling options for glibc / GAHHNUUU-EEB-SNCCEE are:
|
||||||
|
// 1) [gahnuu IO] Wait forever for glibc to spawn a thread with their own pthreads; or
|
||||||
|
// 2) [gahnuu IO] Use their interface for sending a signal instead. Noting that Linux-likes can
|
||||||
|
// handle sigev_notify_thread_id notifications, via a signalqueue and sigevent field.
|
||||||
|
// 3) [posix] Just call getaddrinfo in a thread pool ourselves (or just not and create a thread each time. same for winxp.)
|
||||||
|
//
|
||||||
|
// Noting that GAHHNUUU-SNCCEE resolv NSS modules are the only hope of standard Linux-like applications of getting DoH,
|
||||||
|
// alternative plugin-based DNS services, and cached results on their system.
|
||||||
|
// musl and other embedded drivers are hopeless.
|
||||||
|
//
|
||||||
|
// No matter the POSIX implementation type, it'll match WinXP - 11 with the Windows Name Resolution stack under Windows Sockets 2.
|
||||||
|
// Canceling? Windows XP, Vista and 7 doesn't even have GetAddrInfoExCancel. glibc says, na, not yet because its not in posix yet.
|
||||||
|
// Canceling, but can we? Ok, we can sort of cancel on all platforms. On old Windows, we could force term a thread, but we dont, and idk if we should.
|
||||||
|
// On other POSIX systems, we could force term a thread, but we dont, and idk if we should.
|
||||||
|
// On glibc, we have gai_cancel, that might work.
|
||||||
|
// On glibc, posix, and Windows 7-XP, we will probably find ourselves spinning in a yield loop until the DNS request has been resolved or properly canceled.
|
||||||
|
// Caching? Yea probably.
|
||||||
|
// IPv6? Yea, if the platform vendor supports it.
|
||||||
|
// User config? Yea, if the platform vendor supports it.
|
||||||
|
// Native async? Maybe a thread pool if we're lucky. Otherwise we can just spawn a thread and get the same results
|
||||||
|
//
|
||||||
|
// On the plus side, libc abstraction of getaddrinfo in async form (glibc) or our own hack of spawn a thread (hello ::StartStandard),
|
||||||
|
// we always guarantee the system has a good async getaddrinfo;
|
||||||
|
// the interface we just established has the ability for the user to configure it; and
|
||||||
|
// the system will probably be handling our DNS caching in a global platform standard.
|
||||||
|
//
|
||||||
|
// On alternative libcs and platforms:
|
||||||
|
// >Musl will just have to write a good internal DNS library with plugin routing, caching, and/or async. Not my problem.
|
||||||
|
// >Android has its' own DNS stack based on ISCs lib, with caching. No async expected.
|
||||||
|
// >XNU has its' own dns stack based on ISCs lib (or at least they did. old libresolv.). No async expected.
|
||||||
|
// We can expect forks of ISCs library with IPv6 support with cached entries, somewhere; or similar features under getaddrinfo.
|
||||||
|
//
|
||||||
|
// On Linux:
|
||||||
|
// >Most Linux users will be using their own local version of glibc.
|
||||||
|
// >DNS can be configured how the user wants.
|
||||||
|
// >systemd-resolv and friends should take care of system local cache conctrol.
|
||||||
|
// >and yes that does mean implicit integration with systemd
|
||||||
|
// (systemd-resolve via the NSS module 'nss-resolve' gets served under our users of getaddrinfo [also incl the call under glibc aio] ).
|
||||||
|
//
|
||||||
|
// We assume best case glibc/signal on linux glibc targets.
|
||||||
|
// glibcs async resolve just uses getaddrinfo under the hood, with an actual threadpool. the only question which is faster: pthread spawn or a linux sigqueue?
|
||||||
|
// Knowing we can jut rely on getaddrinfo no matter what, we just spawn a detached thread, if we need async on other platforms with the same expected feature set.
|
||||||
|
// getaddrinfo may suck, but it's the only minimum interface we can provide and expect.
|
||||||
|
//
|
||||||
|
// So far as providing a common async abstraction for the expected platform provider is concerned, this should be the right thing.
|
||||||
|
//
|
||||||
|
// We could always build our own resolver on top of this and:
|
||||||
|
// >platform("k.root-servers.net") ?? (EU RIPE NCC, headquartered in Amsterdam. probably won't care for german or american lower level judges having a moment. )
|
||||||
|
// >193.0.14.129 ?? 2001:7fd::1 ??
|
||||||
|
// >platform("g.root-servers.net") ?? (US DoD glow plant. the good thing: EU plebs and activists be damned, they will not touch DNS results without a vaild US federal court order and 15 layers of bureaucracy. bc DOD feds.)
|
||||||
|
// >192.112.36.4 ?? 2001:500:12::d0d
|
||||||
|
// for the most globally standard, consistent, and censorship-free results.
|
||||||
|
// The downsides:
|
||||||
|
// No caching (TODO: registry with sqlite?)
|
||||||
|
// Probably wont be privacy friendly.
|
||||||
|
// Not all servers with be authenticated or encrypted.
|
||||||
|
// You could implement a nonstandard DNS server that's always recursive, cached, and encrypted, *but* then you probably need to worry about user consent and extra costs.
|
||||||
static void Annoying(int)
|
static void Annoying(int)
|
||||||
{
|
{
|
||||||
|
// We probably do not need SA_NODEFER or iterator protection.
|
||||||
|
//
|
||||||
|
// Under Linux, we can target a specific posix processes with a signal properly.
|
||||||
|
// Under other systems, we cant. On these systems, we shouldn't expect SA_NODEFER to exist.
|
||||||
|
// Let's assume we have proper signal queues. On this note...
|
||||||
|
//
|
||||||
|
// if (--*waitlist->counterp == 0)
|
||||||
|
// __gai_notify_only (waitlist->sigevp, waitlist->caller_pid);
|
||||||
|
// https://elixir.bootlin.com/glibc/glibc-2.40.9000/source/resolv/gai_misc.c#L337
|
||||||
|
// https://elixir.bootlin.com/glibc/glibc-2.40.9000/source/resolv/gai_notify.c#L124-L128
|
||||||
|
// https://elixir.bootlin.com/glibc/glibc-2.40.9000/source/sysdeps/unix/sysv/linux/gai_sigqueue.c#L31
|
||||||
|
// I think glibc is trying to protect us against this as well? I don't even know. I doubt they know either.
|
||||||
|
// They correctly pass around the pid, and then claim they need to because of a linux bug? what the actual fuck.
|
||||||
|
// Average gahnnuuu project. beri quality code.
|
||||||
|
|
||||||
for (const auto &gawad : tlsResolvers)
|
for (const auto &gawad : tlsResolvers)
|
||||||
{
|
{
|
||||||
if (auto pResolver = gawad.lock())
|
if (auto pResolver = AuTryLockMemoryType(gawad))
|
||||||
{
|
{
|
||||||
pResolver->pEvent->Set();
|
// Under almost all POSIX systems, this will result in a blocking write
|
||||||
|
pResolver->pEvent->Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user