dns: fix crash using dns.setServers after resolve4
The callback function in cares_query is synchronous and called before closed. So dns.setServers in the synchronous callback before closed will occur crashing. Fixes: https://github.com/nodejs/node/issues/894 Refs: https://github.com/nodejs/node/blob/v6.9.4/deps/cares/src/ares_process.c#L1332-L1333 Refs: https://github.com/OpenSIPS/opensips/blob/2.3.0/proxy.c PR-URL: https://github.com/nodejs/node/pull/13050 Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
47e3d00241
commit
5a948f6f64
@ -98,6 +98,7 @@ inline const char* ToErrorCodeString(int status) {
|
|||||||
V(ETIMEOUT)
|
V(ETIMEOUT)
|
||||||
#undef V
|
#undef V
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UNKNOWN_ARES_ERROR";
|
return "UNKNOWN_ARES_ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +297,94 @@ Local<Array> HostentToNames(Environment* env, struct hostent* host) {
|
|||||||
return scope.Escape(names);
|
return scope.Escape(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void safe_free_hostent(struct hostent* host) {
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (host->h_addr_list != nullptr) {
|
||||||
|
idx = 0;
|
||||||
|
while (host->h_addr_list[idx]) {
|
||||||
|
free(host->h_addr_list[idx++]);
|
||||||
|
}
|
||||||
|
free(host->h_addr_list);
|
||||||
|
host->h_addr_list = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host->h_aliases != nullptr) {
|
||||||
|
idx = 0;
|
||||||
|
while (host->h_aliases[idx]) {
|
||||||
|
free(host->h_aliases[idx++]);
|
||||||
|
}
|
||||||
|
free(host->h_aliases);
|
||||||
|
host->h_aliases = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host->h_name != nullptr) {
|
||||||
|
free(host->h_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
host->h_addrtype = host->h_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cares_wrap_hostent_cpy(struct hostent* dest, struct hostent* src) {
|
||||||
|
dest->h_addr_list = nullptr;
|
||||||
|
dest->h_addrtype = 0;
|
||||||
|
dest->h_aliases = nullptr;
|
||||||
|
dest->h_length = 0;
|
||||||
|
dest->h_name = nullptr;
|
||||||
|
|
||||||
|
/* copy `h_name` */
|
||||||
|
size_t name_size = strlen(src->h_name) + 1;
|
||||||
|
dest->h_name = node::Malloc<char>(name_size);
|
||||||
|
memcpy(dest->h_name, src->h_name, name_size);
|
||||||
|
|
||||||
|
/* copy `h_aliases` */
|
||||||
|
size_t alias_count;
|
||||||
|
size_t cur_alias_length;
|
||||||
|
for (alias_count = 0;
|
||||||
|
src->h_aliases[alias_count] != nullptr;
|
||||||
|
alias_count++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->h_aliases = node::Malloc<char*>(alias_count + 1);
|
||||||
|
for (size_t i = 0; i < alias_count; i++) {
|
||||||
|
cur_alias_length = strlen(src->h_aliases[i]);
|
||||||
|
dest->h_aliases[i] = node::Malloc(cur_alias_length + 1);
|
||||||
|
memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_length + 1);
|
||||||
|
}
|
||||||
|
dest->h_aliases[alias_count] = nullptr;
|
||||||
|
|
||||||
|
/* copy `h_addr_list` */
|
||||||
|
size_t list_count;
|
||||||
|
for (list_count = 0;
|
||||||
|
src->h_addr_list[list_count] != nullptr;
|
||||||
|
list_count++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->h_addr_list = node::Malloc<char*>(list_count + 1);
|
||||||
|
for (size_t i = 0; i < list_count; i++) {
|
||||||
|
dest->h_addr_list[i] = node::Malloc(src->h_length);
|
||||||
|
memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length);
|
||||||
|
}
|
||||||
|
dest->h_addr_list[list_count] = nullptr;
|
||||||
|
|
||||||
|
/* work after work */
|
||||||
|
dest->h_length = src->h_length;
|
||||||
|
dest->h_addrtype = src->h_addrtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueryWrap;
|
||||||
|
struct CaresAsyncData {
|
||||||
|
QueryWrap* wrap;
|
||||||
|
int status;
|
||||||
|
bool is_host;
|
||||||
|
union {
|
||||||
|
hostent* host;
|
||||||
|
unsigned char* buf;
|
||||||
|
} data;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
uv_async_t async_handle;
|
||||||
|
};
|
||||||
|
|
||||||
class QueryWrap : public AsyncWrap {
|
class QueryWrap : public AsyncWrap {
|
||||||
public:
|
public:
|
||||||
@ -328,30 +417,80 @@ class QueryWrap : public AsyncWrap {
|
|||||||
return static_cast<void*>(this);
|
return static_cast<void*>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Callback(void *arg, int status, int timeouts,
|
static void CaresAsyncClose(uv_handle_t* handle) {
|
||||||
unsigned char* answer_buf, int answer_len) {
|
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
|
||||||
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
|
auto data = static_cast<struct CaresAsyncData*>(async->data);
|
||||||
|
delete data->wrap;
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CaresAsyncCb(uv_async_t* handle) {
|
||||||
|
auto data = static_cast<struct CaresAsyncData*>(handle->data);
|
||||||
|
|
||||||
|
QueryWrap* wrap = data->wrap;
|
||||||
|
int status = data->status;
|
||||||
|
|
||||||
if (status != ARES_SUCCESS) {
|
if (status != ARES_SUCCESS) {
|
||||||
wrap->ParseError(status);
|
wrap->ParseError(status);
|
||||||
|
} else if (!data->is_host) {
|
||||||
|
unsigned char* buf = data->data.buf;
|
||||||
|
wrap->Parse(buf, data->len);
|
||||||
|
free(buf);
|
||||||
} else {
|
} else {
|
||||||
wrap->Parse(answer_buf, answer_len);
|
hostent* host = data->data.host;
|
||||||
|
wrap->Parse(host);
|
||||||
|
safe_free_hostent(host);
|
||||||
|
free(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete wrap;
|
uv_close(reinterpret_cast<uv_handle_t*>(handle), CaresAsyncClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Callback(void *arg, int status, int timeouts,
|
static void Callback(void *arg, int status, int timeouts,
|
||||||
struct hostent* host) {
|
unsigned char* answer_buf, int answer_len) {
|
||||||
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
|
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
|
||||||
|
|
||||||
if (status != ARES_SUCCESS) {
|
unsigned char* buf_copy = nullptr;
|
||||||
wrap->ParseError(status);
|
if (status == ARES_SUCCESS) {
|
||||||
} else {
|
buf_copy = node::Malloc<unsigned char>(answer_len);
|
||||||
wrap->Parse(host);
|
memcpy(buf_copy, answer_buf, answer_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete wrap;
|
CaresAsyncData* data = new CaresAsyncData();
|
||||||
|
data->status = status;
|
||||||
|
data->wrap = wrap;
|
||||||
|
data->is_host = false;
|
||||||
|
data->data.buf = buf_copy;
|
||||||
|
data->len = answer_len;
|
||||||
|
|
||||||
|
uv_async_t* async_handle = &data->async_handle;
|
||||||
|
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
|
||||||
|
|
||||||
|
async_handle->data = data;
|
||||||
|
uv_async_send(async_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Callback(void *arg, int status, int timeouts,
|
||||||
|
struct hostent* host) {
|
||||||
|
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
|
||||||
|
|
||||||
|
struct hostent* host_copy = nullptr;
|
||||||
|
if (status == ARES_SUCCESS) {
|
||||||
|
host_copy = node::Malloc<hostent>(1);
|
||||||
|
cares_wrap_hostent_cpy(host_copy, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
CaresAsyncData* data = new CaresAsyncData();
|
||||||
|
data->status = status;
|
||||||
|
data->data.host = host_copy;
|
||||||
|
data->wrap = wrap;
|
||||||
|
data->is_host = true;
|
||||||
|
|
||||||
|
uv_async_t* async_handle = &data->async_handle;
|
||||||
|
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
|
||||||
|
|
||||||
|
async_handle->data = data;
|
||||||
|
uv_async_send(async_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallOnComplete(Local<Value> answer,
|
void CallOnComplete(Local<Value> answer,
|
||||||
|
12
test/internet/test-dns-setserver-in-callback-of-resolve4.js
Normal file
12
test/internet/test-dns-setserver-in-callback-of-resolve4.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// We don't care about `err` in the callback function of `dns.resolve4`. We just
|
||||||
|
// want to test whether `dns.setServers` that is run after `resolve4` will cause
|
||||||
|
// a crash or not. If it doesn't crash, the test succeeded.
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const dns = require('dns');
|
||||||
|
|
||||||
|
dns.resolve4('google.com', common.mustCall(function(/* err, nameServers */) {
|
||||||
|
dns.setServers([ '8.8.8.8' ]);
|
||||||
|
}));
|
Loading…
x
Reference in New Issue
Block a user