From e007f66ae211192f385dc253bfa19a84e7fc649a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 10 Aug 2017 11:19:13 +0200 Subject: [PATCH] dns: add `verbatim` option to dns.lookup() When true, results from the DNS resolver are passed on as-is, without the reshuffling that Node.js otherwise does that puts IPv4 addresses before IPv6 addresses. PR-URL: https://github.com/nodejs/node/pull/14731 Ref: https://github.com/nodejs/node/issues/6307 Reviewed-By: Refael Ackermann Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- doc/api/dns.md | 6 +++ lib/dns.js | 4 +- src/cares_wrap.cc | 89 +++++++++++++++------------------------ test/internet/test-dns.js | 2 +- 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/doc/api/dns.md b/doc/api/dns.md index 0d191af15e..46927e5f52 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -140,6 +140,12 @@ changes: flags may be passed by bitwise `OR`ing their values. - `all` {boolean} When `true`, the callback returns all resolved addresses in an array. Otherwise, returns a single address. Defaults to `false`. + - `verbatim` {boolean} When `true`, the callback receives IPv4 and IPv6 + addresses in the order the DNS resolver returned them. When `false`, + IPv4 addresses are placed before IPv6 addresses. + Default: currently `false` (addresses are reordered) but this is expected + to change in the not too distant future. + New code should use `{ verbatim: true }`. - `callback` {Function} - `err` {Error} - `address` {string} A string representation of an IPv4 or IPv6 address. diff --git a/lib/dns.js b/lib/dns.js index 53e667565d..e536f1b4f9 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -131,6 +131,7 @@ function lookup(hostname, options, callback) { var hints = 0; var family = -1; var all = false; + var verbatim = false; // Parse arguments if (hostname && typeof hostname !== 'string') { @@ -145,6 +146,7 @@ function lookup(hostname, options, callback) { hints = options.hints >>> 0; family = options.family >>> 0; all = options.all === true; + verbatim = options.verbatim === true; if (hints !== 0 && hints !== cares.AI_ADDRCONFIG && @@ -185,7 +187,7 @@ function lookup(hostname, options, callback) { req.hostname = hostname; req.oncomplete = all ? onlookupall : onlookup; - var err = cares.getaddrinfo(req, hostname, family, hints); + var err = cares.getaddrinfo(req, hostname, family, hints, verbatim); if (err) { process.nextTick(callback, errnoException(err, 'getaddrinfo', hostname)); return {}; diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index e881ff5173..e800e0f2fe 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -196,15 +196,23 @@ void ChannelWrap::New(const FunctionCallbackInfo& args) { class GetAddrInfoReqWrap : public ReqWrap { public: - GetAddrInfoReqWrap(Environment* env, Local req_wrap_obj); + GetAddrInfoReqWrap(Environment* env, + Local req_wrap_obj, + bool verbatim); ~GetAddrInfoReqWrap(); size_t self_size() const override { return sizeof(*this); } + bool verbatim() const { return verbatim_; } + + private: + const bool verbatim_; }; GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, - Local req_wrap_obj) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) { + Local req_wrap_obj, + bool verbatim) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) + , verbatim_(verbatim) { Wrap(req_wrap_obj, this); } @@ -1811,70 +1819,38 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { }; if (status == 0) { - // Success - struct addrinfo *address; int n = 0; - - // Create the response array. Local results = Array::New(env->isolate()); - char ip[INET6_ADDRSTRLEN]; - const char *addr; - - // Iterate over the IPv4 responses again this time creating javascript - // strings for each IP and filling the results array. - address = res; - while (address) { - CHECK_EQ(address->ai_socktype, SOCK_STREAM); - - // Ignore random ai_family types. - if (address->ai_family == AF_INET) { - // Juggle pointers - addr = reinterpret_cast(&(reinterpret_cast( - address->ai_addr)->sin_addr)); - int err = uv_inet_ntop(address->ai_family, - addr, - ip, - INET6_ADDRSTRLEN); - if (err) + auto add = [&] (bool want_ipv4, bool want_ipv6) { + for (auto p = res; p != nullptr; p = p->ai_next) { + CHECK_EQ(p->ai_socktype, SOCK_STREAM); + + const char* addr; + if (want_ipv4 && p->ai_family == AF_INET) { + addr = reinterpret_cast( + &(reinterpret_cast(p->ai_addr)->sin_addr)); + } else if (want_ipv6 && p->ai_family == AF_INET6) { + addr = reinterpret_cast( + &(reinterpret_cast(p->ai_addr)->sin6_addr)); + } else { continue; + } - // Create JavaScript string - Local s = OneByteString(env->isolate(), ip); - results->Set(n, s); - n++; - } - - // Increment - address = address->ai_next; - } - - // Iterate over the IPv6 responses putting them in the array. - address = res; - while (address) { - CHECK_EQ(address->ai_socktype, SOCK_STREAM); - - // Ignore random ai_family types. - if (address->ai_family == AF_INET6) { - // Juggle pointers - addr = reinterpret_cast(&(reinterpret_cast( - address->ai_addr)->sin6_addr)); - int err = uv_inet_ntop(address->ai_family, - addr, - ip, - INET6_ADDRSTRLEN); - if (err) + char ip[INET6_ADDRSTRLEN]; + if (uv_inet_ntop(p->ai_family, addr, ip, sizeof(ip))) continue; - // Create JavaScript string Local s = OneByteString(env->isolate(), ip); results->Set(n, s); n++; } + }; - // Increment - address = address->ai_next; - } + const bool verbatim = req_wrap->verbatim(); + add(true, verbatim); + if (verbatim == false) + add(false, true); // No responses were found to return if (n == 0) { @@ -1965,6 +1941,7 @@ void GetAddrInfo(const FunctionCallbackInfo& args) { CHECK(args[0]->IsObject()); CHECK(args[1]->IsString()); CHECK(args[2]->IsInt32()); + CHECK(args[4]->IsBoolean()); Local req_wrap_obj = args[0].As(); node::Utf8Value hostname(env->isolate(), args[1]); @@ -1985,7 +1962,7 @@ void GetAddrInfo(const FunctionCallbackInfo& args) { CHECK(0 && "bad address family"); } - GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj); + auto req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj, args[4]->IsTrue()); struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index 9e08770590..03bf4bb7f8 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -553,7 +553,7 @@ console.log('looking up nodejs.org...'); const cares = process.binding('cares_wrap'); const req = new cares.GetAddrInfoReqWrap(); -cares.getaddrinfo(req, 'nodejs.org', 4); +cares.getaddrinfo(req, 'nodejs.org', 4, /* hints */ 0, /* verbatim */ true); req.oncomplete = function(err, domains) { assert.strictEqual(err, 0);