diff --git a/doc/api/dns.md b/doc/api/dns.md index 0d191af15e867b..46927e5f528e0c 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 6946380ae673ce..7a3d3336e3aa1d 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -126,6 +126,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') { @@ -140,6 +141,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 && @@ -180,7 +182,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 f43eb7ed8b54ae..5417a9ee55def3 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 e4bdfe51b79455..75e8815f8823d8 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -549,7 +549,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);