This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

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 9926dac.
  • Loading branch information...
ry committed Jun 30, 2010
1 parent 02ed0ec commit 0a8bd34b6993156da074ff89863f12e404e36ede
Showing with 183 additions and 13 deletions.
  1. +23 −13 lib/dns.js
  2. +160 −0 src/node_net.cc
View
@@ -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, []);
+ }
+ });
+ }
+ });
+ }
}
};
View
@@ -14,6 +14,8 @@
#include <fcntl.h>
#include <arpa/inet.h> /* inet_pton */
+#include <netdb.h>
+
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -1015,6 +1017,163 @@ static Handle<Value> SetKeepAlive(const Arguments& args) {
return Undefined();
}
+//
+// G E T A D D R I N F O
+//
+
+
+struct resolve_request {
+ Persistent<Function> 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<Value> argv[2];
+
+ if (req->result != 0) {
+ argv[1] = Array::New();
+ if (req->result == EAI_NODATA) {
+ argv[0] = Local<Value>::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<Array> 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<String> s = String::New(c);
+ results->Set(Integer::New(n), s);
+
+ n++;
+ address = address->ai_next;
+ }
+
+ argv[0] = Local<Value>::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<Value> 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<Function> cb = Local<Function>::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<Function>::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<Value> IsIP(const Arguments& args) {
HandleScope scope;
@@ -1084,6 +1243,7 @@ void InitNet(Handle<Object> 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);

0 comments on commit 0a8bd34

Please sign in to comment.