diff --git a/doc/api.markdown b/doc/api.markdown index 890d38b689..162321e61b 100644 --- a/doc/api.markdown +++ b/doc/api.markdown @@ -2515,6 +2515,17 @@ resolves the IP addresses which are returned. }); }); +### dns.lookup(domain, family=null, callback) + +Resolves a domain (e.g. `'google.com'`) into the first found A (IPv4) or +AAAA (IPv6) record. + +The callback has arguments `(err, address, family)`. The `address` argument +is a string representation of a IP v4 or v6 address. The `family` argument +is either the integer 4 or 6 and denotes the family of `address` (not +neccessarily the value initially passed to `lookup`). + + ### dns.resolve(domain, rrtype='A', callback) Resolves a domain (e.g. `'google.com'`) into an array of the record types diff --git a/lib/dgram.js b/lib/dgram.js index 9c27c8b4d8..c6eab71878 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -28,6 +28,16 @@ function getPool() { return pool; } +function dnsLookup(type, hostname, callback) { + var family = (type ? ((type === "udp6") ? 6 : 4) : null); + dns.lookup(hostname, family, function (err, ip, addressFamily) { + if (!err && family && addressFamily !== family) { + err = new Error('no address found in family '+type+' for '+hostname); + } + callback(err, ip, addressFamily); + }); +} + function Socket (type, listener) { events.EventEmitter.call(this); var self = this; @@ -103,22 +113,15 @@ Socket.prototype.bind = function () { } else { // the first argument is the port, the second an address this.port = arguments[0]; - if (dns.isIP(arguments[1])) { - this.address = arguments[1]; - binding.bind(self.fd, port, arguments[1]); - this.emit("listening"); - } else { - dns.lookup(arguments[1], function (err, ip, addressType) { - // TODO - only look up proper record for address family - if (err) { - self.emit('error', err); - } else { - self.ip = ip; - binding.bind(self.fd, self.port, ip); - self.emit("listening"); - } - }); - } + dnsLookup(this.type, arguments[1], function (err, ip, addressFamily) { + if (err) { + self.emit('error', err); + } else { + self.ip = ip; + binding.bind(self.fd, self.port, ip); + self.emit("listening"); + } + }); } } }; @@ -153,7 +156,7 @@ Socket.prototype.setTTL = function(arg) { } }; -// translate arguments from JS API into C++ API, after optional DNS lookup +// translate arguments from JS API into C++ API, possibly after DNS lookup Socket.prototype.send = function(buffer, offset, length) { var self = this; @@ -173,13 +176,12 @@ Socket.prototype.send = function(buffer, offset, length) { } if (dns.isIP(arguments[4])) { - self.sendto(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); + self.sendto(arguments[0], arguments[1], arguments[2], arguments[3], + arguments[4], arguments[5]); } else { var port = arguments[3], callback = arguments[5]; - - // TODO - only look up proper record for address family - dns.lookup(arguments[4], function (err, ip, addressType) { + dnsLookup(this.type, arguments[4], function (err, ip, addressFamily) { if (err) { // DNS error if (callback) { callback(err); diff --git a/lib/dns.js b/lib/dns.js index afd5cb1fcc..84d5d3b521 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -100,46 +100,93 @@ exports.getHostByName = function (domain, callback) { var net; // Easy DNS A/AAAA look up -exports.lookup = function (domain, callback) { - var addressType = dns.isIP(domain); - if (addressType) { - process.nextTick(function () { - callback(null, domain, addressType); - }); +// lookup(domain, [family,] callback) +exports.lookup = function (domain, family, callback) { + if (arguments.length === 2) { + callback = family; + family = undefined; + } else if (family && family !== 4 && family !== 6) { + family = parseInt(family); + if (family === dns.AF_INET) { + family = 4; + } else if (family === dns.AF_INET6) { + family = 6; + } else if (family !== 4 && family !== 6) { + throw new Error('invalid argument: "family" must be 4 or 6'); + } + } + if (!domain) { + callback(null, null, family === 6 ? 6 : 4); + return; + } + var matchedFamily = dns.isIP(domain); + if (matchedFamily) { + callback(null, domain, matchedFamily); } else { - if (/\w\.local\.?$/.test(domain) ) { + 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); + if (family) { + // resolve names for explicit address family + var af = (family === 4) ? dns.AF_INET : dns.AF_INET6; + channel.getHostByName(domain, af, function (err, domains) { + if (!err && domains && domains.length) { + if (family !== dns.isIP(domains[0])) { + callback(new Error('not found'), []); } else { - callback(err, []); + callback(null, domains[0], family); } - }); - } - }); + } else { + callback(err, []); + } + }); + } else { + // first resolve names for v4 and if that fails, try v6 + 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, []); + } + }); + } + }); + } } } }; -exports.resolve4 = function(domain, callback) { channel.query(domain, dns.A, callback) }; -exports.resolve6 = function(domain, callback) { channel.query(domain, dns.AAAA, callback) }; -exports.resolveMx = function(domain, callback) { channel.query(domain, dns.MX, callback) }; -exports.resolveTxt = function(domain, callback) { channel.query(domain, dns.TXT, callback) }; -exports.resolveSrv = function(domain, callback) { channel.query(domain, dns.SRV, callback) }; -exports.reverse = function(domain, callback) { channel.query(domain, dns.PTR, callback) }; -exports.resolveNs = function(domain, callback) { channel.query(domain, dns.NS, callback) }; - +exports.resolve4 = function(domain, callback) { + channel.query(domain, dns.A, callback); +}; +exports.resolve6 = function(domain, callback) { + channel.query(domain, dns.AAAA, callback); +}; +exports.resolveMx = function(domain, callback) { + channel.query(domain, dns.MX, callback); +}; +exports.resolveTxt = function(domain, callback) { + channel.query(domain, dns.TXT, callback); +}; +exports.resolveSrv = function(domain, callback) { + channel.query(domain, dns.SRV, callback); +}; +exports.reverse = function(domain, callback) { + channel.query(domain, dns.PTR, callback); +}; +exports.resolveNs = function(domain, callback) { + channel.query(domain, dns.NS, callback); +}; var resolveMap = { 'A' : exports.resolve4,