summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2010-06-29 21:20:32 -0700
committerRyan Dahl <ry@tinyclouds.org>2010-06-29 22:15:28 -0700
commit0a8bd34b6993156da074ff89863f12e404e36ede (patch)
tree0da5b10e3cd756d12979dcaa93c7118e47a07baf
parent02ed0ec93bc8f6bcd7f1d629dca64ba1f335ea05 (diff)
downloadnodejs-0a8bd34b6993156da074ff89863f12e404e36ede.tar.gz
nodejs-0a8bd34b6993156da074ff89863f12e404e36ede.tar.bz2
nodejs-0a8bd34b6993156da074ff89863f12e404e36ede.zip
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.
-rw-r--r--lib/dns.js36
-rw-r--r--src/node_net.cc160
2 files changed, 183 insertions, 13 deletions
diff --git a/lib/dns.js b/lib/dns.js
index e57b9b4bf..52aa03427 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 bbc1540bc..cfeeb5141 100644
--- a/src/node_net.cc
+++ b/src/node_net.cc
@@ -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);