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. PR-URL: https://github.com/nodejs/node/pull/14891 Fixes: https://github.com/nodejs/node/issues/14734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
b3e53673b9
commit
b3678dff52
50
lib/dns.js
50
lib/dns.js
@ -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 = {
|
module.exports = {
|
||||||
lookup,
|
lookup,
|
||||||
lookupService,
|
lookupService,
|
||||||
|
|
||||||
Resolver,
|
Resolver,
|
||||||
getServers: defaultResolver.getServers.bind(defaultResolver),
|
setServers: defaultResolverSetServers,
|
||||||
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),
|
|
||||||
|
|
||||||
// uv_getaddrinfo flags
|
// uv_getaddrinfo flags
|
||||||
ADDRCONFIG: cares.AI_ADDRCONFIG,
|
ADDRCONFIG: cares.AI_ADDRCONFIG,
|
||||||
@ -433,3 +449,5 @@ module.exports = {
|
|||||||
ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS',
|
ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS',
|
||||||
CANCELLED: 'ECANCELLED'
|
CANCELLED: 'ECANCELLED'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setExportsFunctions();
|
||||||
|
@ -83,6 +83,7 @@ inline uint32_t cares_get_32bit(const unsigned char* p) {
|
|||||||
|
|
||||||
const int ns_t_cname_or_a = -1;
|
const int ns_t_cname_or_a = -1;
|
||||||
|
|
||||||
|
#define DNS_ESETSRVPENDING -1000
|
||||||
inline const char* ToErrorCodeString(int status) {
|
inline const char* ToErrorCodeString(int status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
#define V(code) case ARES_##code: return #code;
|
#define V(code) case ARES_##code: return #code;
|
||||||
@ -150,6 +151,8 @@ class ChannelWrap : public AsyncWrap {
|
|||||||
void EnsureServers();
|
void EnsureServers();
|
||||||
void CleanupTimer();
|
void CleanupTimer();
|
||||||
|
|
||||||
|
void ModifyActivityQueryCount(int count);
|
||||||
|
|
||||||
inline uv_timer_t* timer_handle() { return timer_handle_; }
|
inline uv_timer_t* timer_handle() { return timer_handle_; }
|
||||||
inline ares_channel cares_channel() { return channel_; }
|
inline ares_channel cares_channel() { return channel_; }
|
||||||
inline bool query_last_ok() const { return query_last_ok_; }
|
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) {
|
inline void set_is_servers_default(bool is_default) {
|
||||||
is_servers_default_ = 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_; }
|
inline node_ares_task_list* task_list() { return &task_list_; }
|
||||||
|
|
||||||
size_t self_size() const override { return sizeof(*this); }
|
size_t self_size() const override { return sizeof(*this); }
|
||||||
@ -170,6 +174,7 @@ class ChannelWrap : public AsyncWrap {
|
|||||||
bool query_last_ok_;
|
bool query_last_ok_;
|
||||||
bool is_servers_default_;
|
bool is_servers_default_;
|
||||||
bool library_inited_;
|
bool library_inited_;
|
||||||
|
int active_query_count_;
|
||||||
node_ares_task_list task_list_;
|
node_ares_task_list task_list_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,7 +185,8 @@ ChannelWrap::ChannelWrap(Environment* env,
|
|||||||
channel_(nullptr),
|
channel_(nullptr),
|
||||||
query_last_ok_(true),
|
query_last_ok_(true),
|
||||||
is_servers_default_(true),
|
is_servers_default_(true),
|
||||||
library_inited_(false) {
|
library_inited_(false),
|
||||||
|
active_query_count_(0) {
|
||||||
MakeWeak<ChannelWrap>(this);
|
MakeWeak<ChannelWrap>(this);
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
@ -545,6 +551,11 @@ void ChannelWrap::CleanupTimer() {
|
|||||||
timer_handle_ = nullptr;
|
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
|
* This function is to check whether current servers are fallback servers
|
||||||
@ -682,6 +693,7 @@ class QueryWrap : public AsyncWrap {
|
|||||||
CaresAsyncCb));
|
CaresAsyncCb));
|
||||||
|
|
||||||
wrap->channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
|
wrap->channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
|
||||||
|
wrap->channel_->ModifyActivityQueryCount(-1);
|
||||||
async_handle->data = data;
|
async_handle->data = data;
|
||||||
uv_async_send(async_handle);
|
uv_async_send(async_handle);
|
||||||
}
|
}
|
||||||
@ -1802,9 +1814,12 @@ static void Query(const FunctionCallbackInfo<Value>& args) {
|
|||||||
Wrap* wrap = new Wrap(channel, req_wrap_obj);
|
Wrap* wrap = new Wrap(channel, req_wrap_obj);
|
||||||
|
|
||||||
node::Utf8Value name(env->isolate(), string);
|
node::Utf8Value name(env->isolate(), string);
|
||||||
|
channel->ModifyActivityQueryCount(1);
|
||||||
int err = wrap->Send(*name);
|
int err = wrap->Send(*name);
|
||||||
if (err)
|
if (err) {
|
||||||
|
channel->ModifyActivityQueryCount(-1);
|
||||||
delete wrap;
|
delete wrap;
|
||||||
|
}
|
||||||
|
|
||||||
args.GetReturnValue().Set(err);
|
args.GetReturnValue().Set(err);
|
||||||
}
|
}
|
||||||
@ -2081,6 +2096,10 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
|
|||||||
ChannelWrap* channel;
|
ChannelWrap* channel;
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());
|
ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder());
|
||||||
|
|
||||||
|
if (channel->active_query_count()) {
|
||||||
|
return args.GetReturnValue().Set(DNS_ESETSRVPENDING);
|
||||||
|
}
|
||||||
|
|
||||||
CHECK(args[0]->IsArray());
|
CHECK(args[0]->IsArray());
|
||||||
|
|
||||||
Local<Array> arr = Local<Array>::Cast(args[0]);
|
Local<Array> arr = Local<Array>::Cast(args[0]);
|
||||||
@ -2161,11 +2180,13 @@ void Cancel(const FunctionCallbackInfo<Value>& args) {
|
|||||||
ares_cancel(channel->cares_channel());
|
ares_cancel(channel->cares_channel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char EMSG_ESETSRVPENDING[] = "There are pending queries.";
|
||||||
void StrError(const FunctionCallbackInfo<Value>& args) {
|
void StrError(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
const char* errmsg = ares_strerror(args[0]->Int32Value(env->context())
|
int code = args[0]->Int32Value(env->context()).FromJust();
|
||||||
.FromJust());
|
const char* errmsg = (code == DNS_ESETSRVPENDING) ?
|
||||||
|
EMSG_ESETSRVPENDING :
|
||||||
|
ares_strerror(code);
|
||||||
args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg));
|
args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,3 +13,6 @@ dns.resolve4(
|
|||||||
common.mustCall(function(/* err, nameServers */) {
|
common.mustCall(function(/* err, nameServers */) {
|
||||||
dns.setServers([ addresses.DNS4_SERVER ]);
|
dns.setServers([ addresses.DNS4_SERVER ]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Test https://github.com/nodejs/node/issues/14734
|
||||||
|
dns.resolve4(addresses.INET4_HOST, common.mustCall());
|
||||||
|
31
test/parallel/test-dns-setserver-when-querying.js
Normal file
31
test/parallel/test-dns-setserver-when-querying.js
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user