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
Show file tree
Hide file tree
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
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Up @@ -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;
Expand Down Expand Up @@ -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_; }
Expand All @@ -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); }
Expand All @@ -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_;
};

Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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));
}

Expand Down
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
Expand Up @@ -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);
}
}