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;
|
||||
};
|
||||
|
||||
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. */
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user