diff --git a/Source/IO/Net/AuNetResolver.Unix.cpp b/Source/IO/Net/AuNetResolver.Unix.cpp index 8092aeea..347dba2f 100644 --- a/Source/IO/Net/AuNetResolver.Unix.cpp +++ b/Source/IO/Net/AuNetResolver.Unix.cpp @@ -5,14 +5,25 @@ Date: 2022-8-26 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 +#endif #include "Networking.hpp" #include "AuNetResolver.Unix.hpp" #include "AuNetEndpoint.hpp" #include #include -#if defined(AURORA_IS_LINUX_DERIVED) - #define RESOLVER_IS_VERY_FREETARDED +// https://elixir.bootlin.com/glibc/glibc-2.40.9000/source/bits/types/sigevent_t.h#L8 +// 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 namespace Aurora::IO::Net @@ -147,9 +158,17 @@ namespace Aurora::IO::Net } sigevent event {0}; + + #if defined(AURORA_PLATFORM_LINUX) event.sigev_notify = SIGEV_SIGNAL; 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(this); if (!pQueue) @@ -170,12 +189,6 @@ namespace Aurora::IO::Net pBase->ar_request = (addrinfo *)&infoEx; pBase->ar_name = hostname.c_str(); - if (!AuTryInsert(tlsResolvers, AuSharedFromThis())) - { - SysPushErrorMemory(); - return false; - } - int iStatus = pgetaddrinfo_a(GAI_NOWAIT, &pBase, 1, @@ -183,27 +196,27 @@ namespace Aurora::IO::Net switch (iStatus) { - case EAI_AGAIN: - { - SysPushErrorNet("Low Resources | Failed to resolve"); - this->uOsError = iStatus; - this->error_ = this->ToError(); - return false; - } - case EAI_MEMORY: - { - SysPushErrorNet("Out of Memory | Failed to resolve"); - this->uOsError = iStatus; - this->error_ = this->ToError(); - return false; - } - case EAI_SYSTEM: - { - SysPushErrorNet("Invalid | Failed to resolve: {}", errno); - this->uOsError = errno; - this->error_ = this->ToError(); - return false; - } + case EAI_AGAIN: + { + SysPushErrorNet("Low Resources | Failed to resolve"); + this->uOsError = iStatus; + this->error_ = this->ToError(); + return false; + } + case EAI_MEMORY: + { + SysPushErrorNet("Out of Memory | Failed to resolve"); + this->uOsError = iStatus; + this->error_ = this->ToError(); + return false; + } + case EAI_SYSTEM: + { + SysPushErrorNet("Invalid | Failed to resolve: {}", errno); + this->uOsError = errno; + this->error_ = this->ToError(); + return this->StartStandard(); + } } if (iStatus != 0) @@ -214,6 +227,13 @@ namespace Aurora::IO::Net return false; } + if (!AuTryInsert(tlsResolvers, AuSharedFromThis())) + { + SysPushErrorMemory(); + this->Cancel(); + return false; + } + return this->BeginOperation(AuSharedFromThis(), this->pWorker_); @@ -468,13 +488,86 @@ namespace Aurora::IO::Net #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) { + // 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) { - 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(); } } }