Browse Source

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 <refack@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
canary-base
Ben Noordhuis 7 years ago
committed by James M Snell
parent
commit
e007f66ae2
  1. 6
      doc/api/dns.md
  2. 4
      lib/dns.js
  3. 85
      src/cares_wrap.cc
  4. 2
      test/internet/test-dns.js

6
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.

4
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 {};

85
src/cares_wrap.cc

@ -196,15 +196,23 @@ void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) {
class GetAddrInfoReqWrap : public ReqWrap<uv_getaddrinfo_t> {
public:
GetAddrInfoReqWrap(Environment* env, Local<Object> req_wrap_obj);
GetAddrInfoReqWrap(Environment* env,
Local<Object> 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<Object> req_wrap_obj)
: ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) {
Local<Object> 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<Array> results = Array::New(env->isolate());
char ip[INET6_ADDRSTRLEN];
const char *addr;
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);
// 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<char*>(&(reinterpret_cast<struct sockaddr_in*>(
address->ai_addr)->sin_addr));
int err = uv_inet_ntop(address->ai_family,
addr,
ip,
INET6_ADDRSTRLEN);
if (err)
const char* addr;
if (want_ipv4 && p->ai_family == AF_INET) {
addr = reinterpret_cast<char*>(
&(reinterpret_cast<struct sockaddr_in*>(p->ai_addr)->sin_addr));
} else if (want_ipv6 && p->ai_family == AF_INET6) {
addr = reinterpret_cast<char*>(
&(reinterpret_cast<struct sockaddr_in6*>(p->ai_addr)->sin6_addr));
} else {
continue;
// Create JavaScript string
Local<String> 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<char*>(&(reinterpret_cast<struct sockaddr_in6*>(
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<String> 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<Value>& args) {
CHECK(args[0]->IsObject());
CHECK(args[1]->IsString());
CHECK(args[2]->IsInt32());
CHECK(args[4]->IsBoolean());
Local<Object> req_wrap_obj = args[0].As<Object>();
node::Utf8Value hostname(env->isolate(), args[1]);
@ -1985,7 +1962,7 @@ void GetAddrInfo(const FunctionCallbackInfo<Value>& 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));

2
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);

Loading…
Cancel
Save