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);
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, []);
+ }
+ });
+ }
+ });
+ }
}
};
#include <fcntl.h>
#include <arpa/inet.h> /* inet_pton */
+#include <netdb.h>
+
#include <netinet/in.h>
#include <netinet/tcp.h>
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;
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);