/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuNetResolver.Unix.cpp Date: 2022-8-26 Author: Reece ***/ #include "Networking.hpp" #include "AuNetResolver.Unix.hpp" #include "AuNetEndpoint.hpp" #include #include #if defined(AURORA_IS_LINUX_DERIVED) #define RESOLVER_IS_VERY_FREETARDED #endif namespace Aurora::IO::Net { static const auto kMagicSignal = SIGRTMIN + 10 - 6 + 9; static AuList> tlsResolvers; struct ThreadLocalCaughtCompletion : Loop::LSSignalCatcher { ThreadLocalCaughtCompletion(NetResolver *pParent); virtual bool OnTrigger(AuUInt handle) override; virtual const AuList &GetHandles() override; virtual bool Singular() override; private: AuList handles_; NetResolver *pParent_; }; ThreadLocalCaughtCompletion::ThreadLocalCaughtCompletion(NetResolver *pParent) : LSSignalCatcher({kMagicSignal}), pParent_(pParent) { handles_ = {this->handle, AuStaticCast(this->pParent_->pEvent)->GetHandle()}; } const AuList &ThreadLocalCaughtCompletion::GetHandles() { return this->handles_; } bool ThreadLocalCaughtCompletion::Singular() { return false; } bool ThreadLocalCaughtCompletion::OnTrigger(AuUInt handle) { if (this->pParent_->CheckAsync()) { if (this->pParent_->pEvent) { this->pParent_->pEvent->Reset(); } return true; } // TODO (Reece) urgent: HACK FIX ME // Its a slow path so this hack shouldn't matter. // We specifically optimize it by keeping the event latched until we // specifically receive a poll update, in which case, reset the event, // give the shitty glibc freetarded code a chance to catch up, then check. // This way, we should never miss an update #if 1 if (handle == handles_[1]) { if (this->pParent_->pEvent) { this->pParent_->pEvent->Reset(); } for (int i = 0; i < 10; i++) { AuThreading::ContextYield(); } return this->pParent_->CheckAsync(); } #endif return false; } NetResolver::NetResolver(const AuSPtr &pWorker) : pWorker_(pWorker) { this->pEvent = AuLoop::NewLSEvent(false, false); } NetResolver::NetResolver(const AuSPtr &pWorker, const AuSPtr, NetError>> &pCompletion) : pCompletion_(pCompletion), pWorker_(pWorker) { this->pEvent = AuLoop::NewLSEvent(false, false); } NetResolver::~NetResolver() { } bool NetResolver::Start() { int iInfoEx { 0 }; if (this->bA && this->bAAAA) { infoEx.ai_family = AF_UNSPEC; } else if (this->bA && !this->bAAAA) { infoEx.ai_family = AF_INET; } else if (this->bAAAA) { infoEx.ai_family = AF_INET6; } else { return false; } sigevent event {0}; event.sigev_notify = SIGEV_SIGNAL; event.sigev_signo = kMagicSignal; //event._sigev_un._tid = gettid(); auto pQueue = AuMakeShared(this); if (!pQueue) { SysPushErrorNet(""); return false; } if (!pQueue->HasValidHandle()) { SysPushErrorNet(""); return false; } this->UpdateTrigger(pQueue); auto pBase = &this->gnuAsyncOperation; pBase->ar_request = (addrinfo *)&infoEx; pBase->ar_name = hostname.c_str(); AuTryInsert(tlsResolvers, AuSharedFromThis()); int iStatus = ::getaddrinfo_a(GAI_NOWAIT, &pBase, 1, &event); switch (iStatus) { case EAI_AGAIN: { SysPushErrorNet("Low Resources | Failed to resolve"); return false; } case EAI_MEMORY: { SysPushErrorNet("Out of Memory | Failed to resolve"); return false; } case EAI_SYSTEM: { SysPushErrorNet("Invalid | Failed to resolve"); return false; } } if (iStatus != 0) { SysPushErrorNet("{}", iStatus); return false; } return this->BeginOperation(AuSharedFromThis(), this->pWorker_); } void NetResolver::SetCompletion(const AuSPtr, NetError>> &callback) { this->pCompletion_ = callback; } void NetResolver::OnOverlappedComplete() { for (auto itr = tlsResolvers.begin(); itr != tlsResolvers.end(); ) { auto pResolver = (*itr).lock(); if (!pResolver) { itr = tlsResolvers.erase(itr); continue; } if (pResolver.get() == this) { itr = tlsResolvers.erase(itr); continue; } itr++; } if (AuExchange(this->bHasCompleted_, true)) { return; } if (this->Unpack()) { if (this->pCompletion_) { try { this->pCompletion_->OnSuccess((void *)&this->processedIps_); } catch (...) { SysPushErrorCatch(); } } } else { if (this->pCompletion_) { try { this->error_ = ENetworkError::eAsyncError; this->pCompletion_->OnFailure((void *)&this->error_); } catch (...) { SysPushErrorCatch(); } } } } void NetResolver::OnOverlappedFailure(const NetError &error) { this->error_ = error; if (this->pCompletion_) { try { this->pCompletion_->OnFailure((void *)&this->error_); } catch (...) { SysPushErrorCatch(); } } this->bHasCompleted_ = true; } bool NetResolver::HasFinished() { return this->bHasCompleted_; } bool NetResolver::CheckAsync() { int iStatus = ::gai_error(&this->gnuAsyncOperation); return ((iStatus == 0) || (iStatus == EAI_CANCELED)); } void NetResolver::Cancel() { while (!this->HasComplete()) { int iStatus = ::gai_cancel(&this->gnuAsyncOperation); switch (iStatus) { case EAI_CANCELED: case EAI_ALLDONE: break; case EAI_NOTCANCELED: { IOYield(); AuThreading::ContextYield(); continue; } } } } const NetError &NetResolver::GetError() { return this->error_; } bool NetResolver::Unpack() { if (!CheckAsync()) { return false; } for (auto pItr = this->gnuAsyncOperation.ar_result; pItr; pItr = pItr->ai_next) { NetEndpoint endpoint; AuMemcpy(endpoint.hint, pItr->ai_addr, pItr->ai_addrlen); DeoptimizeEndpoint(endpoint); if (AuExists(this->processedIps_, endpoint.ip)) { continue; } if (!AuTryInsert(this->processedIps_, endpoint.ip)) { return false; } } return true; } static void Annoying(int) { for (const auto &gawad : tlsResolvers) { if (auto pResolver = gawad.lock()) { pResolver->pEvent->Set(); } } } void InitFreetardedResolver() { struct sigaction action = { .sa_handler = Annoying, .sa_flags = SA_ONSTACK }; ::sigemptyset(&action.sa_mask); ::sigaction(kMagicSignal, &action, nullptr); } }