net: don't prefer IPv4 addresses during resolution
Currently the address resolution family defaults to IPv4. Instead remove the preference and instead resolve to a family suitable for the host. Expose the getaddrinfo flags and allow them to be passed. Add documentation about new flags. Reviewed-by: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
parent
7da63a10ac
commit
430678640c
@ -31,12 +31,31 @@ resolves the IP addresses which are returned.
|
||||
});
|
||||
});
|
||||
|
||||
## dns.lookup(hostname, [family], callback)
|
||||
## dns.lookup(hostname, [options], callback)
|
||||
|
||||
Resolves a hostname (e.g. `'google.com'`) into the first found A (IPv4) or
|
||||
AAAA (IPv6) record.
|
||||
The `family` can be the integer `4` or `6`. Defaults to `null` that indicates
|
||||
both Ip v4 and v6 address family.
|
||||
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.
|
||||
|
||||
```
|
||||
{
|
||||
family: 4,
|
||||
hints: dns.ADDRCONFIG | dns.V4MAPPED
|
||||
}
|
||||
```
|
||||
|
||||
See [supported `getaddrinfo` flags](#dns_supported_getaddrinfo_flags) below for
|
||||
more information on supported flags.
|
||||
|
||||
The callback has arguments `(err, address, family)`. The `address` argument
|
||||
is a string representation of a IP v4 or v6 address. The `family` argument
|
||||
@ -120,7 +139,7 @@ of SRV records are priority, weight, port, and name (e.g.,
|
||||
|
||||
## dns.resolveSoa(hostname, callback)
|
||||
|
||||
The same as `dns.resolve()`, but only for start of authority record queries
|
||||
The same as `dns.resolve()`, but only for start of authority record queries
|
||||
(`SOA` record).
|
||||
|
||||
`addresses` is an object with the following structure:
|
||||
@ -201,3 +220,14 @@ Each DNS query can return one of the following error codes:
|
||||
- `dns.LOADIPHLPAPI`: Error loading iphlpapi.dll.
|
||||
- `dns.ADDRGETNETWORKPARAMS`: Could not find GetNetworkParams function.
|
||||
- `dns.CANCELLED`: DNS query cancelled.
|
||||
|
||||
## Supported getaddrinfo flags
|
||||
|
||||
The following flags can be passed as hints to `dns.lookup`.
|
||||
|
||||
- `dns.ADDRCONFIG`: Returned address types are determined by the types
|
||||
of addresses supported by the current system. For example, IPv4 addresses
|
||||
are only returned if the current system has at least one IPv4 address
|
||||
configured. Loopback addresses are not considered.
|
||||
- `dns.V4MAPPED`: If the IPv6 family was specified, but no IPv6 addresses
|
||||
were found, then return IPv4 mapped IPv6 addresses.
|
||||
|
42
lib/dns.js
42
lib/dns.js
@ -99,20 +99,37 @@ function onlookup(err, addresses) {
|
||||
|
||||
|
||||
// Easy DNS A/AAAA look up
|
||||
// lookup(hostname, [family,] callback)
|
||||
exports.lookup = function(hostname, family, callback) {
|
||||
// parse arguments
|
||||
if (arguments.length === 2) {
|
||||
callback = family;
|
||||
// lookup(hostname, [options,] callback)
|
||||
exports.lookup = function lookup(hostname, options, callback) {
|
||||
var hints = 0;
|
||||
var family;
|
||||
|
||||
// Parse arguments
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
family = 0;
|
||||
} else if (!family) {
|
||||
// Allow user to pass falsy values to options, and still pass callback.
|
||||
} else if (typeof callback !== 'function') {
|
||||
throw TypeError('invalid arguments: callback must be passed');
|
||||
} else if (!options) {
|
||||
family = 0;
|
||||
} else {
|
||||
family = +family;
|
||||
if (family !== 4 && family !== 6) {
|
||||
throw new Error('invalid argument: `family` must be 4 or 6');
|
||||
} else if (util.isObject(options)) {
|
||||
hints = options.hints >>> 0;
|
||||
family = options.family >>> 0;
|
||||
|
||||
if (hints !== 0 &&
|
||||
hints !== exports.ADDRCONFIG &&
|
||||
hints !== exports.V4MAPPED &&
|
||||
hints !== (exports.ADDRCONFIG | exports.V4MAPPED)) {
|
||||
throw new TypeError('invalid argument: hints must use valid flags');
|
||||
}
|
||||
} else {
|
||||
family = options >>> 0;
|
||||
}
|
||||
|
||||
if (family !== 0 && family !== 4 && family !== 6)
|
||||
throw new TypeError('invalid argument: family must be 4 or 6');
|
||||
|
||||
callback = makeAsync(callback);
|
||||
|
||||
if (!hostname) {
|
||||
@ -133,7 +150,7 @@ exports.lookup = function(hostname, family, callback) {
|
||||
oncomplete: onlookup
|
||||
};
|
||||
|
||||
var err = cares.getaddrinfo(req, hostname, family);
|
||||
var err = cares.getaddrinfo(req, hostname, family, hints);
|
||||
if (err) {
|
||||
callback(errnoException(err, 'getaddrinfo', hostname));
|
||||
return {};
|
||||
@ -290,6 +307,9 @@ exports.setServers = function(servers) {
|
||||
}
|
||||
};
|
||||
|
||||
// uv_getaddrinfo flags
|
||||
exports.ADDRCONFIG = cares.AI_ADDRCONFIG;
|
||||
exports.V4MAPPED = cares.AI_V4MAPPED;
|
||||
|
||||
// ERROR CODES
|
||||
exports.NODATA = 'ENODATA';
|
||||
|
13
lib/net.js
13
lib/net.js
@ -881,11 +881,20 @@ Socket.prototype.connect = function(options, cb) {
|
||||
connect(self, self._host, options.port, 4);
|
||||
|
||||
} else {
|
||||
var dns = require('dns');
|
||||
var host = options.host;
|
||||
var family = options.family || 4;
|
||||
var dnsopts = {
|
||||
family: options.family,
|
||||
hints: 0
|
||||
};
|
||||
|
||||
if (dnsopts.family !== 4 && dnsopts.family !== 6)
|
||||
dnsopts.hints = dns.ADDRCONFIG | dns.V4MAPPED;
|
||||
|
||||
debug('connect: find host ' + host);
|
||||
debug('connect: dns options ' + dnsopts);
|
||||
self._host = host;
|
||||
require('dns').lookup(host, family, function(err, ip, addressType) {
|
||||
dns.lookup(host, dnsopts, function(err, ip, addressType) {
|
||||
self.emit('lookup', err, ip, addressType);
|
||||
|
||||
// It's possible we were destroyed while looking this up.
|
||||
|
@ -1014,10 +1014,13 @@ static void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
|
||||
assert(args[0]->IsObject());
|
||||
assert(args[1]->IsString());
|
||||
assert(args[2]->IsInt32());
|
||||
assert(args[3]->IsInt32());
|
||||
Local<Object> req_wrap_obj = args[0].As<Object>();
|
||||
node::Utf8Value hostname(args[1]);
|
||||
|
||||
int family;
|
||||
int32_t flags = args[3]->Int32Value();
|
||||
|
||||
switch (args[2]->Int32Value()) {
|
||||
case 0:
|
||||
family = AF_UNSPEC;
|
||||
@ -1042,6 +1045,7 @@ static void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = flags;
|
||||
|
||||
int err = uv_getaddrinfo(env->event_loop(),
|
||||
&req_wrap->req_,
|
||||
@ -1246,6 +1250,10 @@ static void Initialize(Handle<Object> target,
|
||||
Integer::New(env->isolate(), AF_INET6));
|
||||
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_UNSPEC"),
|
||||
Integer::New(env->isolate(), AF_UNSPEC));
|
||||
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_ADDRCONFIG"),
|
||||
Integer::New(env->isolate(), AI_ADDRCONFIG));
|
||||
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_V4MAPPED"),
|
||||
Integer::New(env->isolate(), AI_V4MAPPED));
|
||||
}
|
||||
|
||||
} // namespace cares_wrap
|
||||
|
@ -337,6 +337,36 @@ TEST(function test_lookup_ipv4_implicit(done) {
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_ipv4_explicit_object(done) {
|
||||
var req = dns.lookup('www.google.com', {
|
||||
family: 4
|
||||
}, function(err, ip, family) {
|
||||
if (err) throw err;
|
||||
assert.ok(net.isIPv4(ip));
|
||||
assert.strictEqual(family, 4);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_ipv4_hint_addrconfig(done) {
|
||||
var req = dns.lookup('www.google.com', {
|
||||
hint: dns.ADDRCONFIG
|
||||
}, function(err, ip, family) {
|
||||
if (err) throw err;
|
||||
assert.ok(net.isIPv4(ip));
|
||||
assert.strictEqual(family, 4);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_ipv6_explicit(done) {
|
||||
var req = dns.lookup('ipv6.google.com', 6, function(err, ip, family) {
|
||||
if (err) throw err;
|
||||
@ -365,6 +395,36 @@ TEST(function test_lookup_ipv6_implicit(done) {
|
||||
*/
|
||||
|
||||
|
||||
TEST(function test_lookup_ipv6_explicit_object(done) {
|
||||
var req = dns.lookup('ipv6.google.com', {
|
||||
family: 6
|
||||
}, function(err, ip, family) {
|
||||
if (err) throw err;
|
||||
assert.ok(net.isIPv6(ip));
|
||||
assert.strictEqual(family, 6);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_ipv6_hint(done) {
|
||||
var req = dns.lookup('ipv6.google.com', {
|
||||
hint: dns.V4MAPPED
|
||||
}, function(err, ip, family) {
|
||||
if (err) throw err;
|
||||
assert.ok(net.isIPv6(ip));
|
||||
assert.strictEqual(family, 6);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_failure(done) {
|
||||
var req = dns.lookup('does.not.exist', 4, function(err, ip, family) {
|
||||
assert.ok(err instanceof Error);
|
||||
|
@ -27,6 +27,8 @@ var dns = require('dns');
|
||||
var existing = dns.getServers();
|
||||
assert(existing.length);
|
||||
|
||||
function noop() {}
|
||||
|
||||
var goog = [
|
||||
'8.8.8.8',
|
||||
'8.8.4.4',
|
||||
@ -61,12 +63,54 @@ assert.deepEqual(dns.getServers(), portsExpected);
|
||||
assert.doesNotThrow(function () { dns.setServers([]); });
|
||||
assert.deepEqual(dns.getServers(), []);
|
||||
|
||||
assert.throws(
|
||||
function() {
|
||||
dns.resolve('test.com', [], new Function);
|
||||
},
|
||||
function(err) {
|
||||
return !(err instanceof TypeError);
|
||||
},
|
||||
"Unexpected error"
|
||||
);
|
||||
assert.throws(function() {
|
||||
dns.resolve('test.com', [], noop);
|
||||
}, function(err) {
|
||||
return !(err instanceof TypeError);
|
||||
}, 'Unexpected error');
|
||||
|
||||
assert.throws(function() {
|
||||
dns.lookup('www.google.com', { hints: 1 }, noop);
|
||||
});
|
||||
|
||||
assert.throws(function() {
|
||||
dns.lookup('www.google.com');
|
||||
}, 'invalid arguments: callback must be passed');
|
||||
|
||||
assert.throws(function() {
|
||||
dns.lookup('www.google.com', 4);
|
||||
}, 'invalid arguments: callback must be passed');
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
dns.lookup('www.google.com', 6, noop);
|
||||
});
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
dns.lookup('www.google.com', {}, noop);
|
||||
});
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
dns.lookup('www.google.com', {
|
||||
family: 4,
|
||||
hints: 0
|
||||
}, noop);
|
||||
});
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
dns.lookup('www.google.com', {
|
||||
family: 6,
|
||||
hints: dns.ADDRCONFIG
|
||||
}, noop);
|
||||
});
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
dns.lookup('www.google.com', {
|
||||
hints: dns.V4MAPPED
|
||||
}, noop);
|
||||
});
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
dns.lookup('www.google.com', {
|
||||
hints: dns.ADDRCONFIG | dns.V4MAPPED
|
||||
}, noop);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user