Skip to content

Commit

Permalink
dns: fix resolve failed starts without network
Browse files Browse the repository at this point in the history
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
Backport-PR-URL: #14434
PR-URL: #13076
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
XadillaX authored and MylesBorins committed Aug 16, 2017
1 parent a1cef1f commit 6518932
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 74 deletions.
160 changes: 86 additions & 74 deletions src/cares_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,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)
Expand Down Expand Up @@ -402,6 +465,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);
Expand Down Expand Up @@ -453,6 +523,7 @@ class QueryWrap : public AsyncWrap {
async_handle,
CaresAsyncCb));

wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED);
async_handle->data = data;
uv_async_send(async_handle);
}
Expand All @@ -478,6 +549,7 @@ class QueryWrap : public AsyncWrap {
async_handle,
CaresAsyncCb));

wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED);
async_handle->data = data;
uv_async_send(async_handle);
}
Expand Down Expand Up @@ -522,12 +594,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;
}

Expand Down Expand Up @@ -570,12 +637,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;
}

Expand Down Expand Up @@ -618,12 +680,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;
}

Expand Down Expand Up @@ -659,12 +716,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;
}

Expand Down Expand Up @@ -710,12 +762,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;
}

Expand Down Expand Up @@ -748,12 +795,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;
}

Expand Down Expand Up @@ -805,12 +847,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;
}

Expand Down Expand Up @@ -861,12 +898,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;
}

Expand Down Expand Up @@ -904,12 +936,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;
}

Expand Down Expand Up @@ -968,12 +995,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;
}

Expand Down Expand Up @@ -1434,6 +1456,9 @@ static void SetServers(const FunctionCallbackInfo<Value>& args) {

delete[] servers;

if (err == ARES_SUCCESS)
env->set_cares_is_servers_default(false);

args.GetReturnValue().Set(err);
}

Expand Down Expand Up @@ -1468,20 +1493,7 @@ static 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. */
Expand Down
18 changes: 18 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ inline Environment::Environment(v8::Local<v8::Context> context,
: isolate_(context->GetIsolate()),
isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)),
timer_base_(uv_now(loop)),
cares_query_last_ok_(true),
cares_is_servers_default_(true),
using_domains_(false),
printed_error_(false),
trace_sync_io_(false),
Expand Down Expand Up @@ -453,6 +455,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_;
}
Expand Down
6 changes: 6 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,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 bool using_domains() const;
Expand Down Expand Up @@ -555,6 +559,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_;
Expand Down

0 comments on commit 6518932

Please sign in to comment.