From 0a8bd34b6993156da074ff89863f12e404e36ede Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 29 Jun 2010 21:20:32 -0700 Subject: [PATCH] Resolve .local domains with getaddrinfo() C-Ares doesn't go through the Name Service Switch (NSS) and thus can't resolve certain classes of names. Generally this doesn't matter and the whole idea of NSS is rather annoying. Nevertheless until C-Ares gets better support, adding this hack to go through getaddrinfo() for .local domain look up. This reverts commit 9926dacd14c39276299712ced4a83fb043f27162. --- lib/dns.js | 36 ++++++++----- src/node_net.cc | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 13 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index e57b9b4..52aa034 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -97,6 +97,8 @@ exports.getHostByName = function (domain, callback) { channel.getHostByName(domain, dns.AF_INET, callback); }; +var net; + // Easy DNS A/AAAA look up exports.lookup = function (domain, callback) { var addressType = dns.isIP(domain); @@ -105,19 +107,27 @@ exports.lookup = function (domain, callback) { callback(null, domain, addressType); }); } else { - channel.getHostByName(domain, dns.AF_INET, function (err, domains4) { - if (domains4 && domains4.length) { - callback(null, domains4[0], 4); - } else { - channel.getHostByName(domain, dns.AF_INET6, function (err, domains6) { - if (domains6 && domains6.length) { - callback(null, domains6[0], 6); - } else { - callback(err, []); - } - }); - } - }); + if (/\w\.local\.?$/.test(domain) ) { + // ANNOYING: In the case of mDNS domains use NSS in the thread pool. + // I wish c-ares had better support. + process.binding('net').getaddrinfo(domain, 4, function (err, domains4) { + callback(err, domains4[0], 4); + }); + } else { + channel.getHostByName(domain, dns.AF_INET, function (err, domains4) { + if (domains4 && domains4.length) { + callback(null, domains4[0], 4); + } else { + channel.getHostByName(domain, dns.AF_INET6, function (err, domains6) { + if (domains6 && domains6.length) { + callback(null, domains6[0], 6); + } else { + callback(err, []); + } + }); + } + }); + } } }; diff --git a/src/node_net.cc b/src/node_net.cc index bbc1540..cfeeb51 100644 --- a/src/node_net.cc +++ b/src/node_net.cc @@ -14,6 +14,8 @@ #include #include /* inet_pton */ +#include + #include #include @@ -1015,6 +1017,163 @@ static Handle SetKeepAlive(const Arguments& args) { return Undefined(); } +// +// G E T A D D R I N F O +// + + +struct resolve_request { + Persistent cb; + struct addrinfo *address_list; + int ai_family; // AF_INET or AF_INET6 + char hostname[1]; +}; + +#ifndef EAI_NODATA // EAI_NODATA is deprecated, FreeBSD already thrown it away in favor of EAI_NONAME +#define EAI_NODATA EAI_NONAME +#endif + +static int AfterResolve(eio_req *req) { + ev_unref(EV_DEFAULT_UC); + + struct resolve_request * rreq = (struct resolve_request *)(req->data); + + HandleScope scope; + Local argv[2]; + + if (req->result != 0) { + argv[1] = Array::New(); + if (req->result == EAI_NODATA) { + argv[0] = Local::New(Null()); + } else { + argv[0] = ErrnoException(req->result, + "getaddrinfo", + gai_strerror(req->result)); + } + } else { + struct addrinfo *address; + int n = 0; + + for (address = rreq->address_list; address; address = address->ai_next) { n++; } + + Local results = Array::New(n); + + char ip[INET6_ADDRSTRLEN]; + const char *addr; + + n = 0; + address = rreq->address_list; + while (address) { + assert(address->ai_socktype == SOCK_STREAM); + assert(address->ai_family == AF_INET || address->ai_family == AF_INET6); + addr = ( address->ai_family == AF_INET + ? (char *) &((struct sockaddr_in *) address->ai_addr)->sin_addr + : (char *) &((struct sockaddr_in6 *) address->ai_addr)->sin6_addr + ); + const char *c = inet_ntop(address->ai_family, addr, ip, INET6_ADDRSTRLEN); + Local s = String::New(c); + results->Set(Integer::New(n), s); + + n++; + address = address->ai_next; + } + + argv[0] = Local::New(Null()); + argv[1] = results; + } + + TryCatch try_catch; + + rreq->cb->Call(Context::GetCurrent()->Global(), 2, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + if (rreq->address_list) freeaddrinfo(rreq->address_list); + rreq->cb.Dispose(); // Dispose of the persistent handle + free(rreq); + + return 0; +} + + +static int Resolve(eio_req *req) { + // Note: this function is executed in the thread pool! CAREFUL + struct resolve_request * rreq = (struct resolve_request *) req->data; + + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = rreq->ai_family; + hints.ai_socktype = SOCK_STREAM; + + req->result = getaddrinfo((char*)rreq->hostname, + NULL, + &hints, + &(rreq->address_list)); + return 0; +} + + +static Handle GetAddrInfo(const Arguments& args) { + HandleScope scope; + + String::Utf8Value hostname(args[0]->ToString()); + + int type = args[1]->Int32Value(); + int fam = AF_INET; + switch (type) { + case 4: + fam = AF_INET; + break; + case 6: + fam = AF_INET6; + break; + default: + return ThrowException(Exception::TypeError( + String::New("Second argument must be an integer 4 or 6"))); + } + + if (!args[2]->IsFunction()) { + return ThrowException(Exception::TypeError( + String::New("Thrid argument must be a callback"))); + } + + Local cb = Local::Cast(args[2]); + + struct resolve_request *rreq = (struct resolve_request *) + calloc(1, sizeof(struct resolve_request) + hostname.length()); + + if (!rreq) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error( + String::New("Could not allocate enough memory"))); + } + + strcpy(rreq->hostname, *hostname); + rreq->cb = Persistent::New(cb); + rreq->ai_family = fam; + + // For the moment I will do DNS lookups in the eio thread pool. This is + // sub-optimal and cannot handle massive numbers of requests. + // + // (One particularly annoying problem is that the pthread stack size needs + // to be increased dramatically to handle getaddrinfo() see X_STACKSIZE in + // wscript ). + // + // In the future I will move to a system using c-ares: + // http://lists.schmorp.de/pipermail/libev/2009q1/000632.html + eio_custom(Resolve, EIO_PRI_DEFAULT, AfterResolve, rreq); + + // There will not be any active watchers from this object on the event + // loop while getaddrinfo() runs. If the only thing happening in the + // script was this hostname resolution, then the event loop would drop + // out. Thus we need to add ev_ref() until AfterResolve(). + ev_ref(EV_DEFAULT_UC); + + return Undefined(); +} + static Handle IsIP(const Arguments& args) { HandleScope scope; @@ -1084,6 +1243,7 @@ void InitNet(Handle target) { NODE_SET_METHOD(target, "setKeepAlive", SetKeepAlive); NODE_SET_METHOD(target, "getsockname", GetSockName); NODE_SET_METHOD(target, "getpeername", GetPeerName); + NODE_SET_METHOD(target, "getaddrinfo", GetAddrInfo); NODE_SET_METHOD(target, "isIP", IsIP); NODE_SET_METHOD(target, "errnoException", CreateErrnoException); -- 2.7.4