Permalink
Browse files

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: #1644
PR-URL: #13076
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information...
XadillaX authored and addaleax committed May 17, 2017
1 parent 595e5e3 commit 2b541471dbec18dd15bee5d0cc46d39ca708f5dc
Showing with 110 additions and 74 deletions.
  1. +86 −74 src/cares_wrap.cc
  2. +18 −0 src/env-inl.h
  3. +6 −0 src/env.h
View
@@ -386,6 +386,69 @@ struct CaresAsyncData {
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 {
public:
QueryWrap(Environment* env, Local<Object> req_wrap_obj)
@@ -417,6 +480,13 @@ class QueryWrap : public AsyncWrap {
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) {
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
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_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED);
async_handle->data = data;
uv_async_send(async_handle);
}
@@ -489,6 +560,7 @@ class QueryWrap : public AsyncWrap {
uv_async_t* async_handle = &data->async_handle;
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED);
async_handle->data = data;
uv_async_send(async_handle);
}
@@ -533,12 +605,7 @@ class QueryAWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_a,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_a, Callback, GetQueryArg());
return 0;
}
@@ -581,12 +648,7 @@ class QueryAaaaWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_aaaa,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_aaaa, Callback, GetQueryArg());
return 0;
}
@@ -629,12 +691,7 @@ class QueryCnameWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_cname,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_cname, Callback, GetQueryArg());
return 0;
}
@@ -670,12 +727,7 @@ class QueryMxWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_mx,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_mx, Callback, GetQueryArg());
return 0;
}
@@ -721,12 +773,7 @@ class QueryNsWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_ns,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_ns, Callback, GetQueryArg());
return 0;
}
@@ -759,12 +806,7 @@ class QueryTxtWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_txt,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_txt, Callback, GetQueryArg());
return 0;
}
@@ -816,12 +858,7 @@ class QuerySrvWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_srv,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_srv, Callback, GetQueryArg());
return 0;
}
@@ -872,12 +909,7 @@ class QueryPtrWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_ptr,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_ptr, Callback, GetQueryArg());
return 0;
}
@@ -915,12 +947,7 @@ class QueryNaptrWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_naptr,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_naptr, Callback, GetQueryArg());
return 0;
}
@@ -979,12 +1006,7 @@ class QuerySoaWrap: public QueryWrap {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_soa,
Callback,
GetQueryArg());
AresQuery(env(), name, ns_c_in, ns_t_soa, Callback, GetQueryArg());
return 0;
}
@@ -1445,6 +1467,9 @@ void SetServers(const FunctionCallbackInfo<Value>& args) {
delete[] servers;
if (err == ARES_SUCCESS)
env->set_cares_is_servers_default(false);
args.GetReturnValue().Set(err);
}
@@ -1479,20 +1504,7 @@ void Initialize(Local<Object> target,
if (r != ARES_SUCCESS)
return env->ThrowError(ToErrorCodeString(r));
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. */
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));
}
SetupCaresChannel(env);
/* Initialize the timeout timer. The timer won't be started until the */
/* first socket is opened. */
View
@@ -292,6 +292,8 @@ inline Environment::Environment(IsolateData* isolate_data,
isolate_data_(isolate_data),
async_hooks_(context->GetIsolate()),
timer_base_(uv_now(isolate_data->event_loop())),
cares_query_last_ok_(true),
cares_is_servers_default_(true),
using_domains_(false),
printed_error_(false),
trace_sync_io_(false),
@@ -505,6 +507,22 @@ inline ares_channel* Environment::cares_channel_ptr() {
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() {
return &cares_task_list_;
}
View
@@ -546,6 +546,10 @@ class Environment {
inline uv_timer_t* cares_timer_handle();
inline ares_channel cares_channel();
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 IsolateData* isolate_data() const;
@@ -667,6 +671,8 @@ class Environment {
const uint64_t timer_base_;
uv_timer_t cares_timer_handle_;
ares_channel cares_channel_;
bool cares_query_last_ok_;
bool cares_is_servers_default_;
node_ares_task_list cares_task_list_;
bool using_domains_;
bool printed_error_;

0 comments on commit 2b54147

Please sign in to comment.