Skip to content

Commit

Permalink
Start working on getaddrinfo_a
Browse files Browse the repository at this point in the history
  • Loading branch information
etcimon committed Dec 13, 2023
1 parent 4935b3b commit 2232f72
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 22 deletions.
7 changes: 6 additions & 1 deletion source/libasync/dns.d
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public:
}
do {
static if (LOG) .tracef("Resolving url: %s", url);
version(Windows) {
static if (is_Windows || EPOLL) {
if (force_async) {
m_cmdInfo.command = DNSCmd.RESOLVEHOST;
m_cmdInfo.ipv6 = ipv6;
Expand Down Expand Up @@ -168,6 +168,11 @@ package struct AsyncDNSRequest
import libasync.internals.win32;
PADDRINFOEX infos;
}
static if (EPOLL) {
import libasync.internals.socket_compat : gaicb, sigevent;
gaicb* host;
sigevent sig;
}
mixin FreeList!1_000;
}

Expand Down
2 changes: 2 additions & 0 deletions source/libasync/events.d
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ EventLoop getThreadEventLoop() nothrow {
return g_evLoop;
}

/*
static ~this() {
if (g_evLoop) g_evLoop.destroy();
}
*/

/// Event handlers can be registered to the event loop by being run(), all events
/// associated with them will trigger the OS to resume the underlying thread which
Expand Down
13 changes: 13 additions & 0 deletions source/libasync/internals/socket_compat.d
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,19 @@ else version (linux) {
TCP_CONGESTION = 13,
TCP_MD5SIG = 14
}
extern(C) nothrow @nogc struct gaicb {
const(char)* ar_name;
const(char)* ar_service;
const(addrinfo)* ar_request;
addrinfo* ar_result;
}
enum : int {
GAI_WAIT = 0,
GAI_NOWAIT = 1,
EAI_INPROGRESS = -100,
EAI_SYSTEM = -10
}
extern(C) nothrow @nogc int getaddrinfo_a(int mode, gaicb **list, int nitems, sigevent *sevp);
}
extern (C) nothrow @nogc:

Expand Down
203 changes: 184 additions & 19 deletions source/libasync/posix.d
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ private:
error_t m_error = EPosix.EOK;
EventInfo* m_evSignal;
static if (EPOLL){
EventInfo* m_evDNS;
fd_t m_epollfd;
HashMap!(Tuple!(fd_t, uint), DWFolderInfo) m_dwFolders; // uint = inotify_add_watch(Path)
}
Expand Down Expand Up @@ -262,9 +263,10 @@ package:
static if (EPOLL) {
close(m_epollfd); // not necessary?

// not necessary:
//try ThreadMem.free(m_evSignal);
//catch (Exception e) { assert(false, "Failed to free resources"); }
try ThreadMem.free(m_evSignal);
catch (Exception e) { assert(false, "Failed to free resources"); }
try ThreadMem.free(m_evDNS);
catch (Exception e) { assert(false, "Failed to free resources"); }

}
else
Expand Down Expand Up @@ -474,24 +476,67 @@ package:
}
break;

case EventType.Signal:
case EventType.DNSResolver:
static if (LOG) try log("Got signal!"); catch (Throwable e) {}

static if (EPOLL) {

static if (LOG) try log("Got signal: " ~ info.fd.to!string ~ " of type: " ~ info.evType.to!string); catch (Throwable e) {}
import libasync.internals.socket_compat : freeaddrinfo, addrinfo, gaicb;
static if (LOG) try log("Got dns signal: " ~ info.fd.to!string ~ " of type: " ~ info.evType.to!string); catch (Throwable e) {}
import core.sys.linux.sys.signalfd : signalfd_siginfo;
import core.sys.posix.unistd : read;
signalfd_siginfo fdsi;
fd_t err = cast(fd_t)read(info.fd, &fdsi, fdsi.sizeof);
shared AsyncSignal sig = cast(shared AsyncSignal) cast(void*) fdsi.ssi_ptr;
AsyncDNSRequest* req = cast(AsyncDNSRequest*) cast(void*) fdsi.ssi_ptr;

NetworkAddress addr;
addrinfo* res = req.host.ar_result;

try sig.handler();
ubyte* pAddr = cast(ubyte*) res.ai_addr;
ubyte* data = cast(ubyte*) addr.sockAddr;
data[0 .. res.ai_addrlen] = pAddr[0 .. res.ai_addrlen]; // perform bit copy
addr.family = cast(ushort)res.ai_family;
*req.dns.addr = cast(shared)addr;

try req.dns.callback();
catch (Exception e) {
setInternalError!"signal handler"(Status.ERROR);
}


freeaddrinfo(res);
try {
ThreadMem.free(&req.host.ar_request);
AsyncDNSRequest.free(req);
} catch (Exception e) {}


}
else /* if KQUEUE */
{
assert(false, "Unsupported platform for async DNS");
}
break;

case EventType.Signal:
static if (LOG) try log("Got signal!"); catch (Throwable e) {}

static if (EPOLL) {

static if (LOG) try log("Got signal: " ~ info.fd.to!string ~ " of type: " ~ info.evType.to!string); catch (Throwable e) {}
import core.sys.linux.sys.signalfd : signalfd_siginfo;
import core.sys.posix.unistd : read;
signalfd_siginfo fdsi;
fd_t err;
while ((err = cast(fd_t)read(info.fd, &fdsi, fdsi.sizeof)) > 0) {
shared AsyncSignal sig = cast(shared AsyncSignal) cast(void*) fdsi.ssi_ptr;

try sig.handler();
catch (Exception e) {
setInternalError!"signal handler"(Status.ERROR);
}
}


}
else /* if KQUEUE */
{
Expand All @@ -501,16 +546,16 @@ package:
try sigarr = new AsyncSignal[32];
catch (Exception e) { assert(false, "Could not allocate signals array"); }
}

bool more = popSignals(sigarr);
foreach (AsyncSignal sig; sigarr)
{
shared AsyncSignal ptr = cast(shared AsyncSignal) sig;
if (ptr is null)
break;
try (cast(shared AsyncSignal)sig).handler();
catch (Exception e) {
setInternalError!"signal handler"(Status.ERROR);
while (popSignals(sigarr)) {
foreach (AsyncSignal sig; sigarr)
{
shared AsyncSignal ptr = cast(shared AsyncSignal) sig;
if (ptr is null)
break;
try (cast(shared AsyncSignal)sig).handler();
catch (Exception e) {
setInternalError!"signal handler"(Status.ERROR);
}
}
}
}
Expand Down Expand Up @@ -1687,6 +1732,125 @@ package:
return getAddressInfo(host, port, ipv6, tcp, hints);
}

bool resolve(AsyncDNSRequest* req, in string url, in ushort port = 0, in bool ipv6 = true, in bool tcp = true)
{
m_status = StatusInfo.init;
import libasync.internals.socket_compat : AF_INET, AF_INET6, SOCK_DGRAM, SOCK_STREAM, IPPROTO_TCP, IPPROTO_UDP, getaddrinfo_a, sigevent, gaicb, addrinfo, GAI_NOWAIT, GAI_WAIT;
import std.string: format;
static bool is_sig_setup = false;

if (!is_sig_setup) {
import core.sys.linux.sys.signalfd;
import core.thread : getpid;

fd_t err;
fd_t dnsfd;

sigset_t mask;

try {
sigemptyset(&mask);
sigaddset(&mask, __libc_current_sigrtmin() + 1);
err = pthread_sigmask(SIG_BLOCK, &mask, null);
if (catchError!"sigprocmask"(err))
{
m_status.code = Status.EVLOOP_FAILURE;
return false;
}
} catch (Throwable) { }



dnsfd = signalfd(-1, &mask, SFD_NONBLOCK);
assert(dnsfd > 0, "Failed to setup signalfd in epoll");

EventType evtype;

epoll_event _event;
_event.events = EPOLLIN;
evtype = EventType.DNSResolver;
try
m_evDNS = ThreadMem.alloc!EventInfo(dnsfd, evtype, EventObject.init, m_instanceId);
catch (Exception e){
assert(false, "Allocation error");
}
_event.data.ptr = cast(void*) m_evDNS;

err = epoll_ctl(m_epollfd, EPOLL_CTL_ADD, dnsfd, &_event);
if (catchError!"EPOLL_CTL_ADD(sfd)"(err))
{
return false;
}
}
addrinfo* hints = assumeWontThrow(ThreadMem.alloc!addrinfo());
error_t err;
if (ipv6) {
hints.ai_family = AF_INET6;
}
else {
hints.ai_family = AF_INET;
}
if (tcp) {
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
}
else {
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
}

gaicb* host = assumeWontThrow(ThreadMem.alloc!gaicb());
sigevent* sig = &req.sig;

host.ar_request = hints;
char[] url_chars = assumeWontThrow(ThreadMem.alloc!(char[])(url.length + 1));
url_chars[0 .. $-1] = url[0 .. $];
url_chars[$-1] = 0;

host.ar_name = url_chars.ptr;
host.ar_service = null;
if (port != 0) {
host.ar_service = null;
}
req.host = host;

sig.sigev_notify = SIGEV_SIGNAL;
sig.sigev_value.sival_ptr = cast(void*)req;
sig.sigev_signo = __libc_current_sigrtmin() + 1;

static if (LOG) {
log("Resolving " ~ url ~ ":" ~ port.to!string);
}
err = cast(error_t) getaddrinfo_a(GAI_WAIT, &req.host, 1, sig);

tracef("getaddrinfo_a ==> %d", err);

if (err != EPosix.EOK) {

/// Unfortunately, glibc < 2.26 has a bug that the DNS resolver caches the contents
/// of /etc/resolve.conf. (See https://sourceware.org/bugzilla/show_bug.cgi?id=984)
/// An issue of Pidgen bug tracker(https://developer.pidgin.im/ticket/2825) shows
/// that calling res_init to refresh the nameserver list.
version (CRuntime_Glibc)
{
version (linux)
{
__res_init();
}
/// At least res_init isn't thread-safe on OSX/iOS, so nothing to do.
}
version(iOS) {
// ios uses a different error reporting for getaddrinfo
import libasync.internals.socket_compat : gai_strerror;
import std.string : fromStringz;
setInternalError!"getAddressInfo"(Status.ERROR, gai_strerror(cast(int) err).fromStringz.to!string);
}
else setInternalError!"getAddressInfo"(Status.ERROR, string.init, err);
return false;
}
return true;
}

void setInternalError(string TRACE)(in Status s, string details = "", error_t error = cast(EPosix) errno())
{
if (details.length > 0)
Expand Down Expand Up @@ -3424,7 +3588,8 @@ enum EventType : char {
Signal,
Timer,
DirectoryWatcher,
Event // custom
Event, // custom
DNSResolver
}

struct EventInfo {
Expand Down
6 changes: 4 additions & 2 deletions source/libasync/types.d
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ static if (__VERSION__ < 2103){
else {
import std.logger.core : LogLevel;
}
enum LOG = false; //trace
enum LOGLEVEL = LogLevel.off;
enum LOG = true; //trace
enum LOGLEVEL = LogLevel.all;
enum DEBUG = false;

import std.typecons: Flag;
Expand All @@ -23,6 +23,8 @@ version(linux) enum is_linux = true;
else enum is_linux = false;
version(iOS) enum is_iOS = true;
else enum is_iOS = false;
version(Windows) enum is_Windows = true;
else enum is_Windows = false;

static if (is_linux)
enum EPOLL = true;
Expand Down

0 comments on commit 2232f72

Please sign in to comment.