diff --git a/doc/api/dns.markdown b/doc/api/dns.markdown index d38265bbf2..6d5583a940 100644 --- a/doc/api/dns.markdown +++ b/doc/api/dns.markdown @@ -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 diff --git a/lib/dns.js b/lib/dns.js index e084fb5e5d..4572d253f1 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -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') { @@ -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 && @@ -121,13 +140,21 @@ 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 {}; } @@ -135,7 +162,7 @@ exports.lookup = function lookup(hostname, options, callback) { 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) { diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index 427337c413..b980b822c1 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -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); }); @@ -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;