From 2b541471dbec18dd15bee5d0cc46d39ca708f5dc Mon Sep 17 00:00:00 2001 From: XadillaX Date: Wed, 17 May 2017 17:23:38 +0800 Subject: [PATCH] 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 Reviewed-By: Ben Noordhuis --- src/cares_wrap.cc | 160 +++++++++++++++++++++++++--------------------- src/env-inl.h | 18 ++++++ src/env.h | 6 ++ 3 files changed, 110 insertions(+), 74 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index f0b81c8c97..82d980f11c 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -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 req_wrap_obj) @@ -417,6 +480,13 @@ class QueryWrap : public AsyncWrap { return static_cast(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(handle); auto data = static_cast(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& 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 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. */ diff --git a/src/env-inl.h b/src/env-inl.h index edcdc410cc..e2a58761fd 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -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_; } diff --git a/src/env.h b/src/env.h index 1a8157949a..c8c8232cc0 100644 --- a/src/env.h +++ b/src/env.h @@ -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_;