Skip to content

Commit

Permalink
dns: add the 'all' option to dns.lookup
Browse files Browse the repository at this point in the history
The 'all' option allows to retrieve all addresses returned by
`getaddrinfo` using the system resolver. While `dns.resolve`
already does return multiple results, it's often preferable to
use the system resolver, like for mDNS.
  • Loading branch information
silverwind committed Feb 6, 2015
1 parent e7573f9 commit 0a914bf
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 27 deletions.
36 changes: 22 additions & 14 deletions doc/api/dns.markdown
Expand Up @@ -61,29 +61,37 @@ AAAA (IPv6) record. `options` can be an object or integer. If `options` is
not provided, then IP v4 and v6 addresses are both valid. If `options` is
an integer, then it must be `4` or `6`.

Alternatively, `options` can be an object containing two properties,
`family` and `hints`. Both properties are optional. If `family` is provided,
it must be the integer `4` or `6`. If `family` is not provided then IP v4
and v6 addresses are accepted. The `hints` field, if present, should be one
or more of the supported `getaddrinfo` flags. If `hints` is not provided,
then no flags are passed to `getaddrinfo`. Multiple flags can be passed
through `hints` by logically `OR`ing their values. An example usage of
`options` is shown below.
Alternatively, `options` can be an object containing these properties:

* `family` {Number} - The record family. If present, must be the integer
`4` or `6`. If not provided, both IP v4 and v6 addresses are accepted.
* `hints`: {Number} - If present, it should be one or more of the supported
`getaddrinfo` flags. If `hints` is not provided, then no flags are passed to
`getaddrinfo`. Multiple flags can be passed through `hints` by logically
`OR`ing their values.
See [supported `getaddrinfo` flags](#dns_supported_getaddrinfo_flags) below
for more information on supported flags.
* `all`: {Boolean} - When `true`, the callback returns all resolved addresses
in an array, otherwise returns a single address. Defaults to `false`.

All properties are optional. An example usage of options is shown below.

```
{
family: 4,
hints: dns.ADDRCONFIG | dns.V4MAPPED
all: true
}
```

See [supported `getaddrinfo` flags](#dns_supported_getaddrinfo_flags) below for
more information on supported flags.
The callback has arguments `(err, address, family)`. `address` is a string
representation of a IP v4 or v6 address. `family` is either the integer 4 or 6
and denotes the family of `address` (not necessarily the value initially passed
to `lookup`).

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`).
With the `all` option set, the arguments change to `(err, addresses)`, with
`addresses` being an array of objects with the properties `address` and
`family`.

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
33 changes: 30 additions & 3 deletions lib/dns.js
Expand Up @@ -82,11 +82,29 @@ function onlookup(err, addresses) {
}


function onlookupall(err, addresses) {
var results = [];
if (err) {
return this.callback(errnoException(err, 'getaddrinfo', this.hostname));
}

for (var i = 0; i < addresses.length; i++) {
results.push({
address: addresses[i],
family: this.family || (addresses[i].indexOf(':') >= 0 ? 6 : 4)
});
}

this.callback(null, results);
}


// Easy DNS A/AAAA look up
// lookup(hostname, [options,] callback)
exports.lookup = function lookup(hostname, options, callback) {
var hints = 0;
var family = -1;
var all = false;

// Parse arguments
if (hostname && typeof hostname !== 'string') {
Expand All @@ -99,6 +117,7 @@ exports.lookup = function lookup(hostname, options, callback) {
} else if (options !== null && typeof options === 'object') {
hints = options.hints >>> 0;
family = options.family >>> 0;
all = options.all === true;

if (hints !== 0 &&
hints !== exports.ADDRCONFIG &&
Expand All @@ -121,21 +140,29 @@ exports.lookup = function lookup(hostname, options, callback) {
callback = makeAsync(callback);

if (!hostname) {
callback(null, null, family === 6 ? 6 : 4);
if (all) {
callback(null, []);
} else {
callback(null, null, family === 6 ? 6 : 4);
}
return {};
}

var matchedFamily = net.isIP(hostname);
if (matchedFamily) {
callback(null, hostname, matchedFamily);
if (all) {
callback(null, [{address: hostname, family: matchedFamily}]);
} else {
callback(null, hostname, matchedFamily);
}
return {};
}

var req = new GetAddrInfoReqWrap();
req.callback = callback;
req.family = family;
req.hostname = hostname;
req.oncomplete = onlookup;
req.oncomplete = all ? onlookupall : onlookup;

var err = cares.getaddrinfo(req, hostname, family, hints);
if (err) {
Expand Down
106 changes: 96 additions & 10 deletions test/internet/test-dns.js
Expand Up @@ -226,34 +226,34 @@ TEST(function test_resolveNaptr(done) {
TEST(function test_resolveSoa(done) {
var req = dns.resolveSoa('nodejs.org', function(err, result) {
if (err) throw err;

assert.ok(result);
assert.ok(typeof result === 'object');

assert.ok(typeof result.nsname === 'string');
assert.ok(result.nsname.length > 0);

assert.ok(typeof result.hostmaster === 'string');
assert.ok(result.hostmaster.length > 0);

assert.ok(typeof result.serial === 'number');
assert.ok((result.serial > 0) && (result.serial < 4294967295));

assert.ok(typeof result.refresh === 'number');
assert.ok((result.refresh > 0) && (result.refresh < 2147483647));
assert.ok((result.refresh > 0) && (result.refresh < 2147483647));

assert.ok(typeof result.retry === 'number');
assert.ok((result.retry > 0) && (result.retry < 2147483647));

assert.ok(typeof result.expire === 'number');
assert.ok((result.expire > 0) && (result.expire < 2147483647));

assert.ok(typeof result.minttl === 'number');
assert.ok((result.minttl >= 0) && (result.minttl < 2147483647));

done();
});

checkWrap(req);
});

Expand Down Expand Up @@ -471,6 +471,92 @@ TEST(function test_lookup_localhost_ipv4(done) {
});


TEST(function test_lookup_ip_all(done) {
var req = dns.lookup('127.0.0.1', {all: true}, function(err, ips, family) {
if (err) throw err;
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);
assert.strictEqual(ips[0].address, '127.0.0.1');
assert.strictEqual(ips[0].family, 4);

done();
});

checkWrap(req);
});


TEST(function test_lookup_null_all(done) {
var req = dns.lookup(null, {all: true}, function(err, ips, family) {
if (err) throw err;
assert.ok(Array.isArray(ips));
assert.strictEqual(ips.length, 0);

done();
});

checkWrap(req);
});


TEST(function test_lookup_all_ipv4(done) {
var req = dns.lookup('www.google.com', {all: true, family: 4}, function(err, ips) {
if (err) throw err;
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);

ips.forEach(function(ip) {
assert.ok(isIPv4(ip.address));
assert.strictEqual(ip.family, 4);
});

done();
});

checkWrap(req);
});


TEST(function test_lookup_all_ipv6(done) {
var req = dns.lookup('www.google.com', {all: true, family: 6}, function(err, ips) {
if (err) throw err;
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);

ips.forEach(function(ip) {
assert.ok(isIPv6(ip.address));
assert.strictEqual(ip.family, 6);
});

done();
});

checkWrap(req);
});


TEST(function test_lookup_all_mixed(done) {
var req = dns.lookup('www.google.com', {all: true}, function(err, ips) {
if (err) throw err;
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);

ips.forEach(function(ip) {
if (isIPv4(ip.address))
assert.equal(ip.family, 4);
else if (isIPv6(ip.address))
assert.equal(ip.family, 6);
else
assert(false);
});

done();
});

checkWrap(req);
});


TEST(function test_lookupservice_ip_ipv4(done) {
var req = dns.lookupService('127.0.0.1', 80, function(err, host, service) {
if (err) throw err;
Expand Down

0 comments on commit 0a914bf

Please sign in to comment.