Skip to content

Commit 3588730

Browse files
XadillaXgibfahn
authored andcommittedFeb 19, 2018
dns: fix crash while setting server during query
Fix this issue follow these two points: 1. Keep track of how many queries are currently open. If `setServers()` is called while there are open queries, error out. 2. For `Resolver` instances, use option 1. For dns.setServers(), just create a fresh new default channel every time it is called, and then set its servers list. Fixes: #14734 PR-URL: #14891 Backport-PR-URL: #17778 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent 1331a2a commit 3588730

File tree

4 files changed

+93
-21
lines changed

4 files changed

+93
-21
lines changed
 

‎lib/dns.js

+34-16
Original file line numberDiff line numberDiff line change
@@ -374,28 +374,44 @@ function setServers(servers) {
374374
}
375375
}
376376

377-
const defaultResolver = new Resolver();
377+
let defaultResolver = new Resolver();
378+
379+
const resolverKeys = [
380+
'getServers',
381+
'resolve',
382+
'resolveAny',
383+
'resolve4',
384+
'resolve6',
385+
'resolveCname',
386+
'resolveMx',
387+
'resolveNs',
388+
'resolveTxt',
389+
'resolveSrv',
390+
'resolvePtr',
391+
'resolveNaptr',
392+
'resolveSoa',
393+
'reverse'
394+
];
395+
396+
function setExportsFunctions() {
397+
resolverKeys.forEach((key) => {
398+
module.exports[key] = defaultResolver[key].bind(defaultResolver);
399+
});
400+
}
401+
402+
function defaultResolverSetServers(servers) {
403+
const resolver = new Resolver();
404+
resolver.setServers(servers);
405+
defaultResolver = resolver;
406+
setExportsFunctions();
407+
}
378408

379409
module.exports = {
380410
lookup,
381411
lookupService,
382412

383413
Resolver,
384-
getServers: defaultResolver.getServers.bind(defaultResolver),
385-
setServers: defaultResolver.setServers.bind(defaultResolver),
386-
resolve: defaultResolver.resolve.bind(defaultResolver),
387-
resolveAny: defaultResolver.resolveAny.bind(defaultResolver),
388-
resolve4: defaultResolver.resolve4.bind(defaultResolver),
389-
resolve6: defaultResolver.resolve6.bind(defaultResolver),
390-
resolveCname: defaultResolver.resolveCname.bind(defaultResolver),
391-
resolveMx: defaultResolver.resolveMx.bind(defaultResolver),
392-
resolveNs: defaultResolver.resolveNs.bind(defaultResolver),
393-
resolveTxt: defaultResolver.resolveTxt.bind(defaultResolver),
394-
resolveSrv: defaultResolver.resolveSrv.bind(defaultResolver),
395-
resolvePtr: defaultResolver.resolvePtr.bind(defaultResolver),
396-
resolveNaptr: defaultResolver.resolveNaptr.bind(defaultResolver),
397-
resolveSoa: defaultResolver.resolveSoa.bind(defaultResolver),
398-
reverse: defaultResolver.reverse.bind(defaultResolver),
414+
setServers: defaultResolverSetServers,
399415

400416
// uv_getaddrinfo flags
401417
ADDRCONFIG: cares.AI_ADDRCONFIG,
@@ -427,3 +443,5 @@ module.exports = {
427443
ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS',
428444
CANCELLED: 'ECANCELLED'
429445
};
446+
447+
setExportsFunctions();

‎src/cares_wrap.cc

+26-5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ inline uint32_t cares_get_32bit(const unsigned char* p) {
8181

8282
const int ns_t_cname_or_a = -1;
8383

84+
#define DNS_ESETSRVPENDING -1000
8485
inline const char* ToErrorCodeString(int status) {
8586
switch (status) {
8687
#define V(code) case ARES_##code: return #code;
@@ -148,6 +149,8 @@ class ChannelWrap : public AsyncWrap {
148149
void EnsureServers();
149150
void CleanupTimer();
150151

152+
void ModifyActivityQueryCount(int count);
153+
151154
inline uv_timer_t* timer_handle() { return timer_handle_; }
152155
inline ares_channel cares_channel() { return channel_; }
153156
inline bool query_last_ok() const { return query_last_ok_; }
@@ -156,6 +159,7 @@ class ChannelWrap : public AsyncWrap {
156159
inline void set_is_servers_default(bool is_default) {
157160
is_servers_default_ = is_default;
158161
}
162+
inline int active_query_count() { return active_query_count_; }
159163
inline node_ares_task_list* task_list() { return &task_list_; }
160164

161165
size_t self_size() const override { return sizeof(*this); }
@@ -168,6 +172,7 @@ class ChannelWrap : public AsyncWrap {
168172
bool query_last_ok_;
169173
bool is_servers_default_;
170174
bool library_inited_;
175+
int active_query_count_;
171176
node_ares_task_list task_list_;
172177
};
173178

@@ -178,7 +183,8 @@ ChannelWrap::ChannelWrap(Environment* env,
178183
channel_(nullptr),
179184
query_last_ok_(true),
180185
is_servers_default_(true),
181-
library_inited_(false) {
186+
library_inited_(false),
187+
active_query_count_(0) {
182188
MakeWeak<ChannelWrap>(this);
183189

184190
Setup();
@@ -543,6 +549,11 @@ void ChannelWrap::CleanupTimer() {
543549
timer_handle_ = nullptr;
544550
}
545551

552+
void ChannelWrap::ModifyActivityQueryCount(int count) {
553+
active_query_count_ += count;
554+
if (active_query_count_ < 0) active_query_count_ = 0;
555+
}
556+
546557

547558
/**
548559
* This function is to check whether current servers are fallback servers
@@ -686,6 +697,7 @@ class QueryWrap : public AsyncWrap {
686697
CaresAsyncCb));
687698

688699
wrap->channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
700+
wrap->channel_->ModifyActivityQueryCount(-1);
689701
async_handle->data = data;
690702
uv_async_send(async_handle);
691703
}
@@ -1806,9 +1818,12 @@ static void Query(const FunctionCallbackInfo<Value>& args) {
18061818
Wrap* wrap = new Wrap(channel, req_wrap_obj);
18071819

18081820
node::Utf8Value name(env->isolate(), string);
1821+
channel->ModifyActivityQueryCount(1);
18091822
int err = wrap->Send(*name);
1810-
if (err)
1823+
if (err) {
1824+
channel->ModifyActivityQueryCount(-1);
18111825
delete wrap;
1826+
}
18121827

18131828
args.GetReturnValue().Set(err);
18141829
}
@@ -2085,6 +2100,10 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
20852100
ChannelWrap* channel;
20862101
ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());
20872102

2103+
if (channel->active_query_count()) {
2104+
return args.GetReturnValue().Set(DNS_ESETSRVPENDING);
2105+
}
2106+
20882107
CHECK(args[0]->IsArray());
20892108

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

2168-
2187+
const char EMSG_ESETSRVPENDING[] = "There are pending queries.";
21692188
void StrError(const FunctionCallbackInfo<Value>& args) {
21702189
Environment* env = Environment::GetCurrent(args);
2171-
const char* errmsg = ares_strerror(args[0]->Int32Value(env->context())
2172-
.FromJust());
2190+
int code = args[0]->Int32Value(env->context()).FromJust();
2191+
const char* errmsg = (code == DNS_ESETSRVPENDING) ?
2192+
EMSG_ESETSRVPENDING :
2193+
ares_strerror(code);
21732194
args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg));
21742195
}
21752196

‎test/internet/test-dns-setserver-in-callback-of-resolve4.js

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ const dns = require('dns');
1010
dns.resolve4('google.com', common.mustCall(function(/* err, nameServers */) {
1111
dns.setServers([ '8.8.8.8' ]);
1212
}));
13+
14+
// Test https://github.com/nodejs/node/issues/14734
15+
dns.resolve4('google.com', common.mustCall());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
const dns = require('dns');
6+
7+
const goog = [
8+
'8.8.8.8',
9+
'8.8.4.4',
10+
];
11+
12+
{
13+
// Fix https://github.com/nodejs/node/issues/14734
14+
15+
{
16+
const resolver = new dns.Resolver();
17+
resolver.resolve('localhost', common.mustCall());
18+
19+
common.expectsError(resolver.setServers.bind(resolver, goog), {
20+
message: /^c-ares failed to set servers: "There are pending queries\." \[.+\]$/g
21+
});
22+
}
23+
24+
{
25+
dns.resolve('localhost', common.mustCall());
26+
27+
// should not throw
28+
dns.setServers(goog);
29+
}
30+
}

0 commit comments

Comments
 (0)
Please sign in to comment.