dns: allow dns.lookup() to return all addresses
authorRoman Reiss <me@silverwind.io>
Fri, 6 Feb 2015 20:43:50 +0000 (21:43 +0100)
committercjihrig <cjihrig@gmail.com>
Fri, 6 Feb 2015 22:18:47 +0000 (17:18 -0500)
This commit adds the 'all' option to dns.lookup(), allowing
all lookup results to be returned.

Semver: Minor
Fixes: https://github.com/iojs/io.js/issues/736
PR-URL: https://github.com/iojs/io.js/pull/744
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
doc/api/dns.markdown
lib/dns.js
test/internet/test-dns.js

index d38265b..6d5583a 100644 (file)
@@ -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
index e084fb5..4572d25 100644 (file)
@@ -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) {
index 427337c..b980b82 100644 (file)
@@ -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;