diff --git a/doc/api/dns.md b/doc/api/dns.md index 94e64864b64070..982263507af7ca 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -59,8 +59,21 @@ the [Implementation considerations section][] for more information. added: v0.11.3 --> -Returns an array of IP address strings that are being used for name -resolution. +Returns an array of IP address strings, formatted according to [rfc3986][], +that are currently configured for DNS resolution. A string will include a port +section if a custom port is used. + +eg. + + +```js +[ + '4.4.4.4', + '2001:4860:4860::8888', + '4.4.4.4:1053', + '[2001:4860:4860::8888]:1053' +] +``` ## dns.lookup(hostname[, options], callback) -- `servers` {string[]} +- `servers` {string[]} array of [rfc3986][] formatted addresses + +Sets the IP address and port of servers to be used when performing DNS +resolution. The `servers` argument is an array of [rfc3986][] formatted +addresses. If the port is the IANA default DNS port (53) it can be omitted. -Sets the IP addresses of the servers to be used when resolving. The `servers` -argument is an array of IPv4 or IPv6 addresses. +eg. -If a port is specified on the address, it will be removed. +```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. @@ -583,3 +606,4 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_. [supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags [the official libuv documentation]: http://docs.libuv.org/en/latest/threadpool.html [`util.promisify()`]: util.html#util_util_promisify_original +[rfc3986]: https://tools.ietf.org/html/rfc3986#section-3.2.2 diff --git a/lib/dns.js b/lib/dns.js index e2af27863e7f67..8f84a079502b36 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -60,6 +60,7 @@ function errnoException(err, syscall, hostname) { return ex; } +const IANA_DNS_PORT = 53; 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, // 16-31 @@ -301,7 +302,13 @@ function resolve(hostname, rrtype, callback) { 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 newSet = []; const IPv6RE = /\[(.*)\]/; - const addrSplitRE = /:\d+$/; + const addrSplitRE = /(^.+?)(?::(\d+))?$/; servers.forEach((serv) => { var ipVersion = isIP(serv); if (ipVersion !== 0) - return newSet.push([ipVersion, serv]); + return newSet.push([ipVersion, serv, IANA_DNS_PORT]); const match = serv.match(IPv6RE); // we have an IPv6 in brackets if (match) { ipVersion = isIP(match[1]); - if (ipVersion !== 0) - return newSet.push([ipVersion, match[1]]); + if (ipVersion !== 0) { + 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); - if (ipVersion !== 0) - return newSet.push([ipVersion, s]); + if (ipVersion !== 0) { + return newSet.push([ipVersion, s, parseInt(p)]); + } throw new Error(`IP address is not properly formatted: ${serv}`); }); diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index bd340f0d920a88..41a5633ee022f1 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -442,9 +442,9 @@ void AresEnsureServers(Environment* env) { } 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 (servers == nullptr) return; @@ -456,7 +456,9 @@ void AresEnsureServers(Environment* env) { /* if the only server is not 127.0.0.1, ignore */ 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); env->set_cares_is_servers_default(false); return; @@ -1924,12 +1926,12 @@ void GetServers(const FunctionCallbackInfo& args) { Local 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); - ares_addr_node* cur = servers; + ares_addr_port_node* cur = servers; for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) { char ip[INET6_ADDRSTRLEN]; @@ -1938,8 +1940,11 @@ void GetServers(const FunctionCallbackInfo& args) { int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); CHECK_EQ(err, 0); - Local addr = OneByteString(env->isolate(), ip); - server_array->Set(i, addr); + Local ret = Array::New(env->isolate(), 2); + 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); @@ -1962,8 +1967,8 @@ void SetServers(const FunctionCallbackInfo& args) { return args.GetReturnValue().Set(rv); } - ares_addr_node* servers = new ares_addr_node[len]; - ares_addr_node* last = nullptr; + ares_addr_port_node* servers = new ares_addr_port_node[len]; + ares_addr_port_node* last = nullptr; int err; @@ -1974,12 +1979,15 @@ void SetServers(const FunctionCallbackInfo& args) { CHECK(elm->Get(0)->Int32Value()); CHECK(elm->Get(1)->IsString()); + CHECK(elm->Get(2)->Int32Value()); int fam = elm->Get(0)->Int32Value(); 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) { case 4: cur->family = AF_INET; @@ -2005,7 +2013,7 @@ void SetServers(const FunctionCallbackInfo& args) { } if (err == 0) - err = ares_set_servers(env->cares_channel(), &servers[0]); + err = ares_set_servers_ports(env->cares_channel(), &servers[0]); else err = ARES_EBADSTR; diff --git a/test/parallel/test-dns.js b/test/parallel/test-dns.js index a605e76c1d4f16..175ac102984288 100644 --- a/test/parallel/test-dns.js +++ b/test/parallel/test-dns.js @@ -35,6 +35,8 @@ assert.doesNotThrow(() => { servers[0] = '127.0.0.1'; servers[2] = '0.0.0.0'; dns.setServers(servers); + + assert.deepStrictEqual(dns.getServers(), ['127.0.0.1', '0.0.0.0']); }); assert.doesNotThrow(() => { @@ -53,6 +55,11 @@ assert.doesNotThrow(() => { }); dns.setServers(servers); + assert.deepStrictEqual(dns.getServers(), [ + '127.0.0.1', + '192.168.1.1', + '0.0.0.0' + ]); }); const goog = [ @@ -63,6 +70,8 @@ assert.doesNotThrow(() => dns.setServers(goog)); assert.deepStrictEqual(dns.getServers(), goog); assert.throws(() => dns.setServers(['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); const goog6 = [ @@ -79,10 +88,14 @@ assert.deepStrictEqual(dns.getServers(), goog6); const ports = [ '4.4.4.4:53', '[2001:4860:4860::8888]:53', + '103.238.225.181:666', + '[fe80::483a:5aff:fee6:1f04]:666' ]; const portsExpected = [ '4.4.4.4', '2001:4860:4860::8888', + '103.238.225.181:666', + '[fe80::483a:5aff:fee6:1f04]:666' ]; dns.setServers(ports); assert.deepStrictEqual(dns.getServers(), portsExpected);