diff options
author | James M Snell <jasnell@gmail.com> | 2021-04-09 01:45:45 +0300 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2021-04-17 04:46:08 +0300 |
commit | bc31dc0e0fab7b86be06e40a7ce3e5968d0e3753 (patch) | |
tree | 08eb3fc235e859c7a9f1a270e8715a5b0d6c0e9b /src/cares_wrap.cc | |
parent | 5e588c1c7c6e710feba6a87f4cb9c19a653c9231 (diff) |
dns: refactor cares_wrap internals
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/38172
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'src/cares_wrap.cc')
-rw-r--r-- | src/cares_wrap.cc | 2079 |
1 files changed, 809 insertions, 1270 deletions
diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index bd00cd25624..c0368739ff0 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -19,17 +19,18 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CARES_STATICLIB -#include "ares.h" #include "async_wrap-inl.h" +#include "base_object-inl.h" #include "base64-inl.h" +#include "cares_wrap.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node.h" +#include "node_errors.h" #include "req_wrap-inl.h" #include "util-inl.h" +#include "v8.h" #include "uv.h" -#include "node_errors.h" #include <cerrno> #include <cstring> @@ -37,28 +38,6 @@ #include <vector> #include <unordered_set> -#ifdef __POSIX__ -# include <netdb.h> -#endif // __POSIX__ - -#if defined(__ANDROID__) || \ - defined(__MINGW32__) || \ - defined(__OpenBSD__) || \ - defined(_MSC_VER) - -# include <nameser.h> -#else -# include <arpa/nameser.h> -#endif - -#ifndef T_CAA -# define T_CAA 257 /* Certification Authority Authorization */ -#endif - -#if defined(__OpenBSD__) -# define AI_V4MAPPED 0 -#endif - namespace node { namespace cares_wrap { @@ -85,190 +64,8 @@ inline uint16_t cares_get_16bit(const unsigned char* p) { return static_cast<uint32_t>(p[0] << 8U) | (static_cast<uint32_t>(p[1])); } -const int ns_t_cname_or_a = -1; - -#define DNS_ESETSRVPENDING -1000 -inline const char* ToErrorCodeString(int status) { - switch (status) { -#define V(code) case ARES_##code: return #code; - V(EADDRGETNETWORKPARAMS) - V(EBADFAMILY) - V(EBADFLAGS) - V(EBADHINTS) - V(EBADNAME) - V(EBADQUERY) - V(EBADRESP) - V(EBADSTR) - V(ECANCELLED) - V(ECONNREFUSED) - V(EDESTRUCTION) - V(EFILE) - V(EFORMERR) - V(ELOADIPHLPAPI) - V(ENODATA) - V(ENOMEM) - V(ENONAME) - V(ENOTFOUND) - V(ENOTIMP) - V(ENOTINITIALIZED) - V(EOF) - V(EREFUSED) - V(ESERVFAIL) - V(ETIMEOUT) -#undef V - } - - return "UNKNOWN_ARES_ERROR"; -} - -class ChannelWrap; - -struct node_ares_task : public MemoryRetainer { - ChannelWrap* channel; - ares_socket_t sock; - uv_poll_t poll_watcher; - - inline void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(node_ares_task) - SET_SELF_SIZE(node_ares_task) -}; - -struct TaskHash { - size_t operator()(node_ares_task* a) const { - return std::hash<ares_socket_t>()(a->sock); - } -}; - -struct TaskEqual { - inline bool operator()(node_ares_task* a, node_ares_task* b) const { - return a->sock == b->sock; - } -}; - -using node_ares_task_list = - std::unordered_set<node_ares_task*, TaskHash, TaskEqual>; - -class ChannelWrap : public AsyncWrap { - public: - ChannelWrap(Environment* env, Local<Object> object, int timeout); - ~ChannelWrap() override; - - static void New(const FunctionCallbackInfo<Value>& args); - - void Setup(); - void EnsureServers(); - void StartTimer(); - void CloseTimer(); - - void ModifyActivityQueryCount(int count); - - inline uv_timer_t* timer_handle() { return timer_handle_; } - inline ares_channel cares_channel() { return channel_; } - inline void set_query_last_ok(bool ok) { query_last_ok_ = ok; } - inline void set_is_servers_default(bool is_default) { - is_servers_default_ = is_default; - } - inline int active_query_count() { return active_query_count_; } - inline node_ares_task_list* task_list() { return &task_list_; } - - void MemoryInfo(MemoryTracker* tracker) const override { - if (timer_handle_ != nullptr) - tracker->TrackField("timer_handle", *timer_handle_); - tracker->TrackField("task_list", task_list_, "node_ares_task_list"); - } - - SET_MEMORY_INFO_NAME(ChannelWrap) - SET_SELF_SIZE(ChannelWrap) - - static void AresTimeout(uv_timer_t* handle); - - private: - uv_timer_t* timer_handle_; - ares_channel channel_; - bool query_last_ok_; - bool is_servers_default_; - bool library_inited_; - int timeout_; - int active_query_count_; - node_ares_task_list task_list_; -}; - -ChannelWrap::ChannelWrap(Environment* env, - Local<Object> object, - int timeout) - : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), - timer_handle_(nullptr), - channel_(nullptr), - query_last_ok_(true), - is_servers_default_(true), - library_inited_(false), - timeout_(timeout), - active_query_count_(0) { - MakeWeak(); - - Setup(); -} - -void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) { - CHECK(args.IsConstructCall()); - CHECK_EQ(args.Length(), 1); - CHECK(args[0]->IsInt32()); - const int timeout = args[0].As<Int32>()->Value(); - Environment* env = Environment::GetCurrent(args); - new ChannelWrap(env, args.This(), timeout); -} - -class GetAddrInfoReqWrap : public ReqWrap<uv_getaddrinfo_t> { - public: - GetAddrInfoReqWrap(Environment* env, - Local<Object> req_wrap_obj, - bool verbatim); - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) - SET_SELF_SIZE(GetAddrInfoReqWrap) - - bool verbatim() const { return verbatim_; } - - private: - const bool verbatim_; -}; - -GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, - Local<Object> req_wrap_obj, - bool verbatim) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) - , verbatim_(verbatim) { -} - - -class GetNameInfoReqWrap : public ReqWrap<uv_getnameinfo_t> { - public: - GetNameInfoReqWrap(Environment* env, Local<Object> req_wrap_obj); - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) - SET_SELF_SIZE(GetNameInfoReqWrap) -}; - -GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, - Local<Object> req_wrap_obj) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) { -} - - -/* This is called once per second by loop->timer. It is used to constantly */ -/* call back into c-ares for possibly processing timeouts. */ -void ChannelWrap::AresTimeout(uv_timer_t* handle) { - ChannelWrap* channel = static_cast<ChannelWrap*>(handle->data); - CHECK_EQ(channel->timer_handle(), handle); - CHECK_EQ(false, channel->task_list()->empty()); - ares_process_fd(channel->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); -} - - void ares_poll_cb(uv_poll_t* watcher, int status, int events) { - node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); + NodeAresTask* task = ContainerOf(&NodeAresTask::poll_watcher, watcher); ChannelWrap* channel = task->channel; /* Reset the idle timer */ @@ -289,41 +86,17 @@ void ares_poll_cb(uv_poll_t* watcher, int status, int events) { void ares_poll_close_cb(uv_poll_t* watcher) { - node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); - delete task; -} - -void node_ares_task::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("channel", channel); -} - -/* Allocates and returns a new node_ares_task */ -node_ares_task* ares_task_create(ChannelWrap* channel, ares_socket_t sock) { - auto task = new node_ares_task(); - - task->channel = channel; - task->sock = sock; - - if (uv_poll_init_socket(channel->env()->event_loop(), - &task->poll_watcher, sock) < 0) { - /* This should never happen. */ - delete task; - return nullptr; - } - - return task; + std::unique_ptr<NodeAresTask> free_me( + ContainerOf(&NodeAresTask::poll_watcher, watcher)); } /* Callback from ares when socket operation is started */ -void ares_sockstate_cb(void* data, - ares_socket_t sock, - int read, - int write) { +void ares_sockstate_cb(void* data, ares_socket_t sock, int read, int write) { ChannelWrap* channel = static_cast<ChannelWrap*>(data); - node_ares_task* task; + NodeAresTask* task; - node_ares_task lookup_task; + NodeAresTask lookup_task; lookup_task.sock = sock; auto it = channel->task_list()->find(&lookup_task); @@ -334,7 +107,7 @@ void ares_sockstate_cb(void* data, /* New socket */ channel->StartTimer(); - task = ares_task_create(channel, sock); + task = NodeAresTask::Create(channel, sock); if (task == nullptr) { /* This should never happen unless we're out of memory or something */ /* is seriously wrong. The socket won't be polled, but the query will */ @@ -367,414 +140,55 @@ void ares_sockstate_cb(void* data, } } - -Local<Array> HostentToNames(Environment* env, - struct hostent* host, - Local<Array> append_to = Local<Array>()) { +Local<Array> HostentToNames(Environment* env, struct hostent* host) { EscapableHandleScope scope(env->isolate()); - auto context = env->context(); - bool append = !append_to.IsEmpty(); - Local<Array> names = append ? append_to : Array::New(env->isolate()); - size_t offset = names->Length(); - - for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { - Local<String> address = OneByteString(env->isolate(), host->h_aliases[i]); - names->Set(context, i + offset, address).Check(); - } - - return append ? names : scope.Escape(names); -} - -void safe_free_hostent(struct hostent* host) { - int idx; - - if (host->h_addr_list != nullptr) { - idx = 0; - while (host->h_addr_list[idx]) { - free(host->h_addr_list[idx++]); - } - free(host->h_addr_list); - host->h_addr_list = nullptr; - } - - if (host->h_aliases != nullptr) { - idx = 0; - while (host->h_aliases[idx]) { - free(host->h_aliases[idx++]); - } - free(host->h_aliases); - host->h_aliases = nullptr; - } - - free(host->h_name); - free(host); -} - -void cares_wrap_hostent_cpy(struct hostent* dest, const struct hostent* src) { - dest->h_addr_list = nullptr; - dest->h_addrtype = 0; - dest->h_aliases = nullptr; - dest->h_length = 0; - dest->h_name = nullptr; - - /* copy `h_name` */ - size_t name_size = strlen(src->h_name) + 1; - dest->h_name = node::Malloc<char>(name_size); - memcpy(dest->h_name, src->h_name, name_size); - - /* copy `h_aliases` */ - size_t alias_count; - for (alias_count = 0; - src->h_aliases[alias_count] != nullptr; - alias_count++) { - } - - dest->h_aliases = node::Malloc<char*>(alias_count + 1); - for (size_t i = 0; i < alias_count; i++) { - const size_t cur_alias_size = strlen(src->h_aliases[i]) + 1; - dest->h_aliases[i] = node::Malloc(cur_alias_size); - memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_size); - } - dest->h_aliases[alias_count] = nullptr; - - /* copy `h_addr_list` */ - size_t list_count; - for (list_count = 0; - src->h_addr_list[list_count] != nullptr; - list_count++) { - } - - dest->h_addr_list = node::Malloc<char*>(list_count + 1); - for (size_t i = 0; i < list_count; i++) { - dest->h_addr_list[i] = node::Malloc(src->h_length); - memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length); - } - dest->h_addr_list[list_count] = nullptr; - - /* work after work */ - dest->h_length = src->h_length; - dest->h_addrtype = src->h_addrtype; -} - -class QueryWrap; - -void ChannelWrap::Setup() { - 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 = this; - options.timeout = timeout_; - - int r; - if (!library_inited_) { - Mutex::ScopedLock lock(ares_library_mutex); - // Multiple calls to ares_library_init() increase a reference counter, - // so this is a no-op except for the first call to it. - r = ares_library_init(ARES_LIB_INIT_ALL); - if (r != ARES_SUCCESS) - return env()->ThrowError(ToErrorCodeString(r)); - } - - /* We do the call to ares_init_option for caller. */ - const int optmask = - ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB; - r = ares_init_options(&channel_, &options, optmask); - - if (r != ARES_SUCCESS) { - Mutex::ScopedLock lock(ares_library_mutex); - ares_library_cleanup(); - return env()->ThrowError(ToErrorCodeString(r)); - } - - library_inited_ = true; -} - -void ChannelWrap::StartTimer() { - if (timer_handle_ == nullptr) { - timer_handle_ = new uv_timer_t(); - timer_handle_->data = static_cast<void*>(this); - uv_timer_init(env()->event_loop(), timer_handle_); - } else if (uv_is_active(reinterpret_cast<uv_handle_t*>(timer_handle_))) { - return; - } - int timeout = timeout_; - if (timeout == 0) timeout = 1; - if (timeout < 0 || timeout > 1000) timeout = 1000; - uv_timer_start(timer_handle_, AresTimeout, timeout, timeout); -} - -void ChannelWrap::CloseTimer() { - if (timer_handle_ == nullptr) - return; - - env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; }); - timer_handle_ = nullptr; -} -ChannelWrap::~ChannelWrap() { - ares_destroy(channel_); - - if (library_inited_) { - Mutex::ScopedLock lock(ares_library_mutex); - // This decreases the reference counter increased by ares_library_init(). - ares_library_cleanup(); - } + std::vector<Local<Value>> names; - CloseTimer(); -} + for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) + names.emplace_back(OneByteString(env->isolate(), host->h_aliases[i])); + Local<Array> ret = Array::New(env->isolate(), names.data(), names.size()); -void ChannelWrap::ModifyActivityQueryCount(int count) { - active_query_count_ += count; - CHECK_GE(active_query_count_, 0); + return scope.Escape(ret); } +Local<Array> HostentToNames(Environment* env, + struct hostent* host, + Local<Array> names) { + size_t offset = names->Length(); -/** - * 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 ChannelWrap::EnsureServers() { - /* if last query is OK or servers are set by user self, do not check */ - if (query_last_ok_ || !is_servers_default_) { - return; - } - - ares_addr_port_node* servers = nullptr; - - ares_get_servers_ports(channel_, &servers); - - /* if no server or multi-servers, ignore */ - if (servers == nullptr) return; - if (servers->next != nullptr) { - ares_free_data(servers); - 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) || - servers[0].tcp_port != 0 || - servers[0].udp_port != 0) { - ares_free_data(servers); - is_servers_default_ = false; - return; + for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { + names->Set( + env->context(), + i + offset, + OneByteString(env->isolate(), host->h_aliases[i])).Check(); } - ares_free_data(servers); - servers = nullptr; - - /* destroy channel and reset channel */ - ares_destroy(channel_); - - CloseTimer(); - Setup(); + return names; } - -class QueryWrap : public AsyncWrap { - public: - QueryWrap(ChannelWrap* channel, Local<Object> req_wrap_obj, const char* name) - : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP), - channel_(channel), - trace_name_(name) { - } - - ~QueryWrap() override { - CHECK_EQ(false, persistent().IsEmpty()); - - // Let Callback() know that this object no longer exists. - if (callback_ptr_ != nullptr) - *callback_ptr_ = nullptr; - } - - // Subclasses should implement the appropriate Send method. - virtual int Send(const char* name) { - UNREACHABLE(); - return 0; - } - - virtual int Send(const char* name, int family) { - UNREACHABLE(); - return 0; - } - - protected: - void AresQuery(const char* name, - int dnsclass, - int type) { - channel_->EnsureServers(); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( - TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, - "name", TRACE_STR_COPY(name)); - ares_query(channel_->cares_channel(), name, dnsclass, type, Callback, - MakeCallbackPointer()); - } - - struct ResponseData { - int status; - bool is_host; - DeleteFnPtr<hostent, safe_free_hostent> host; - MallocedBuffer<unsigned char> buf; - }; - - void AfterResponse() { - CHECK(response_data_); - - const int status = response_data_->status; - - if (status != ARES_SUCCESS) { - ParseError(status); - } else if (!response_data_->is_host) { - Parse(response_data_->buf.data, response_data_->buf.size); - } else { - Parse(response_data_->host.get()); - } - } - - void* MakeCallbackPointer() { - CHECK_NULL(callback_ptr_); - callback_ptr_ = new QueryWrap*(this); - return callback_ptr_; - } - - static QueryWrap* FromCallbackPointer(void* arg) { - std::unique_ptr<QueryWrap*> wrap_ptr { static_cast<QueryWrap**>(arg) }; - QueryWrap* wrap = *wrap_ptr.get(); - if (wrap == nullptr) return nullptr; - wrap->callback_ptr_ = nullptr; - return wrap; - } - - static void Callback(void* arg, int status, int timeouts, - unsigned char* answer_buf, int answer_len) { - QueryWrap* wrap = FromCallbackPointer(arg); - if (wrap == nullptr) return; - - unsigned char* buf_copy = nullptr; - if (status == ARES_SUCCESS) { - buf_copy = node::Malloc<unsigned char>(answer_len); - memcpy(buf_copy, answer_buf, answer_len); - } - - wrap->response_data_ = std::make_unique<ResponseData>(); - ResponseData* data = wrap->response_data_.get(); - data->status = status; - data->is_host = false; - data->buf = MallocedBuffer<unsigned char>(buf_copy, answer_len); - - wrap->QueueResponseCallback(status); - } - - static void Callback(void* arg, int status, int timeouts, - struct hostent* host) { - QueryWrap* wrap = FromCallbackPointer(arg); - if (wrap == nullptr) return; - - struct hostent* host_copy = nullptr; - if (status == ARES_SUCCESS) { - host_copy = node::Malloc<hostent>(1); - cares_wrap_hostent_cpy(host_copy, host); - } - - wrap->response_data_ = std::make_unique<ResponseData>(); - ResponseData* data = wrap->response_data_.get(); - data->status = status; - data->host.reset(host_copy); - data->is_host = true; - - wrap->QueueResponseCallback(status); - } - - void QueueResponseCallback(int status) { - BaseObjectPtr<QueryWrap> strong_ref{this}; - env()->SetImmediate([this, strong_ref](Environment*) { - AfterResponse(); - - // Delete once strong_ref goes out of scope. - Detach(); - }); - - channel_->set_query_last_ok(status != ARES_ECONNREFUSED); - channel_->ModifyActivityQueryCount(-1); - } - - void CallOnComplete(Local<Value> answer, - Local<Value> extra = Local<Value>()) { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - Local<Value> argv[] = { - Integer::New(env()->isolate(), 0), - answer, - extra - }; - const int argc = arraysize(argv) - extra.IsEmpty(); - TRACE_EVENT_NESTABLE_ASYNC_END0( - TRACING_CATEGORY_NODE2(dns, native), trace_name_, this); - - MakeCallback(env()->oncomplete_string(), argc, argv); - } - - void ParseError(int status) { - CHECK_NE(status, ARES_SUCCESS); - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - const char* code = ToErrorCodeString(status); - Local<Value> arg = OneByteString(env()->isolate(), code); - TRACE_EVENT_NESTABLE_ASYNC_END1( - TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, - "error", status); - MakeCallback(env()->oncomplete_string(), 1, &arg); - } - - // Subclasses should implement the appropriate Parse method. - virtual void Parse(unsigned char* buf, int len) { - UNREACHABLE(); - } - - virtual void Parse(struct hostent* host) { - UNREACHABLE(); - } - - BaseObjectPtr<ChannelWrap> channel_; - - private: - std::unique_ptr<ResponseData> response_data_; - const char* trace_name_; - // Pointer to pointer to 'this' that can be reset from the destructor, - // in order to let Callback() know that 'this' no longer exists. - QueryWrap** callback_ptr_ = nullptr; -}; - - template <typename T> -Local<Array> AddrTTLToArray(Environment* env, - const T* addrttls, - size_t naddrttls) { - auto isolate = env->isolate(); - +Local<Array> AddrTTLToArray( + Environment* env, + const T* addrttls, + size_t naddrttls) { MaybeStackBuffer<Local<Value>, 8> ttls(naddrttls); for (size_t i = 0; i < naddrttls; i++) - ttls[i] = Integer::NewFromUnsigned(isolate, addrttls[i].ttl); + ttls[i] = Integer::NewFromUnsigned(env->isolate(), addrttls[i].ttl); - return Array::New(isolate, ttls.out(), naddrttls); + return Array::New(env->isolate(), ttls.out(), naddrttls); } - -int ParseGeneralReply(Environment* env, - const unsigned char* buf, - int len, - int* type, - Local<Array> ret, - void* addrttls = nullptr, - int* naddrttls = nullptr) { +int ParseGeneralReply( + Environment* env, + const unsigned char* buf, + int len, + int* type, + Local<Array> ret, + void* addrttls = nullptr, + int* naddrttls = nullptr) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); hostent* host; int status; @@ -809,18 +223,20 @@ int ParseGeneralReply(Environment* env, if (status != ARES_SUCCESS) return status; + CHECK_NOT_NULL(host); + HostEntPointer ptr(host); + /* If it's `CNAME`, return the CNAME value; * And if it's `CNAME_OR_A` and it has value in `h_name` and `h_aliases[0]`, * we consider it's a CNAME record, otherwise we consider it's an A record. */ - if ((*type == ns_t_cname_or_a && host->h_name && host->h_aliases[0]) || + if ((*type == ns_t_cname_or_a && ptr->h_name && ptr->h_aliases[0]) || *type == ns_t_cname) { // A cname lookup always returns a single record but we follow the // common API here. *type = ns_t_cname; - ret->Set(context, + ret->Set(env->context(), ret->Length(), - OneByteString(env->isolate(), host->h_name)).Check(); - ares_free_hostent(host); + OneByteString(env->isolate(), ptr->h_name)).Check(); return ARES_SUCCESS; } @@ -828,116 +244,110 @@ int ParseGeneralReply(Environment* env, *type = ns_t_a; if (*type == ns_t_ns) { - HostentToNames(env, host, ret); + HostentToNames(env, ptr.get(), ret); } else if (*type == ns_t_ptr) { uint32_t offset = ret->Length(); - for (uint32_t i = 0; host->h_aliases[i] != nullptr; i++) { - auto alias = OneByteString(env->isolate(), host->h_aliases[i]); - ret->Set(context, i + offset, alias).Check(); + for (uint32_t i = 0; ptr->h_aliases[i] != nullptr; i++) { + auto alias = OneByteString(env->isolate(), ptr->h_aliases[i]); + ret->Set(env->context(), i + offset, alias).Check(); } } else { uint32_t offset = ret->Length(); char ip[INET6_ADDRSTRLEN]; - for (uint32_t i = 0; host->h_addr_list[i] != nullptr; ++i) { - uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip)); + for (uint32_t i = 0; ptr->h_addr_list[i] != nullptr; ++i) { + uv_inet_ntop(ptr->h_addrtype, ptr->h_addr_list[i], ip, sizeof(ip)); auto address = OneByteString(env->isolate(), ip); - ret->Set(context, i + offset, address).Check(); + ret->Set(env->context(), i + offset, address).Check(); } } - ares_free_hostent(host); - return ARES_SUCCESS; } - -int ParseMxReply(Environment* env, - const unsigned char* buf, - int len, - Local<Array> ret, - bool need_type = false) { +int ParseMxReply( + Environment* env, + const unsigned char* buf, + int len, + Local<Array> ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_mx_reply* mx_start; int status = ares_parse_mx_reply(buf, len, &mx_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } uint32_t offset = ret->Length(); ares_mx_reply* current = mx_start; for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local<Object> mx_record = Object::New(env->isolate()); - mx_record->Set(context, + mx_record->Set(env->context(), env->exchange_string(), OneByteString(env->isolate(), current->host)).Check(); - mx_record->Set(context, + mx_record->Set(env->context(), env->priority_string(), Integer::New(env->isolate(), current->priority)).Check(); if (need_type) - mx_record->Set(context, + mx_record->Set(env->context(), env->type_string(), env->dns_mx_string()).Check(); - ret->Set(context, i + offset, mx_record).Check(); + ret->Set(env->context(), i + offset, mx_record).Check(); } ares_free_data(mx_start); return ARES_SUCCESS; } -int ParseCaaReply(Environment* env, - const unsigned char* buf, - int len, - Local<Array> ret, - bool need_type = false) { +int ParseCaaReply( + Environment* env, + const unsigned char* buf, + int len, + Local<Array> ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_caa_reply* caa_start; int status = ares_parse_caa_reply(buf, len, &caa_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } uint32_t offset = ret->Length(); ares_caa_reply* current = caa_start; for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local<Object> caa_record = Object::New(env->isolate()); - caa_record->Set(context, + caa_record->Set(env->context(), env->dns_critical_string(), Integer::New(env->isolate(), current->critical)).Check(); - caa_record->Set(context, + caa_record->Set(env->context(), OneByteString(env->isolate(), current->property), OneByteString(env->isolate(), current->value)).Check(); if (need_type) - caa_record->Set(context, + caa_record->Set(env->context(), env->type_string(), env->dns_caa_string()).Check(); - ret->Set(context, i + offset, caa_record).Check(); + ret->Set(env->context(), i + offset, caa_record).Check(); } ares_free_data(caa_start); return ARES_SUCCESS; } -int ParseTxtReply(Environment* env, - const unsigned char* buf, - int len, - Local<Array> ret, - bool need_type = false) { +int ParseTxtReply( + Environment* env, + const unsigned char* buf, + int len, + Local<Array> ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_txt_ext* txt_out; int status = ares_parse_txt_reply_ext(buf, len, &txt_out); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } Local<Array> txt_chunk; @@ -953,13 +363,13 @@ int ParseTxtReply(Environment* env, if (!txt_chunk.IsEmpty()) { if (need_type) { Local<Object> elem = Object::New(env->isolate()); - elem->Set(context, env->entries_string(), txt_chunk).Check(); - elem->Set(context, + elem->Set(env->context(), env->entries_string(), txt_chunk).Check(); + elem->Set(env->context(), env->type_string(), env->dns_txt_string()).Check(); - ret->Set(context, offset + i++, elem).Check(); + ret->Set(env->context(), offset + i++, elem).Check(); } else { - ret->Set(context, offset + i++, txt_chunk).Check(); + ret->Set(env->context(), offset + i++, txt_chunk).Check(); } } @@ -967,20 +377,20 @@ int ParseTxtReply(Environment* env, j = 0; } - txt_chunk->Set(context, j++, txt).Check(); + txt_chunk->Set(env->context(), j++, txt).Check(); } // Push last chunk if it isn't empty if (!txt_chunk.IsEmpty()) { if (need_type) { Local<Object> elem = Object::New(env->isolate()); - elem->Set(context, env->entries_string(), txt_chunk).Check(); - elem->Set(context, + elem->Set(env->context(), env->entries_string(), txt_chunk).Check(); + elem->Set(env->context(), env->type_string(), env->dns_txt_string()).Check(); - ret->Set(context, offset + i, elem).Check(); + ret->Set(env->context(), offset + i, elem).Check(); } else { - ret->Set(context, offset + i, txt_chunk).Check(); + ret->Set(env->context(), offset + i, txt_chunk).Check(); } } @@ -989,42 +399,41 @@ int ParseTxtReply(Environment* env, } -int ParseSrvReply(Environment* env, - const unsigned char* buf, - int len, - Local<Array> ret, - bool need_type = false) { +int ParseSrvReply( + Environment* env, + const unsigned char* buf, + int len, + Local<Array> ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); struct ares_srv_reply* srv_start; int status = ares_parse_srv_reply(buf, len, &srv_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } ares_srv_reply* current = srv_start; int offset = ret->Length(); for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local<Object> srv_record = Object::New(env->isolate()); - srv_record->Set(context, + srv_record->Set(env->context(), env->name_string(), OneByteString(env->isolate(), current->host)).Check(); - srv_record->Set(context, + srv_record->Set(env->context(), env->port_string(), Integer::New(env->isolate(), current->port)).Check(); - srv_record->Set(context, + srv_record->Set(env->context(), env->priority_string(), Integer::New(env->isolate(), current->priority)).Check(); - srv_record->Set(context, + srv_record->Set(env->context(), env->weight_string(), Integer::New(env->isolate(), current->weight)).Check(); if (need_type) - srv_record->Set(context, + srv_record->Set(env->context(), env->type_string(), env->dns_srv_string()).Check(); - ret->Set(context, i + offset, srv_record).Check(); + ret->Set(env->context(), i + offset, srv_record).Check(); } ares_free_data(srv_start); @@ -1032,53 +441,52 @@ int ParseSrvReply(Environment* env, } -int ParseNaptrReply(Environment* env, - const unsigned char* buf, - int len, - Local<Array> ret, - bool need_type = false) { +int ParseNaptrReply( + Environment* env, + const unsigned char* buf, + int len, + Local<Array> ret, + bool need_type = false) { HandleScope handle_scope(env->isolate()); - auto context = env->context(); ares_naptr_reply* naptr_start; int status = ares_parse_naptr_reply(buf, len, &naptr_start); - if (status != ARES_SUCCESS) { + if (status != ARES_SUCCESS) return status; - } ares_naptr_reply* current = naptr_start; int offset = ret->Length(); for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { Local<Object> naptr_record = Object::New(env->isolate()); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->flags_string(), OneByteString(env->isolate(), current->flags)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->service_string(), OneByteString(env->isolate(), current->service)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->regexp_string(), OneByteString(env->isolate(), current->regexp)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->replacement_string(), OneByteString(env->isolate(), current->replacement)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->order_string(), Integer::New(env->isolate(), current->order)).Check(); - naptr_record->Set(context, + naptr_record->Set(env->context(), env->preference_string(), Integer::New(env->isolate(), current->preference)).Check(); if (need_type) - naptr_record->Set(context, + naptr_record->Set(env->context(), env->type_string(), env->dns_naptr_string()).Check(); - ret->Set(context, i + offset, naptr_record).Check(); + ret->Set(env->context(), i + offset, naptr_record).Check(); } ares_free_data(naptr_start); @@ -1086,12 +494,12 @@ int ParseNaptrReply(Environment* env, } -int ParseSoaReply(Environment* env, - unsigned char* buf, - int len, - Local<Object>* ret) { +int ParseSoaReply( + Environment* env, + unsigned char* buf, + int len, + Local<Object>* ret) { EscapableHandleScope handle_scope(env->isolate()); - auto context = env->context(); // Manage memory using standardard smart pointer std::unique_tr struct AresDeleter { @@ -1172,29 +580,29 @@ int ParseSoaReply(Environment* env, const unsigned int minttl = ReadUint32BE(ptr + 4 * 4); Local<Object> soa_record = Object::New(env->isolate()); - soa_record->Set(context, + soa_record->Set(env->context(), env->nsname_string(), OneByteString(env->isolate(), nsname.get())).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->hostmaster_string(), OneByteString(env->isolate(), hostmaster.get())).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->serial_string(), Integer::NewFromUnsigned(env->isolate(), serial)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->refresh_string(), Integer::New(env->isolate(), refresh)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->retry_string(), Integer::New(env->isolate(), retry)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->expire_string(), Integer::New(env->isolate(), expire)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->minttl_string(), Integer::NewFromUnsigned(env->isolate(), minttl)).Check(); - soa_record->Set(context, + soa_record->Set(env->context(), env->type_string(), env->dns_soa_string()).Check(); @@ -1208,658 +616,764 @@ int ParseSoaReply(Environment* env, return ARES_SUCCESS; } +} // anonymous namespace +ChannelWrap::ChannelWrap(Environment* env, Local<Object> object, int timeout) + : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), + timeout_(timeout) { + MakeWeak(); -class QueryAnyWrap: public QueryWrap { - public: - QueryAnyWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveAny") { - } - - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_any); - return 0; - } + Setup(); +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAnyWrap) - SET_SELF_SIZE(QueryAnyWrap) - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - auto context = env()->context(); - Context::Scope context_scope(context); - - Local<Array> ret = Array::New(env()->isolate()); - int type, status, old_count; - - /* Parse A records or CNAME records */ - ares_addrttl addrttls[256]; - int naddrttls = arraysize(addrttls); - - type = ns_t_cname_or_a; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addrttls, - &naddrttls); - uint32_t a_count = ret->Length(); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +void ChannelWrap::MemoryInfo(MemoryTracker* tracker) const { + if (timer_handle_ != nullptr) + tracker->TrackField("timer_handle", *timer_handle_); + tracker->TrackField("task_list", task_list_, "NodeAresTask::List"); +} - if (type == ns_t_a) { - CHECK_EQ(static_cast<uint32_t>(naddrttls), a_count); - for (uint32_t i = 0; i < a_count; i++) { - Local<Object> obj = Object::New(env()->isolate()); - obj->Set(context, - env()->address_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->ttl_string(), - Integer::NewFromUnsigned( - env()->isolate(), addrttls[i].ttl)).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_a_string()).Check(); - ret->Set(context, i, obj).Check(); - } - } else { - for (uint32_t i = 0; i < a_count; i++) { - Local<Object> obj = Object::New(env()->isolate()); - obj->Set(context, - env()->value_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_cname_string()).Check(); - ret->Set(context, i, obj).Check(); - } - } +void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) { + CHECK(args.IsConstructCall()); + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsInt32()); + const int timeout = args[0].As<Int32>()->Value(); + Environment* env = Environment::GetCurrent(args); + new ChannelWrap(env, args.This(), timeout); +} - /* Parse AAAA records */ - ares_addr6ttl addr6ttls[256]; - int naddr6ttls = arraysize(addr6ttls); - - type = ns_t_aaaa; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addr6ttls, - &naddr6ttls); - uint32_t aaaa_count = ret->Length() - a_count; - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +GetAddrInfoReqWrap::GetAddrInfoReqWrap( + Environment* env, + Local<Object> req_wrap_obj, + bool verbatim) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP), + verbatim_(verbatim) {} - CHECK_EQ(aaaa_count, static_cast<uint32_t>(naddr6ttls)); - CHECK_EQ(ret->Length(), a_count + aaaa_count); - for (uint32_t i = a_count; i < ret->Length(); i++) { - Local<Object> obj = Object::New(env()->isolate()); - obj->Set(context, - env()->address_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->ttl_string(), - Integer::NewFromUnsigned( - env()->isolate(), addr6ttls[i - a_count].ttl)).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_aaaa_string()).Check(); - ret->Set(context, i, obj).Check(); - } +GetNameInfoReqWrap::GetNameInfoReqWrap( + Environment* env, + Local<Object> req_wrap_obj) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) {} - /* Parse MX records */ - status = ParseMxReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +/* This is called once per second by loop->timer. It is used to constantly */ +/* call back into c-ares for possibly processing timeouts. */ +void ChannelWrap::AresTimeout(uv_timer_t* handle) { + ChannelWrap* channel = static_cast<ChannelWrap*>(handle->data); + CHECK_EQ(channel->timer_handle(), handle); + CHECK_EQ(false, channel->task_list()->empty()); + ares_process_fd(channel->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); +} - /* Parse NS records */ - type = ns_t_ns; - old_count = ret->Length(); - status = ParseGeneralReply(env(), buf, len, &type, ret); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } - for (uint32_t i = old_count; i < ret->Length(); i++) { - Local<Object> obj = Object::New(env()->isolate()); - obj->Set(context, - env()->value_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_ns_string()).Check(); - ret->Set(context, i, obj).Check(); - } - /* Parse TXT records */ - status = ParseTxtReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +void NodeAresTask::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("channel", channel); +} - /* Parse SRV records */ - status = ParseSrvReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - return; - } +/* Allocates and returns a new NodeAresTask */ +NodeAresTask* NodeAresTask::Create(ChannelWrap* channel, ares_socket_t sock) { + auto task = new NodeAresTask(); - /* Parse PTR records */ - type = ns_t_ptr; - old_count = ret->Length(); - status = ParseGeneralReply(env(), buf, len, &type, ret); - for (uint32_t i = old_count; i < ret->Length(); i++) { - Local<Object> obj = Object::New(env()->isolate()); - obj->Set(context, - env()->value_string(), - ret->Get(context, i).ToLocalChecked()).Check(); - obj->Set(context, - env()->type_string(), - env()->dns_ptr_string()).Check(); - ret->Set(context, i, obj).Check(); - } + task->channel = channel; + task->sock = sock; - /* Parse NAPTR records */ - status = ParseNaptrReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } + if (uv_poll_init_socket(channel->env()->event_loop(), + &task->poll_watcher, sock) < 0) { + /* This should never happen. */ + delete task; + return nullptr; + } - /* Parse SOA records */ - Local<Object> soa_record = Local<Object>(); - status = ParseSoaReply(env(), buf, len, &soa_record); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } - if (!soa_record.IsEmpty()) - ret->Set(context, ret->Length(), soa_record).Check(); + return task; +} - /* Parse CAA records */ - status = ParseCaaReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +void ChannelWrap::Setup() { + 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 = this; + options.timeout = timeout_; - CallOnComplete(ret); + int r; + if (!library_inited_) { + Mutex::ScopedLock lock(ares_library_mutex); + // Multiple calls to ares_library_init() increase a reference counter, + // so this is a no-op except for the first call to it. + r = ares_library_init(ARES_LIB_INIT_ALL); + if (r != ARES_SUCCESS) + return env()->ThrowError(ToErrorCodeString(r)); } -}; + /* We do the call to ares_init_option for caller. */ + const int optmask = + ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB; + r = ares_init_options(&channel_, &options, optmask); -class QueryAWrap: public QueryWrap { - public: - QueryAWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolve4") { + if (r != ARES_SUCCESS) { + Mutex::ScopedLock lock(ares_library_mutex); + ares_library_cleanup(); + return env()->ThrowError(ToErrorCodeString(r)); } - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_a); - return 0; + library_inited_ = true; +} + +void ChannelWrap::StartTimer() { + if (timer_handle_ == nullptr) { + timer_handle_ = new uv_timer_t(); + timer_handle_->data = static_cast<void*>(this); + uv_timer_init(env()->event_loop(), timer_handle_); + } else if (uv_is_active(reinterpret_cast<uv_handle_t*>(timer_handle_))) { + return; } + int timeout = timeout_; + if (timeout == 0) timeout = 1; + if (timeout < 0 || timeout > 1000) timeout = 1000; + uv_timer_start(timer_handle_, AresTimeout, timeout, timeout); +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAWrap) - SET_SELF_SIZE(QueryAWrap) - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - ares_addrttl addrttls[256]; - int naddrttls = arraysize(addrttls), status; - Local<Array> ret = Array::New(env()->isolate()); - - int type = ns_t_a; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addrttls, - &naddrttls); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } +void ChannelWrap::CloseTimer() { + if (timer_handle_ == nullptr) + return; + + env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; }); + timer_handle_ = nullptr; +} - Local<Array> ttls = AddrTTLToArray<ares_addrttl>(env(), - addrttls, - naddrttls); +ChannelWrap::~ChannelWrap() { + ares_destroy(channel_); - CallOnComplete(ret, ttls); + if (library_inited_) { + Mutex::ScopedLock lock(ares_library_mutex); + // This decreases the reference counter increased by ares_library_init(). + ares_library_cleanup(); } -}; + CloseTimer(); +} -class QueryAaaaWrap: public QueryWrap { - public: - QueryAaaaWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolve6") { - } - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_aaaa); - return 0; +void ChannelWrap::ModifyActivityQueryCount(int count) { + active_query_count_ += count; + CHECK_GE(active_query_count_, 0); +} + + +/** + * 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 ChannelWrap::EnsureServers() { + /* if last query is OK or servers are set by user self, do not check */ + if (query_last_ok_ || !is_servers_default_) { + return; } - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAaaaWrap) - SET_SELF_SIZE(QueryAaaaWrap) - - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - - ares_addr6ttl addrttls[256]; - int naddrttls = arraysize(addrttls), status; - Local<Array> ret = Array::New(env()->isolate()); - - int type = ns_t_aaaa; - status = ParseGeneralReply(env(), - buf, - len, - &type, - ret, - addrttls, - &naddrttls); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + ares_addr_port_node* servers = nullptr; - Local<Array> ttls = AddrTTLToArray<ares_addr6ttl>(env(), - addrttls, - naddrttls); + ares_get_servers_ports(channel_, &servers); - CallOnComplete(ret, ttls); + /* if no server or multi-servers, ignore */ + if (servers == nullptr) return; + if (servers->next != nullptr) { + ares_free_data(servers); + is_servers_default_ = false; + return; } -}; -class QueryCaaWrap: public QueryWrap { - public: - QueryCaaWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveCaa") { + /* 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) || + servers[0].tcp_port != 0 || + servers[0].udp_port != 0) { + ares_free_data(servers); + is_servers_default_ = false; + return; } - int Send(const char* name) override { - AresQuery(name, ns_c_in, T_CAA); - return 0; - } + ares_free_data(servers); + servers = nullptr; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAaaaWrap) - SET_SELF_SIZE(QueryAaaaWrap) + /* destroy channel and reset channel */ + ares_destroy(channel_); - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + CloseTimer(); + Setup(); +} - Local<Array> ret = Array::New(env()->isolate()); - int status = ParseCaaReply(env(), buf, len, ret); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } +int AnyTraits::Send(QueryWrap<AnyTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_any); + return 0; +} - this->CallOnComplete(ret); - } -}; +int ATraits::Send(QueryWrap<ATraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_a); + return 0; +} -class QueryCnameWrap: public QueryWrap { - public: - QueryCnameWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveCname") { - } +int AaaaTraits::Send(QueryWrap<AaaaTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_aaaa); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_cname); - return 0; - } +int CaaTraits::Send(QueryWrap<CaaTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, T_CAA); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryCnameWrap) - SET_SELF_SIZE(QueryCnameWrap) +int CnameTraits::Send(QueryWrap<CnameTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_cname); + return 0; +} - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); +int MxTraits::Send(QueryWrap<MxTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_mx); + return 0; +} - Local<Array> ret = Array::New(env()->isolate()); - int type = ns_t_cname; - int status = ParseGeneralReply(env(), buf, len, &type, ret); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } +int NsTraits::Send(QueryWrap<NsTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_ns); + return 0; +} - this->CallOnComplete(ret); - } -}; +int TxtTraits::Send(QueryWrap<TxtTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_txt); + return 0; +} +int SrvTraits::Send(QueryWrap<SrvTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_srv); + return 0; +} -class QueryMxWrap: public QueryWrap { - public: - QueryMxWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveMx") { - } +int PtrTraits::Send(QueryWrap<PtrTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_ptr); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_mx); - return 0; - } +int NaptrTraits::Send(QueryWrap<NaptrTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_naptr); + return 0; +} + +int SoaTraits::Send(QueryWrap<SoaTraits>* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_soa); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryMxWrap) - SET_SELF_SIZE(QueryMxWrap) +int AnyTraits::Parse( + QueryAnyWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + unsigned char* buf = response->buf.data; + int len = response->buf.size; - Local<Array> mx_records = Array::New(env()->isolate()); - int status = ParseMxReply(env(), buf, len, mx_records); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Local<Array> ret = Array::New(env->isolate()); + int type, status, old_count; + + /* Parse A records or CNAME records */ + ares_addrttl addrttls[256]; + int naddrttls = arraysize(addrttls); + + type = ns_t_cname_or_a; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addrttls, + &naddrttls); + uint32_t a_count = ret->Length(); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - this->CallOnComplete(mx_records); + if (type == ns_t_a) { + CHECK_EQ(static_cast<uint32_t>(naddrttls), a_count); + for (uint32_t i = 0; i < a_count; i++) { + Local<Object> obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->address_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->ttl_string(), + Integer::NewFromUnsigned( + env->isolate(), addrttls[i].ttl)).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_a_string()).Check(); + ret->Set(env->context(), i, obj).Check(); + } + } else { + for (uint32_t i = 0; i < a_count; i++) { + Local<Object> obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->value_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_cname_string()).Check(); + ret->Set(env->context(), i, obj).Check(); + } } -}; + /* Parse AAAA records */ + ares_addr6ttl addr6ttls[256]; + int naddr6ttls = arraysize(addr6ttls); + + type = ns_t_aaaa; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addr6ttls, + &naddr6ttls); + uint32_t aaaa_count = ret->Length() - a_count; + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; -class QueryNsWrap: public QueryWrap { - public: - QueryNsWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveNs") { - } + CHECK_EQ(aaaa_count, static_cast<uint32_t>(naddr6ttls)); + CHECK_EQ(ret->Length(), a_count + aaaa_count); + for (uint32_t i = a_count; i < ret->Length(); i++) { + Local<Object> obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->address_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->ttl_string(), + Integer::NewFromUnsigned( + env->isolate(), addr6ttls[i - a_count].ttl)).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_aaaa_string()).Check(); + ret->Set(env->context(), i, obj).Check(); + } + + /* Parse MX records */ + status = ParseMxReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; + + /* Parse NS records */ + type = ns_t_ns; + old_count = ret->Length(); + status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_ns); - return 0; + for (uint32_t i = old_count; i < ret->Length(); i++) { + Local<Object> obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->value_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_ns_string()).Check(); + ret->Set(env->context(), i, obj).Check(); } - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryNsWrap) - SET_SELF_SIZE(QueryNsWrap) + /* Parse TXT records */ + status = ParseTxtReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + /* Parse SRV records */ + status = ParseSrvReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - int type = ns_t_ns; - Local<Array> names = Array::New(env()->isolate()); - int status = ParseGeneralReply(env(), buf, len, &type, names); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + /* Parse PTR records */ + type = ns_t_ptr; + old_count = ret->Length(); + status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; + for (uint32_t i = old_count; i < ret->Length(); i++) { + Local<Object> obj = Object::New(env->isolate()); + obj->Set(env->context(), + env->value_string(), + ret->Get(env->context(), i).ToLocalChecked()).Check(); + obj->Set(env->context(), + env->type_string(), + env->dns_ptr_string()).Check(); + ret->Set(env->context(), i, obj).Check(); + } + + /* Parse NAPTR records */ + status = ParseNaptrReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - this->CallOnComplete(names); - } -}; + /* Parse SOA records */ + Local<Object> soa_record = Local<Object>(); + status = ParseSoaReply(env, buf, len, &soa_record); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; + if (!soa_record.IsEmpty()) + ret->Set(env->context(), ret->Length(), soa_record).Check(); -class QueryTxtWrap: public QueryWrap { - public: - QueryTxtWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveTxt") { - } + /* Parse CAA records */ + status = ParseCaaReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_txt); - return 0; - } + wrap->CallOnComplete(ret); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryTxtWrap) - SET_SELF_SIZE(QueryTxtWrap) +int ATraits::Parse( + QueryAWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + unsigned char* buf = response->buf.data; + int len = response->buf.size; - Local<Array> txt_records = Array::New(env()->isolate()); - int status = ParseTxtReply(env(), buf, len, txt_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - this->CallOnComplete(txt_records); - } -}; + ares_addrttl addrttls[256]; + int naddrttls = arraysize(addrttls), status; + Local<Array> ret = Array::New(env->isolate()); + + int type = ns_t_a; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addrttls, + &naddrttls); + if (status != ARES_SUCCESS) + return status; + Local<Array> ttls = AddrTTLToArray<ares_addrttl>(env, addrttls, naddrttls); -class QuerySrvWrap: public QueryWrap { - public: - explicit QuerySrvWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveSrv") { - } + wrap->CallOnComplete(ret, ttls); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_srv); - return 0; - } +int AaaaTraits::Parse( + QueryAaaaWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuerySrvWrap) - SET_SELF_SIZE(QuerySrvWrap) + unsigned char* buf = response->buf.data; + int len = response->buf.size; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - Local<Array> srv_records = Array::New(env()->isolate()); - int status = ParseSrvReply(env(), buf, len, srv_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + ares_addr6ttl addrttls[256]; + int naddrttls = arraysize(addrttls), status; + Local<Array> ret = Array::New(env->isolate()); + + int type = ns_t_aaaa; + status = ParseGeneralReply(env, + buf, + len, + &type, + ret, + addrttls, + &naddrttls); + if (status != ARES_SUCCESS) + return status; - this->CallOnComplete(srv_records); - } -}; + Local<Array> ttls = AddrTTLToArray<ares_addr6ttl>(env, addrttls, naddrttls); -class QueryPtrWrap: public QueryWrap { - public: - explicit QueryPtrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolvePtr") { - } + wrap->CallOnComplete(ret, ttls); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_ptr); - return 0; - } +int CaaTraits::Parse( + QueryCaaWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryPtrWrap) - SET_SELF_SIZE(QueryPtrWrap) + unsigned char* buf = response->buf.data; + int len = response->buf.size; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - int type = ns_t_ptr; - Local<Array> aliases = Array::New(env()->isolate()); + Local<Array> ret = Array::New(env->isolate()); + int status = ParseCaaReply(env, buf, len, ret); + if (status != ARES_SUCCESS) + return status; - int status = ParseGeneralReply(env(), buf, len, &type, aliases); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + wrap->CallOnComplete(ret); + return 0; +} - this->CallOnComplete(aliases); - } -}; +int CnameTraits::Parse( + QueryCnameWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; -class QueryNaptrWrap: public QueryWrap { - public: - explicit QueryNaptrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveNaptr") { - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_naptr); - return 0; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryNaptrWrap) - SET_SELF_SIZE(QueryNaptrWrap) + Local<Array> ret = Array::New(env->isolate()); + int type = ns_t_cname; + int status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS) + return status; - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + wrap->CallOnComplete(ret); + return 0; +} - Local<Array> naptr_records = Array::New(env()->isolate()); - int status = ParseNaptrReply(env(), buf, len, naptr_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } +int MxTraits::Parse( + QueryMxWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - this->CallOnComplete(naptr_records); - } -}; + unsigned char* buf = response->buf.data; + int len = response->buf.size; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); -class QuerySoaWrap: public QueryWrap { - public: - QuerySoaWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveSoa") { - } + Local<Array> mx_records = Array::New(env->isolate()); + int status = ParseMxReply(env, buf, len, mx_records); - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_soa); - return 0; - } + if (status != ARES_SUCCESS) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuerySoaWrap) - SET_SELF_SIZE(QuerySoaWrap) + wrap->CallOnComplete(mx_records); + return 0; +} - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - auto context = env()->context(); - Context::Scope context_scope(context); +int NsTraits::Parse( + QueryNsWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - ares_soa_reply* soa_out; - int status = ares_parse_soa_reply(buf, len, &soa_out); + unsigned char* buf = response->buf.data; + int len = response->buf.size; - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - Local<Object> soa_record = Object::New(env()->isolate()); - - soa_record->Set(context, - env()->nsname_string(), - OneByteString(env()->isolate(), - soa_out->nsname)).Check(); - soa_record->Set(context, - env()->hostmaster_string(), - OneByteString(env()->isolate(), - soa_out->hostmaster)).Check(); - soa_record->Set(context, - env()->serial_string(), - Integer::NewFromUnsigned( - env()->isolate(), soa_out->serial)).Check(); - soa_record->Set(context, - env()->refresh_string(), - Integer::New(env()->isolate(), - soa_out->refresh)).Check(); - soa_record->Set(context, - env()->retry_string(), - Integer::New(env()->isolate(), soa_out->retry)).Check(); - soa_record->Set(context, - env()->expire_string(), - Integer::New(env()->isolate(), soa_out->expire)).Check(); - soa_record->Set(context, - env()->minttl_string(), - Integer::NewFromUnsigned( - env()->isolate(), soa_out->minttl)).Check(); - - ares_free_data(soa_out); - - this->CallOnComplete(soa_record); - } -}; + int type = ns_t_ns; + Local<Array> names = Array::New(env->isolate()); + int status = ParseGeneralReply(env, buf, len, &type, names); + if (status != ARES_SUCCESS) + return status; + wrap->CallOnComplete(names); + return 0; +} -class GetHostByAddrWrap: public QueryWrap { - public: - explicit GetHostByAddrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "reverse") { - } +int TxtTraits::Parse( + QueryTxtWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int Send(const char* name) override { - int length, family; - char address_buffer[sizeof(struct in6_addr)]; + unsigned char* buf = response->buf.data; + int len = response->buf.size; - if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { - length = sizeof(struct in_addr); - family = AF_INET; - } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { - length = sizeof(struct in6_addr); - family = AF_INET6; - } else { - return UV_EINVAL; // So errnoException() reports a proper error. - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( - TRACING_CATEGORY_NODE2(dns, native), "reverse", this, - "name", TRACE_STR_COPY(name), - "family", family == AF_INET ? "ipv4" : "ipv6"); - - ares_gethostbyaddr(channel_->cares_channel(), - address_buffer, - length, - family, - Callback, - MakeCallbackPointer()); - return 0; - } + Local<Array> txt_records = Array::New(env->isolate()); + int status = ParseTxtReply(env, buf, len, txt_records); + if (status != ARES_SUCCESS) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetHostByAddrWrap) - SET_SELF_SIZE(GetHostByAddrWrap) + wrap->CallOnComplete(txt_records); + return 0; +} + +int SrvTraits::Parse( + QuerySrvWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - protected: - void Parse(struct hostent* host) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - this->CallOnComplete(HostentToNames(env(), host)); + unsigned char* buf = response->buf.data; + int len = response->buf.size; + + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local<Array> srv_records = Array::New(env->isolate()); + int status = ParseSrvReply(env, buf, len, srv_records); + if (status != ARES_SUCCESS) + return status; + + wrap->CallOnComplete(srv_records); + return 0; +} + +int PtrTraits::Parse( + QueryPtrWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; + + unsigned char* buf = response->buf.data; + int len = response->buf.size; + + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + int type = ns_t_ptr; + Local<Array> aliases = Array::New(env->isolate()); + + int status = ParseGeneralReply(env, buf, len, &type, aliases); + if (status != ARES_SUCCESS) + return status; + + wrap->CallOnComplete(aliases); + return 0; +} + +int NaptrTraits::Parse( + QueryNaptrWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; + + unsigned char* buf = response->buf.data; + int len = response->buf.size; + + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local<Array> naptr_records = Array::New(env->isolate()); + int status = ParseNaptrReply(env, buf, len, naptr_records); + if (status != ARES_SUCCESS) + return status; + + wrap->CallOnComplete(naptr_records); + return 0; +} + +int SoaTraits::Parse( + QuerySoaWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; + + unsigned char* buf = response->buf.data; + int len = response->buf.size; + + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + ares_soa_reply* soa_out; + int status = ares_parse_soa_reply(buf, len, &soa_out); + + if (status != ARES_SUCCESS) + return status; + + Local<Object> soa_record = Object::New(env->isolate()); + + soa_record->Set(env->context(), + env->nsname_string(), + OneByteString(env->isolate(), soa_out->nsname)).Check(); + soa_record->Set(env->context(), + env->hostmaster_string(), + OneByteString(env->isolate(), soa_out->hostmaster)).Check(); + soa_record->Set(env->context(), + env->serial_string(), + Integer::NewFromUnsigned( + env->isolate(), soa_out->serial)).Check(); + soa_record->Set(env->context(), + env->refresh_string(), + Integer::New(env->isolate(), soa_out->refresh)).Check(); + soa_record->Set(env->context(), + env->retry_string(), + Integer::New(env->isolate(), soa_out->retry)).Check(); + soa_record->Set(env->context(), + env->expire_string(), + Integer::New(env->isolate(), soa_out->expire)).Check(); + soa_record->Set(env->context(), + env->minttl_string(), + Integer::NewFromUnsigned( + env->isolate(), soa_out->minttl)).Check(); + + ares_free_data(soa_out); + + wrap->CallOnComplete(soa_record); + return 0; +} + +int ReverseTraits::Send(GetHostByAddrWrap* wrap, const char* name) { + int length, family; + char address_buffer[sizeof(struct in6_addr)]; + + if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { + length = sizeof(struct in_addr); + family = AF_INET; + } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { + length = sizeof(struct in6_addr); + family = AF_INET6; + } else { + return UV_EINVAL; // So errnoException() reports a proper error. } -}; + TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( + TRACING_CATEGORY_NODE2(dns, native), "reverse", wrap, + "name", TRACE_STR_COPY(name), + "family", family == AF_INET ? "ipv4" : "ipv6"); + + ares_gethostbyaddr( + wrap->channel()->cares_channel(), + address_buffer, + length, + family, + GetHostByAddrWrap::Callback, + wrap->MakeCallbackPointer()); + return 0; +} + +int ReverseTraits::Parse( + GetHostByAddrWrap* wrap, + const std::unique_ptr<ResponseData>& response) { + if (UNLIKELY(!response->is_host)) + return ARES_EBADRESP; + + struct hostent* host = response->host.get(); + + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + wrap->CallOnComplete(HostentToNames(env, host)); + return 0; +} +namespace { template <class Wrap> static void Query(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); @@ -2312,6 +1826,32 @@ void StrError(const FunctionCallbackInfo<Value>& args) { args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg)); } +} // namespace + +inline void safe_free_hostent(struct hostent* host) { + int idx; + + if (host->h_addr_list != nullptr) { + idx = 0; + while (host->h_addr_list[idx]) { + free(host->h_addr_list[idx++]); + } + free(host->h_addr_list); + host->h_addr_list = nullptr; + } + + if (host->h_aliases != nullptr) { + idx = 0; + while (host->h_aliases[idx]) { + free(host->h_aliases[idx++]); + } + free(host->h_aliases); + host->h_aliases = nullptr; + } + + free(host->h_name); + free(host); +} void Initialize(Local<Object> target, Local<Value> unused, @@ -2385,7 +1925,6 @@ void Initialize(Local<Object> target, env->SetConstructorFunction(target, "ChannelWrap", channel_wrap); } -} // anonymous namespace } // namespace cares_wrap } // namespace node |