dns: make dns.setServers support customized port

allow `dns.setServers` parameter to contain port

e.g.

```
dns.setServers([ '103.238.225.181:666' ]);
```

And `dns.getServers` will return IP with port if not the default port.

PR-URL: https://github.com/nodejs/node/pull/13723
Refs: https://github.com/nodejs/node/issues/7903
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
XadillaX 2017-06-16 13:27:21 -04:00 committed by Refael Ackermann
parent 1fcb76e8f2
commit 330349f706
4 changed files with 83 additions and 26 deletions

View File

@ -59,8 +59,21 @@ the [Implementation considerations section][] for more information.
added: v0.11.3 added: v0.11.3
--> -->
Returns an array of IP address strings that are being used for name Returns an array of IP address strings, formatted according to [rfc5952][],
resolution. that are currently configured for DNS resolution. A string will include a port
section if a custom port is used.
For example:
<!-- eslint-disable -->
```js
[
'4.4.4.4',
'2001:4860:4860::8888',
'4.4.4.4:1053',
'[2001:4860:4860::8888]:1053'
]
```
## dns.lookup(hostname[, options], callback) ## dns.lookup(hostname[, options], callback)
<!-- YAML <!-- YAML
@ -482,12 +495,22 @@ one of the [DNS error codes][].
<!-- YAML <!-- YAML
added: v0.11.3 added: v0.11.3
--> -->
- `servers` {string[]} - `servers` {string[]} array of [rfc5952][] formatted addresses
Sets the IP addresses of the servers to be used when resolving. The `servers` Sets the IP address and port of servers to be used when performing DNS
argument is an array of IPv4 or IPv6 addresses. resolution. The `servers` argument is an array of [rfc5952][] formatted
addresses. If the port is the IANA default DNS port (53) it can be omitted.
If a port is specified on the address, it will be removed. For example:
```js
dns.setServers([
'4.4.4.4',
'[2001:4860:4860::8888]',
'4.4.4.4:1053',
'[2001:4860:4860::8888]:1053'
]);
```
An error will be thrown if an invalid address is provided. An error will be thrown if an invalid address is provided.
@ -583,3 +606,4 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
[supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags [supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags
[the official libuv documentation]: http://docs.libuv.org/en/latest/threadpool.html [the official libuv documentation]: http://docs.libuv.org/en/latest/threadpool.html
[`util.promisify()`]: util.html#util_util_promisify_original [`util.promisify()`]: util.html#util_util_promisify_original
[rfc5952]: https://tools.ietf.org/html/rfc5952#section-6

View File

@ -60,6 +60,7 @@ function errnoException(err, syscall, hostname) {
return ex; return ex;
} }
const IANA_DNS_PORT = 53;
const digits = [ const digits = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
@ -301,7 +302,13 @@ function resolve(hostname, rrtype, callback) {
function getServers() { function getServers() {
return cares.getServers(); const ret = cares.getServers();
return ret.map((val) => {
if (!val[1] || val[1] === IANA_DNS_PORT) return val[0];
const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0];
return `${host}:${val[1]}`;
});
} }
@ -311,26 +318,31 @@ function setServers(servers) {
const orig = cares.getServers(); const orig = cares.getServers();
const newSet = []; const newSet = [];
const IPv6RE = /\[(.*)\]/; const IPv6RE = /\[(.*)\]/;
const addrSplitRE = /:\d+$/; const addrSplitRE = /(^.+?)(?::(\d+))?$/;
servers.forEach((serv) => { servers.forEach((serv) => {
var ipVersion = isIP(serv); var ipVersion = isIP(serv);
if (ipVersion !== 0) if (ipVersion !== 0)
return newSet.push([ipVersion, serv]); return newSet.push([ipVersion, serv, IANA_DNS_PORT]);
const match = serv.match(IPv6RE); const match = serv.match(IPv6RE);
// we have an IPv6 in brackets // we have an IPv6 in brackets
if (match) { if (match) {
ipVersion = isIP(match[1]); ipVersion = isIP(match[1]);
if (ipVersion !== 0) if (ipVersion !== 0) {
return newSet.push([ipVersion, match[1]]); const port =
parseInt(serv.replace(addrSplitRE, '$2')) ||
IANA_DNS_PORT;
return newSet.push([ipVersion, match[1], port]);
}
} }
const s = serv.split(addrSplitRE)[0]; const [, s, p] = serv.match(addrSplitRE);
ipVersion = isIP(s); ipVersion = isIP(s);
if (ipVersion !== 0) if (ipVersion !== 0) {
return newSet.push([ipVersion, s]); return newSet.push([ipVersion, s, parseInt(p)]);
}
throw new Error(`IP address is not properly formatted: ${serv}`); throw new Error(`IP address is not properly formatted: ${serv}`);
}); });

View File

@ -442,9 +442,9 @@ void AresEnsureServers(Environment* env) {
} }
ares_channel channel = env->cares_channel(); ares_channel channel = env->cares_channel();
ares_addr_node* servers = nullptr; ares_addr_port_node* servers = nullptr;
ares_get_servers(channel, &servers); ares_get_servers_ports(channel, &servers);
/* if no server or multi-servers, ignore */ /* if no server or multi-servers, ignore */
if (servers == nullptr) return; if (servers == nullptr) return;
@ -456,7 +456,9 @@ void AresEnsureServers(Environment* env) {
/* if the only server is not 127.0.0.1, ignore */ /* if the only server is not 127.0.0.1, ignore */
if (servers[0].family != AF_INET || if (servers[0].family != AF_INET ||
servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK)) { servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK) ||
servers[0].tcp_port != 0 ||
servers[0].udp_port != 0) {
ares_free_data(servers); ares_free_data(servers);
env->set_cares_is_servers_default(false); env->set_cares_is_servers_default(false);
return; return;
@ -1924,12 +1926,12 @@ void GetServers(const FunctionCallbackInfo<Value>& args) {
Local<Array> server_array = Array::New(env->isolate()); Local<Array> server_array = Array::New(env->isolate());
ares_addr_node* servers; ares_addr_port_node* servers;
int r = ares_get_servers(env->cares_channel(), &servers); int r = ares_get_servers_ports(env->cares_channel(), &servers);
CHECK_EQ(r, ARES_SUCCESS); CHECK_EQ(r, ARES_SUCCESS);
ares_addr_node* cur = servers; ares_addr_port_node* cur = servers;
for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) { for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) {
char ip[INET6_ADDRSTRLEN]; char ip[INET6_ADDRSTRLEN];
@ -1938,8 +1940,11 @@ void GetServers(const FunctionCallbackInfo<Value>& args) {
int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip));
CHECK_EQ(err, 0); CHECK_EQ(err, 0);
Local<String> addr = OneByteString(env->isolate(), ip); Local<Array> ret = Array::New(env->isolate(), 2);
server_array->Set(i, addr); ret->Set(0, OneByteString(env->isolate(), ip));
ret->Set(1, Integer::New(env->isolate(), cur->udp_port));
server_array->Set(i, ret);
} }
ares_free_data(servers); ares_free_data(servers);
@ -1962,8 +1967,8 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
return args.GetReturnValue().Set(rv); return args.GetReturnValue().Set(rv);
} }
ares_addr_node* servers = new ares_addr_node[len]; ares_addr_port_node* servers = new ares_addr_port_node[len];
ares_addr_node* last = nullptr; ares_addr_port_node* last = nullptr;
int err; int err;
@ -1974,12 +1979,15 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
CHECK(elm->Get(0)->Int32Value()); CHECK(elm->Get(0)->Int32Value());
CHECK(elm->Get(1)->IsString()); CHECK(elm->Get(1)->IsString());
CHECK(elm->Get(2)->Int32Value());
int fam = elm->Get(0)->Int32Value(); int fam = elm->Get(0)->Int32Value();
node::Utf8Value ip(env->isolate(), elm->Get(1)); node::Utf8Value ip(env->isolate(), elm->Get(1));
int port = elm->Get(2)->Int32Value();
ares_addr_node* cur = &servers[i]; ares_addr_port_node* cur = &servers[i];
cur->tcp_port = cur->udp_port = port;
switch (fam) { switch (fam) {
case 4: case 4:
cur->family = AF_INET; cur->family = AF_INET;
@ -2005,7 +2013,7 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
} }
if (err == 0) if (err == 0)
err = ares_set_servers(env->cares_channel(), &servers[0]); err = ares_set_servers_ports(env->cares_channel(), &servers[0]);
else else
err = ARES_EBADSTR; err = ARES_EBADSTR;

View File

@ -35,6 +35,8 @@ assert.doesNotThrow(() => {
servers[0] = '127.0.0.1'; servers[0] = '127.0.0.1';
servers[2] = '0.0.0.0'; servers[2] = '0.0.0.0';
dns.setServers(servers); dns.setServers(servers);
assert.deepStrictEqual(dns.getServers(), ['127.0.0.1', '0.0.0.0']);
}); });
assert.doesNotThrow(() => { assert.doesNotThrow(() => {
@ -53,6 +55,11 @@ assert.doesNotThrow(() => {
}); });
dns.setServers(servers); dns.setServers(servers);
assert.deepStrictEqual(dns.getServers(), [
'127.0.0.1',
'192.168.1.1',
'0.0.0.0'
]);
}); });
const goog = [ const goog = [
@ -63,6 +70,8 @@ assert.doesNotThrow(() => dns.setServers(goog));
assert.deepStrictEqual(dns.getServers(), goog); assert.deepStrictEqual(dns.getServers(), goog);
assert.throws(() => dns.setServers(['foobar']), assert.throws(() => dns.setServers(['foobar']),
/^Error: IP address is not properly formatted: foobar$/); /^Error: IP address is not properly formatted: foobar$/);
assert.throws(() => dns.setServers(['127.0.0.1:va']),
/^Error: IP address is not properly formatted: 127\.0\.0\.1:va$/);
assert.deepStrictEqual(dns.getServers(), goog); assert.deepStrictEqual(dns.getServers(), goog);
const goog6 = [ const goog6 = [
@ -79,10 +88,14 @@ assert.deepStrictEqual(dns.getServers(), goog6);
const ports = [ const ports = [
'4.4.4.4:53', '4.4.4.4:53',
'[2001:4860:4860::8888]:53', '[2001:4860:4860::8888]:53',
'103.238.225.181:666',
'[fe80::483a:5aff:fee6:1f04]:666'
]; ];
const portsExpected = [ const portsExpected = [
'4.4.4.4', '4.4.4.4',
'2001:4860:4860::8888', '2001:4860:4860::8888',
'103.238.225.181:666',
'[fe80::483a:5aff:fee6:1f04]:666'
]; ];
dns.setServers(ports); dns.setServers(ports);
assert.deepStrictEqual(dns.getServers(), portsExpected); assert.deepStrictEqual(dns.getServers(), portsExpected);