dns: fix resolve
failed starts without network
Fix the bug that you start process without network at first, but it connected lately, `dns.resolve` will stay failed with ECONNREFUSED because c-ares servers fallback to 127.0.0.1 at the very beginning. If c-ares servers "127.0.0.1" is detected and its not set by user self, and last query is not OK, recreating `ares_channel` operation will be triggered to reload servers. Fixes: https://github.com/nodejs/node/issues/1644 PR-URL: https://github.com/nodejs/node/pull/13076 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
595e5e3b23
commit
2b541471db
@ -386,6 +386,69 @@ struct CaresAsyncData {
|
|||||||
uv_async_t async_handle;
|
uv_async_t async_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SetupCaresChannel(Environment* env) {
|
||||||
|
struct ares_options options;
|
||||||
|
memset(&options, 0, sizeof(options));
|
||||||
|
options.flags = ARES_FLAG_NOCHECKRESP;
|
||||||
|
options.sock_state_cb = ares_sockstate_cb;
|
||||||
|
options.sock_state_cb_data = env;
|
||||||
|
|
||||||
|
/* We do the call to ares_init_option for caller. */
|
||||||
|
int r = ares_init_options(env->cares_channel_ptr(),
|
||||||
|
&options,
|
||||||
|
ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB);
|
||||||
|
|
||||||
|
if (r != ARES_SUCCESS) {
|
||||||
|
ares_library_cleanup();
|
||||||
|
return env->ThrowError(ToErrorCodeString(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is to check whether current servers are fallback servers
|
||||||
|
* when cares initialized.
|
||||||
|
*
|
||||||
|
* The fallback servers of cares is [ "127.0.0.1" ] with no user additional
|
||||||
|
* setting.
|
||||||
|
*/
|
||||||
|
void AresEnsureServers(Environment* env) {
|
||||||
|
/* if last query is OK or servers are set by user self, do not check */
|
||||||
|
if (env->cares_query_last_ok() || !env->cares_is_servers_default()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_channel channel = env->cares_channel();
|
||||||
|
ares_addr_node* servers = nullptr;
|
||||||
|
|
||||||
|
ares_get_servers(channel, &servers);
|
||||||
|
|
||||||
|
/* if no server or multi-servers, ignore */
|
||||||
|
if (servers == nullptr) return;
|
||||||
|
if (servers->next != nullptr) {
|
||||||
|
ares_free_data(servers);
|
||||||
|
env->set_cares_is_servers_default(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if the only server is not 127.0.0.1, ignore */
|
||||||
|
if (servers[0].family != AF_INET ||
|
||||||
|
servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK)) {
|
||||||
|
ares_free_data(servers);
|
||||||
|
env->set_cares_is_servers_default(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_free_data(servers);
|
||||||
|
servers = nullptr;
|
||||||
|
|
||||||
|
/* destroy channel and reset channel */
|
||||||
|
ares_destroy(channel);
|
||||||
|
|
||||||
|
SetupCaresChannel(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class QueryWrap : public AsyncWrap {
|
class QueryWrap : public AsyncWrap {
|
||||||
public:
|
public:
|
||||||
QueryWrap(Environment* env, Local<Object> req_wrap_obj)
|
QueryWrap(Environment* env, Local<Object> req_wrap_obj)
|
||||||
@ -417,6 +480,13 @@ class QueryWrap : public AsyncWrap {
|
|||||||
return static_cast<void*>(this);
|
return static_cast<void*>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void AresQuery(Environment* env, const char* name,
|
||||||
|
int dnsclass, int type, ares_callback callback,
|
||||||
|
void* arg) {
|
||||||
|
AresEnsureServers(env);
|
||||||
|
ares_query(env->cares_channel(), name, dnsclass, type, callback, arg);
|
||||||
|
}
|
||||||
|
|
||||||
static void CaresAsyncClose(uv_handle_t* handle) {
|
static void CaresAsyncClose(uv_handle_t* handle) {
|
||||||
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
|
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
|
||||||
auto data = static_cast<struct CaresAsyncData*>(async->data);
|
auto data = static_cast<struct CaresAsyncData*>(async->data);
|
||||||
@ -466,6 +536,7 @@ class QueryWrap : public AsyncWrap {
|
|||||||
uv_async_t* async_handle = &data->async_handle;
|
uv_async_t* async_handle = &data->async_handle;
|
||||||
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
|
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
|
||||||
|
|
||||||
|
wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED);
|
||||||
async_handle->data = data;
|
async_handle->data = data;
|
||||||
uv_async_send(async_handle);
|
uv_async_send(async_handle);
|
||||||
}
|
}
|
||||||
@ -489,6 +560,7 @@ class QueryWrap : public AsyncWrap {
|
|||||||
uv_async_t* async_handle = &data->async_handle;
|
uv_async_t* async_handle = &data->async_handle;
|
||||||
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
|
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
|
||||||
|
|
||||||
|
wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED);
|
||||||
async_handle->data = data;
|
async_handle->data = data;
|
||||||
uv_async_send(async_handle);
|
uv_async_send(async_handle);
|
||||||
}
|
}
|
||||||
@ -533,12 +605,7 @@ class QueryAWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_a, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_a,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,12 +648,7 @@ class QueryAaaaWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_aaaa, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_aaaa,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,12 +691,7 @@ class QueryCnameWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_cname, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_cname,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,12 +727,7 @@ class QueryMxWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_mx, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_mx,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,12 +773,7 @@ class QueryNsWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_ns, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_ns,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,12 +806,7 @@ class QueryTxtWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_txt, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_txt,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,12 +858,7 @@ class QuerySrvWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_srv, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_srv,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,12 +909,7 @@ class QueryPtrWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_ptr, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_ptr,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -915,12 +947,7 @@ class QueryNaptrWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_naptr, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_naptr,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -979,12 +1006,7 @@ class QuerySoaWrap: public QueryWrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Send(const char* name) override {
|
int Send(const char* name) override {
|
||||||
ares_query(env()->cares_channel(),
|
AresQuery(env(), name, ns_c_in, ns_t_soa, Callback, GetQueryArg());
|
||||||
name,
|
|
||||||
ns_c_in,
|
|
||||||
ns_t_soa,
|
|
||||||
Callback,
|
|
||||||
GetQueryArg());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1445,6 +1467,9 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
|
|||||||
|
|
||||||
delete[] servers;
|
delete[] servers;
|
||||||
|
|
||||||
|
if (err == ARES_SUCCESS)
|
||||||
|
env->set_cares_is_servers_default(false);
|
||||||
|
|
||||||
args.GetReturnValue().Set(err);
|
args.GetReturnValue().Set(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1479,20 +1504,7 @@ void Initialize(Local<Object> target,
|
|||||||
if (r != ARES_SUCCESS)
|
if (r != ARES_SUCCESS)
|
||||||
return env->ThrowError(ToErrorCodeString(r));
|
return env->ThrowError(ToErrorCodeString(r));
|
||||||
|
|
||||||
struct ares_options options;
|
SetupCaresChannel(env);
|
||||||
memset(&options, 0, sizeof(options));
|
|
||||||
options.flags = ARES_FLAG_NOCHECKRESP;
|
|
||||||
options.sock_state_cb = ares_sockstate_cb;
|
|
||||||
options.sock_state_cb_data = env;
|
|
||||||
|
|
||||||
/* We do the call to ares_init_option for caller. */
|
|
||||||
r = ares_init_options(env->cares_channel_ptr(),
|
|
||||||
&options,
|
|
||||||
ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB);
|
|
||||||
if (r != ARES_SUCCESS) {
|
|
||||||
ares_library_cleanup();
|
|
||||||
return env->ThrowError(ToErrorCodeString(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the timeout timer. The timer won't be started until the */
|
/* Initialize the timeout timer. The timer won't be started until the */
|
||||||
/* first socket is opened. */
|
/* first socket is opened. */
|
||||||
|
@ -292,6 +292,8 @@ inline Environment::Environment(IsolateData* isolate_data,
|
|||||||
isolate_data_(isolate_data),
|
isolate_data_(isolate_data),
|
||||||
async_hooks_(context->GetIsolate()),
|
async_hooks_(context->GetIsolate()),
|
||||||
timer_base_(uv_now(isolate_data->event_loop())),
|
timer_base_(uv_now(isolate_data->event_loop())),
|
||||||
|
cares_query_last_ok_(true),
|
||||||
|
cares_is_servers_default_(true),
|
||||||
using_domains_(false),
|
using_domains_(false),
|
||||||
printed_error_(false),
|
printed_error_(false),
|
||||||
trace_sync_io_(false),
|
trace_sync_io_(false),
|
||||||
@ -505,6 +507,22 @@ inline ares_channel* Environment::cares_channel_ptr() {
|
|||||||
return &cares_channel_;
|
return &cares_channel_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool Environment::cares_query_last_ok() {
|
||||||
|
return cares_query_last_ok_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Environment::set_cares_query_last_ok(bool ok) {
|
||||||
|
cares_query_last_ok_ = ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Environment::cares_is_servers_default() {
|
||||||
|
return cares_is_servers_default_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Environment::set_cares_is_servers_default(bool is_default) {
|
||||||
|
cares_is_servers_default_ = is_default;
|
||||||
|
}
|
||||||
|
|
||||||
inline node_ares_task_list* Environment::cares_task_list() {
|
inline node_ares_task_list* Environment::cares_task_list() {
|
||||||
return &cares_task_list_;
|
return &cares_task_list_;
|
||||||
}
|
}
|
||||||
|
@ -546,6 +546,10 @@ class Environment {
|
|||||||
inline uv_timer_t* cares_timer_handle();
|
inline uv_timer_t* cares_timer_handle();
|
||||||
inline ares_channel cares_channel();
|
inline ares_channel cares_channel();
|
||||||
inline ares_channel* cares_channel_ptr();
|
inline ares_channel* cares_channel_ptr();
|
||||||
|
inline bool cares_query_last_ok();
|
||||||
|
inline void set_cares_query_last_ok(bool ok);
|
||||||
|
inline bool cares_is_servers_default();
|
||||||
|
inline void set_cares_is_servers_default(bool is_default);
|
||||||
inline node_ares_task_list* cares_task_list();
|
inline node_ares_task_list* cares_task_list();
|
||||||
inline IsolateData* isolate_data() const;
|
inline IsolateData* isolate_data() const;
|
||||||
|
|
||||||
@ -667,6 +671,8 @@ class Environment {
|
|||||||
const uint64_t timer_base_;
|
const uint64_t timer_base_;
|
||||||
uv_timer_t cares_timer_handle_;
|
uv_timer_t cares_timer_handle_;
|
||||||
ares_channel cares_channel_;
|
ares_channel cares_channel_;
|
||||||
|
bool cares_query_last_ok_;
|
||||||
|
bool cares_is_servers_default_;
|
||||||
node_ares_task_list cares_task_list_;
|
node_ares_task_list cares_task_list_;
|
||||||
bool using_domains_;
|
bool using_domains_;
|
||||||
bool printed_error_;
|
bool printed_error_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user