Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Implement dns.lookupAll() and use for net.connect() #4169

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 40 additions & 7 deletions doc/api/dns.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
Stability: 3 - Stable

Use `require('dns')` to access this module. All methods in the dns module
use C-Ares except for `dns.lookup` which uses `getaddrinfo(3)` in a thread
pool. C-Ares is much faster than `getaddrinfo` but the system resolver is
more constant with how other programs operate. When a user does
`net.connect(80, 'google.com')` or `http.get({ host: 'google.com' })` the
`dns.lookup` method is used. Users who need to do a large number of look ups
quickly should use the methods that go through C-Ares.
use C-Ares except for `dns.lookup` and `dns.lookupAll` which use
`getaddrinfo(3)` in a thread pool. C-Ares is much faster than `getaddrinfo`
but the system resolver is more constant with how other programs operate.
When a user does `net.connect(80, 'google.com')` or
`http.get({ host: 'google.com' })` the `dns.lookupAll` method is used.
Users who need to do a large number of look ups quickly should use the
methods that go through C-Ares.

Here is an example which resolves `'www.google.com'` then reverse
resolves the IP addresses which are returned.
Expand Down Expand Up @@ -41,7 +42,39 @@ both Ip v4 and v6 address family.
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
necessarily the value initially passed to `lookup`).
necessarily the value initially passed to `lookup`).

On error, `err` is an `Error` object, where `err.code` is the error code.
Keep in mind that `err.code` will be set to `'ENOENT'` not only when
the domain does not exist but also when the lookup fails in other ways
such as no available file descriptors.


## dns.lookupAll(domain, [family], callback)

The same as `dns.lookup()`, but returns all A (IPv4) or AAAA (IPv6) records for
`domain`.

The `family` can be the integer `4` or `6` to limit returned records to
either A (IPv4) or AAAA (IPv6), respectively.

The callback differs from `dns.lookup()` by receiving arguments `(err, addresses)`.
The `addresses` argument will be an array of address objects, each one with an `ip`
key and a `family` key denoting the address family (either `4` or `6`). Below is
an example of the `addresses` data returned for a lookup of `google.com`:

[ { ip: '74.125.237.130', family: 4 },
{ ip: '74.125.237.131', family: 4 },
{ ip: '74.125.237.132', family: 4 },
{ ip: '74.125.237.133', family: 4 },
{ ip: '74.125.237.134', family: 4 },
{ ip: '74.125.237.135', family: 4 },
{ ip: '74.125.237.136', family: 4 },
{ ip: '74.125.237.137', family: 4 },
{ ip: '74.125.237.142', family: 4 },
{ ip: '74.125.237.128', family: 4 },
{ ip: '74.125.237.129', family: 4 },
{ ip: '2404:6800:4006:804::1004', family: 6 } ]

On error, `err` is an `Error` object, where `err.code` is the error code.
Keep in mind that `err.code` will be set to `'ENOENT'` not only when
Expand Down
71 changes: 48 additions & 23 deletions lib/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,56 +75,81 @@ function makeAsync(callback) {
}


// Easy DNS A/AAAA look up
// lookup(domain, [family,] callback)
exports.lookup = function(domain, family, callback) {
// parse arguments
if (arguments.length === 2) {
callback = family;
family = 0;
function parseLookupOptions(domain, family, callback){
var options = { domain: domain, family: family, callback: callback };
if (typeof family === 'function'){
options.callback = family;
options.family = 0;
} else if (!family) {
family = 0;
options.family = 0;
} else {
family = +family;
if (family !== 4 && family !== 6) {
options.family = +family;
if (options.family !== 4 && options.family !== 6) {
throw new Error('invalid argument: `family` must be 4 or 6');
}
}
callback = makeAsync(callback);
return options;
}


// Easy DNS A/AAAA look up
// lookup(domain, [family,] callback)
// callback is called with (err, address, family)
exports.lookup = function(domain, family, callback) {
var options = parseLookupOptions(domain, family, callback);

if (!domain) {
callback(null, null, family === 6 ? 6 : 4);
return exports.lookupAll(options.domain, options.family, function(err, addresses){
if (err){
options.callback(err);
return;
}

var ip = addresses.length ? addresses[0].ip : null;
var fam = addresses.length ? addresses[0].family : options.family === 6 ? 6 : 4;
options.callback(null, ip, fam);
});
};


// Easy DNS A/AAAA look up, returning all addresses
// lookupAll(domain, [family,] callback)
// callback is called with (err, addresses)
exports.lookupAll = function(domain, family, callback) {
var options = parseLookupOptions(domain, family, callback);
callback = makeAsync(options.callback);

if (!options.domain) {
callback(null, []);
return {};
}

// Hack required for Windows because Win7 removed the
// localhost entry from c:\WINDOWS\system32\drivers\etc\hosts
// See http://daniel.haxx.se/blog/2011/02/21/localhost-hack-on-windows/
// TODO Remove this once c-ares handles this problem.
if (process.platform == 'win32' && domain == 'localhost') {
callback(null, '127.0.0.1', 4);
if (process.platform == 'win32' && options.domain == 'localhost') {
callback(null, [ { ip: '127.0.0.1', family: 4 } ]);
return {};
}

var matchedFamily = net.isIP(domain);
var matchedFamily = net.isIP(options.domain);
if (matchedFamily) {
callback(null, domain, matchedFamily);
callback(null, [ { ip: options.domain, family: matchedFamily } ]);
return {};
}

function onanswer(addresses) {
if (addresses) {
if (family) {
callback(null, addresses[0], family);
} else {
callback(null, addresses[0], addresses[0].indexOf(':') >= 0 ? 6 : 4);
}
// Return each address as a tuple of { ip: <addr>, family: <6/4> }
callback(null, addresses.map(function(addr){
return { ip: addr, family: net.isIP(addr) };
}));
} else {
callback(errnoException(errno, 'getaddrinfo'));
}
}

var wrap = cares.getaddrinfo(domain, family);
var wrap = cares.getaddrinfo(options.domain, options.family);

if (!wrap) {
throw errnoException(errno, 'getaddrinfo');
Expand Down
31 changes: 20 additions & 11 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ function connect(self, address, port, addressType, localAddress) {
if (connectReq !== null) {
connectReq.oncomplete = afterConnect;
} else {
self._destroy(errnoException(errno, 'connect'));
afterConnect(null, self._handle);
}
}

Expand Down Expand Up @@ -688,7 +688,7 @@ Socket.prototype.connect = function(options, cb) {
} else {
var host = options.host;
debug('connect: find host ' + host);
require('dns').lookup(host, function(err, ip, addressType) {
require('dns').lookupAll(host, function(err, addresses) {
// It's possible we were destroyed while looking this up.
// XXX it would be great if we could cancel the promise returned by
// the look up.
Expand All @@ -706,13 +706,12 @@ Socket.prototype.connect = function(options, cb) {
} else {
timers.active(self);

addressType = addressType || 4;
self._addresses = addresses;
self._port = options.port;
self._localAddress = options.localAddress;

// node_net.cc handles null host names graciously but user land
// expects remoteAddress to have a meaningful value
ip = ip || (addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1');

connect(self, ip, options.port, addressType, options.localAddress);
var nextAddr = self._addresses.shift();
connect(self, nextAddr.ip, self._port, nextAddr.family, self._localAddress);
}
});
}
Expand Down Expand Up @@ -746,9 +745,9 @@ function afterConnect(status, handle, req, readable, writable) {
debug('afterConnect');

assert.ok(self._connecting);
self._connecting = false;

if (status == 0) {
self._connecting = false;
self.readable = readable;
self.writable = writable;
timers.active(self);
Expand All @@ -774,8 +773,18 @@ function afterConnect(status, handle, req, readable, writable) {
self.end();
}
} else {
self._connectQueueCleanUp();
self._destroy(errnoException(errno, 'connect'));
if (self._addresses.length){
// Set up a new socket handle.
self._handle.close();
self._handle = createTCP();
initSocketHandle(self);

var nextAddr = self._addresses.shift();
connect(self, nextAddr.ip, self._port, nextAddr.family, self._localAddress);
} else {
self._connectQueueCleanUp();
self._destroy(errnoException(errno, 'connect'));
}
}
}

Expand Down