Browse Source

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>
v6
XadillaX 8 years ago
committed by Anna Henningsen
parent
commit
2b541471db
No known key found for this signature in database GPG Key ID: D8B9F5AEAE84E4CF
  1. 160
      src/cares_wrap.cc
  2. 18
      src/env-inl.h
  3. 6
      src/env.h

160
src/cares_wrap.cc

@ -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. */

18
src/env-inl.h

@ -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_;
} }

6
src/env.h

@ -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…
Cancel
Save