Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dns: fix crash while setting server during query #14891

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions lib/dns.js
Original file line number Diff line number Diff line change
@@ -380,28 +380,44 @@ function setServers(servers) {
}
}

const defaultResolver = new Resolver();
let defaultResolver = new Resolver();

const resolverKeys = [
'getServers',
'resolve',
'resolveAny',
'resolve4',
'resolve6',
'resolveCname',
'resolveMx',
'resolveNs',
'resolveTxt',
'resolveSrv',
'resolvePtr',
'resolveNaptr',
'resolveSoa',
'reverse'
];

function setExportsFunctions() {
resolverKeys.forEach((key) => {
module.exports[key] = defaultResolver[key].bind(defaultResolver);
});
}

function defaultResolverSetServers(servers) {
const resolver = new Resolver();
resolver.setServers(servers);
defaultResolver = resolver;
setExportsFunctions();
}

module.exports = {
lookup,
lookupService,

Resolver,
getServers: defaultResolver.getServers.bind(defaultResolver),
setServers: defaultResolver.setServers.bind(defaultResolver),
resolve: defaultResolver.resolve.bind(defaultResolver),
resolveAny: defaultResolver.resolveAny.bind(defaultResolver),
resolve4: defaultResolver.resolve4.bind(defaultResolver),
resolve6: defaultResolver.resolve6.bind(defaultResolver),
resolveCname: defaultResolver.resolveCname.bind(defaultResolver),
resolveMx: defaultResolver.resolveMx.bind(defaultResolver),
resolveNs: defaultResolver.resolveNs.bind(defaultResolver),
resolveTxt: defaultResolver.resolveTxt.bind(defaultResolver),
resolveSrv: defaultResolver.resolveSrv.bind(defaultResolver),
resolvePtr: defaultResolver.resolvePtr.bind(defaultResolver),
resolveNaptr: defaultResolver.resolveNaptr.bind(defaultResolver),
resolveSoa: defaultResolver.resolveSoa.bind(defaultResolver),
reverse: defaultResolver.reverse.bind(defaultResolver),
setServers: defaultResolverSetServers,

// uv_getaddrinfo flags
ADDRCONFIG: cares.AI_ADDRCONFIG,
@@ -433,3 +449,5 @@ module.exports = {
ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS',
CANCELLED: 'ECANCELLED'
};

setExportsFunctions();
31 changes: 26 additions & 5 deletions src/cares_wrap.cc
Original file line number Diff line number Diff line change
@@ -83,6 +83,7 @@ inline uint32_t cares_get_32bit(const unsigned char* p) {

const int ns_t_cname_or_a = -1;

#define DNS_ESETSRVPENDING -1000
inline const char* ToErrorCodeString(int status) {
switch (status) {
#define V(code) case ARES_##code: return #code;
@@ -150,6 +151,8 @@ class ChannelWrap : public AsyncWrap {
void EnsureServers();
void CleanupTimer();

void ModifyActivityQueryCount(int count);

inline uv_timer_t* timer_handle() { return timer_handle_; }
inline ares_channel cares_channel() { return channel_; }
inline bool query_last_ok() const { return query_last_ok_; }
@@ -158,6 +161,7 @@ class ChannelWrap : public AsyncWrap {
inline void set_is_servers_default(bool is_default) {
is_servers_default_ = is_default;
}
inline int active_query_count() { return active_query_count_; }
inline node_ares_task_list* task_list() { return &task_list_; }

size_t self_size() const override { return sizeof(this); }
@@ -170,6 +174,7 @@ class ChannelWrap : public AsyncWrap {
bool query_last_ok_;
bool is_servers_default_;
bool library_inited_;
int active_query_count_;
node_ares_task_list task_list_;
};

@@ -180,7 +185,8 @@ ChannelWrap::ChannelWrap(Environment* env,
channel_(nullptr),
query_last_ok_(true),
is_servers_default_(true),
library_inited_(false) {
library_inited_(false),
active_query_count_(0) {
MakeWeak<ChannelWrap>(this);

Setup();
@@ -545,6 +551,11 @@ void ChannelWrap::CleanupTimer() {
timer_handle_ = nullptr;
}

void ChannelWrap::ModifyActivityQueryCount(int count) {
active_query_count_ += count;
if (active_query_count_ < 0) active_query_count_ = 0;
}


/**
* This function is to check whether current servers are fallback servers
@@ -688,6 +699,7 @@ class QueryWrap : public AsyncWrap {
CaresAsyncCb));

wrap->channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
wrap->channel_->ModifyActivityQueryCount(-1);
async_handle->data = data;
uv_async_send(async_handle);
}
@@ -1808,9 +1820,12 @@ static void Query(const FunctionCallbackInfo<Value>& args) {
Wrap* wrap = new Wrap(channel, req_wrap_obj);

node::Utf8Value name(env->isolate(), string);
channel->ModifyActivityQueryCount(1);
int err = wrap->Send(*name);
if (err)
if (err) {
channel->ModifyActivityQueryCount(-1);
delete wrap;
}

args.GetReturnValue().Set(err);
}
@@ -2087,6 +2102,10 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
ChannelWrap* channel;
ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());

if (channel->active_query_count()) {
return args.GetReturnValue().Set(DNS_ESETSRVPENDING);
}

CHECK(args[0]->IsArray());

Local<Array> arr = Local<Array>::Cast(args[0]);
@@ -2167,11 +2186,13 @@ void Cancel(const FunctionCallbackInfo<Value>& args) {
ares_cancel(channel->cares_channel());
}


const char EMSG_ESETSRVPENDING[] = "There are pending queries.";
void StrError(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
const char* errmsg = ares_strerror(args[0]->Int32Value(env->context())
.FromJust());
int code = args[0]->Int32Value(env->context()).FromJust();
const char* errmsg = (code == DNS_ESETSRVPENDING) ?
EMSG_ESETSRVPENDING :
ares_strerror(code);
args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg));
}

3 changes: 3 additions & 0 deletions test/internet/test-dns-setserver-in-callback-of-resolve4.js
Original file line number Diff line number Diff line change
@@ -13,3 +13,6 @@ dns.resolve4(
common.mustCall(function(/* err, nameServers */) {
dns.setServers([ addresses.DNS4_SERVER ]);
}));

// Test https://github.com/nodejs/node/issues/14734
dns.resolve4(addresses.INET4_HOST, common.mustCall());
31 changes: 31 additions & 0 deletions test/parallel/test-dns-setserver-when-querying.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const common = require('../common');

const dns = require('dns');

const goog = [
'8.8.8.8',
'8.8.4.4',
];

{
// Fix https://github.com/nodejs/node/issues/14734

{
const resolver = new dns.Resolver();
resolver.resolve('localhost', common.mustCall());

common.expectsError(resolver.setServers.bind(resolver, goog), {
code: 'ERR_DNS_SET_SERVERS_FAILED',
message: /^c-ares failed to set servers: "There are pending queries\." \[.+\]$/g
});
}

{
dns.resolve('localhost', common.mustCall());

// should not throw
dns.setServers(goog);
}
}