From b5b28e05a997d9cc103a6ddfe1eff6c89c397d94 Mon Sep 17 00:00:00 2001 From: XadillaX Date: Sat, 17 Jun 2017 01:27:21 +0800 Subject: [PATCH 1/5] dns: make `dns.setServers` support customized port Make `dns.setServers` parameter may be strings contain IP and port which split by `:`. eg. ``` dns.setServers([ "103.238.225.181:666" ]); ``` And `dns.getServers` will return IP with port if that server is not use default port. Refs: https://github.com/nodejs/node/issues/7903 --- lib/dns.js | 27 ++++++++++++++++++++------- src/cares_wrap.cc | 32 ++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index e2af27863e7f67..6c12ea7ce35726 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -301,7 +301,13 @@ function resolve(hostname, rrtype, callback) { function getServers() { - return cares.getServers(); + const ret = cares.getServers(); + return ret.map((val) => { + if (!val[1] || val[1] === 53) return val[0]; + + const ipVersion = isIP(val[0]); + return ipVersion === 6 ? `[${val[0]}]:${val[1]}` : `${val[0]}:${val[1]}`; + }); } @@ -311,26 +317,33 @@ 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, 53]); 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 portMatch = serv.match(addrSplitRE) || []; + return newSet.push([ipVersion, match[1], parseInt(portMatch[1] || 53)]); + } } const s = serv.split(addrSplitRE)[0]; ipVersion = isIP(s); - if (ipVersion !== 0) - return newSet.push([ipVersion, s]); + if (ipVersion !== 0) { + return newSet.push([ + ipVersion, + s, + parseInt(serv.substr(serv.indexOf(':') + 1)) + ]); + } 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; From 92f8619f62e387fa92ed89d4b9a7293cb01c1c7e Mon Sep 17 00:00:00 2001 From: XadillaX Date: Sat, 17 Jun 2017 01:51:56 +0800 Subject: [PATCH 2/5] update the test cases and documentation --- doc/api/dns.md | 32 +++++++++++++++++++++++++++----- test/parallel/test-dns.js | 11 +++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/doc/api/dns.md b/doc/api/dns.md index 94e64864b64070..d6cf5e6c67bd34 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -59,8 +59,20 @@ 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 and port strings that are being used for name +resolution, splited with `:`. If a server uses a default DNS server port, the +port part will be ignored in the result. + +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[]} -Sets the IP addresses of the servers to be used when resolving. The `servers` -argument is an array of IPv4 or IPv6 addresses. +Sets the IP addresses and port of the servers to be used when resolving. The +`servers` argument is an array of IPv4 or IPv6 addresses. If the port of a +server is the default DNS server port, this part can be ignored in the string. -If a port is specified on the address, it will be removed. +eg. + +```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. diff --git a/test/parallel/test-dns.js b/test/parallel/test-dns.js index a605e76c1d4f16..bd71971890e627 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 = [ @@ -79,10 +86,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); From 41da7fac40e932bc9a4c95df9fd5f4df797c5e4a Mon Sep 17 00:00:00 2001 From: XadillaX Date: Sat, 17 Jun 2017 03:14:57 +0800 Subject: [PATCH 3/5] update after reviewing --- doc/api/dns.md | 16 +++++++++------- lib/dns.js | 18 +++++++----------- test/parallel/test-dns.js | 2 ++ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/api/dns.md b/doc/api/dns.md index d6cf5e6c67bd34..4287e68cd0fc4b 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -59,12 +59,13 @@ the [Implementation considerations section][] for more information. added: v0.11.3 --> -Returns an array of IP address and port strings that are being used for name -resolution, splited with `:`. If a server uses a default DNS server port, the -port part will be ignored in the result. +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', @@ -494,11 +495,11 @@ one of the [DNS error codes][]. -- `servers` {string[]} +- `servers` {string[]} array of [rfc3986][] formatted addresses -Sets the IP addresses and port of the servers to be used when resolving. The -`servers` argument is an array of IPv4 or IPv6 addresses. If the port of a -server is the default DNS server port, this part can be ignored in the string. +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. eg. @@ -605,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 6c12ea7ce35726..11b7f2d91fe1a8 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -305,8 +305,8 @@ function getServers() { return ret.map((val) => { if (!val[1] || val[1] === 53) return val[0]; - const ipVersion = isIP(val[0]); - return ipVersion === 6 ? `[${val[0]}]:${val[1]}` : `${val[0]}:${val[1]}`; + const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0]; + return `${host}:${val[1]}`; }); } @@ -317,7 +317,7 @@ function setServers(servers) { const orig = cares.getServers(); const newSet = []; const IPv6RE = /\[(.*)\]/; - const addrSplitRE = /:(\d+)$/; + const addrSplitRE = /(^.+?)(?::(\d+))?$/; servers.forEach((serv) => { var ipVersion = isIP(serv); @@ -329,20 +329,16 @@ function setServers(servers) { if (match) { ipVersion = isIP(match[1]); if (ipVersion !== 0) { - const portMatch = serv.match(addrSplitRE) || []; - return newSet.push([ipVersion, match[1], parseInt(portMatch[1] || 53)]); + const port = parseInt(serv.replace(addrSplitRE, '$2')) || 53; + 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, - parseInt(serv.substr(serv.indexOf(':') + 1)) - ]); + return newSet.push([ipVersion, s, parseInt(p)]); } throw new Error(`IP address is not properly formatted: ${serv}`); diff --git a/test/parallel/test-dns.js b/test/parallel/test-dns.js index bd71971890e627..175ac102984288 100644 --- a/test/parallel/test-dns.js +++ b/test/parallel/test-dns.js @@ -70,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 = [ From b5f8bba0e149175a4a3bd38aedf4f2493f8f4ea7 Mon Sep 17 00:00:00 2001 From: XadillaX Date: Tue, 20 Jun 2017 00:50:07 +0800 Subject: [PATCH 4/5] make 53 be const --- lib/dns.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index 11b7f2d91fe1a8..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 @@ -303,7 +304,7 @@ function resolve(hostname, rrtype, callback) { function getServers() { const ret = cares.getServers(); return ret.map((val) => { - if (!val[1] || val[1] === 53) return val[0]; + 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]}`; @@ -322,14 +323,16 @@ function setServers(servers) { servers.forEach((serv) => { var ipVersion = isIP(serv); if (ipVersion !== 0) - return newSet.push([ipVersion, serv, 53]); + 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) { - const port = parseInt(serv.replace(addrSplitRE, '$2')) || 53; + const port = + parseInt(serv.replace(addrSplitRE, '$2')) || + IANA_DNS_PORT; return newSet.push([ipVersion, match[1], port]); } } From c8152acab94fae19a35c2bb971037b3ae4f05c5b Mon Sep 17 00:00:00 2001 From: XadillaX Date: Tue, 20 Jun 2017 00:53:00 +0800 Subject: [PATCH 5/5] fix typo --- doc/api/dns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/dns.md b/doc/api/dns.md index 4287e68cd0fc4b..982263507af7ca 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -69,7 +69,7 @@ eg. ```js [ '4.4.4.4', - '[2001:4860:4860::8888]', + '2001:4860:4860::8888', '4.4.4.4:1053', '[2001:4860:4860::8888]:1053' ]