From bc31dc0e0fab7b86be06e40a7ce3e5968d0e3753 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 8 Apr 2021 15:45:45 -0700 Subject: [PATCH] dns: refactor cares_wrap internals Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/38172 Reviewed-By: Matteo Collina --- src/cares_wrap.cc | 2113 ++++++++++++------------------ src/cares_wrap.h | 534 ++++++++ test/pummel/test-heapdump-dns.js | 2 +- 3 files changed, 1361 insertions(+), 1288 deletions(-) create mode 100644 src/cares_wrap.h diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index bd00cd25624a67..c0368739ff03ef 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 #include @@ -37,28 +38,6 @@ #include #include -#ifdef __POSIX__ -# include -#endif // __POSIX__ - -#if defined(__ANDROID__) || \ - defined(__MINGW32__) || \ - defined(__OpenBSD__) || \ - defined(_MSC_VER) - -# include -#else -# include -#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(p[0] << 8U) | (static_cast(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()(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; - -class ChannelWrap : public AsyncWrap { - public: - ChannelWrap(Environment* env, Local object, int timeout); - ~ChannelWrap() override; - - static void New(const FunctionCallbackInfo& 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, - 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& args) { - CHECK(args.IsConstructCall()); - CHECK_EQ(args.Length(), 1); - CHECK(args[0]->IsInt32()); - const int timeout = args[0].As()->Value(); - Environment* env = Environment::GetCurrent(args); - new ChannelWrap(env, args.This(), timeout); -} - -class GetAddrInfoReqWrap : public ReqWrap { - public: - GetAddrInfoReqWrap(Environment* env, - Local 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 req_wrap_obj, - bool verbatim) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) - , verbatim_(verbatim) { -} - - -class GetNameInfoReqWrap : public ReqWrap { - public: - GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) - SET_SELF_SIZE(GetNameInfoReqWrap) -}; - -GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, - Local 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(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 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(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 HostentToNames(Environment* env, - struct hostent* host, - Local append_to = Local()) { +Local HostentToNames(Environment* env, struct hostent* host) { EscapableHandleScope scope(env->isolate()); - auto context = env->context(); - bool append = !append_to.IsEmpty(); - Local 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 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; + std::vector> names; - /* copy `h_name` */ - size_t name_size = strlen(src->h_name) + 1; - dest->h_name = node::Malloc(name_size); - memcpy(dest->h_name, src->h_name, name_size); + for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) + names.emplace_back(OneByteString(env->isolate(), host->h_aliases[i])); - /* copy `h_aliases` */ - size_t alias_count; - for (alias_count = 0; - src->h_aliases[alias_count] != nullptr; - alias_count++) { - } - - dest->h_aliases = node::Malloc(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(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; + Local ret = Array::New(env->isolate(), names.data(), names.size()); - /* work after work */ - dest->h_length = src->h_length; - dest->h_addrtype = src->h_addrtype; + return scope.Escape(ret); } -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(this); - uv_timer_init(env()->event_loop(), timer_handle_); - } else if (uv_is_active(reinterpret_cast(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(); - } - - CloseTimer(); -} - - -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; - } - - 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; - } +Local HostentToNames(Environment* env, + struct hostent* host, + Local names) { + size_t offset = names->Length(); - /* 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 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 host; - MallocedBuffer 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 wrap_ptr { static_cast(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(answer_len); - memcpy(buf_copy, answer_buf, answer_len); - } - - wrap->response_data_ = std::make_unique(); - ResponseData* data = wrap->response_data_.get(); - data->status = status; - data->is_host = false; - data->buf = MallocedBuffer(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(1); - cares_wrap_hostent_cpy(host_copy, host); - } - - wrap->response_data_ = std::make_unique(); - 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 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 answer, - Local extra = Local()) { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - Local 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 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 channel_; - - private: - std::unique_ptr 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 -Local AddrTTLToArray(Environment* env, - const T* addrttls, - size_t naddrttls) { - auto isolate = env->isolate(); - +Local AddrTTLToArray( + Environment* env, + const T* addrttls, + size_t naddrttls) { MaybeStackBuffer, 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 ret, - void* addrttls = nullptr, - int* naddrttls = nullptr) { +int ParseGeneralReply( + Environment* env, + const unsigned char* buf, + int len, + int* type, + Local 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 ret, - bool need_type = false) { +int ParseMxReply( + Environment* env, + const unsigned char* buf, + int len, + Local 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 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 ret, - bool need_type = false) { +int ParseCaaReply( + Environment* env, + const unsigned char* buf, + int len, + Local 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 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 ret, - bool need_type = false) { +int ParseTxtReply( + Environment* env, + const unsigned char* buf, + int len, + Local 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 txt_chunk; @@ -953,13 +363,13 @@ int ParseTxtReply(Environment* env, if (!txt_chunk.IsEmpty()) { if (need_type) { Local 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 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 ret, - bool need_type = false) { +int ParseSrvReply( + Environment* env, + const unsigned char* buf, + int len, + Local 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 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 ret, - bool need_type = false) { +int ParseNaptrReply( + Environment* env, + const unsigned char* buf, + int len, + Local 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 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* ret) { +int ParseSoaReply( + Environment* env, + unsigned char* buf, + int len, + Local* ret) { EscapableHandleScope handle_scope(env->isolate()); - auto context = env->context(); // Manage memory using standardard smart pointer std::unique_tr struct AresDeleter { @@ -1172,694 +580,800 @@ int ParseSoaReply(Environment* env, const unsigned int minttl = ReadUint32BE(ptr + 4 * 4); Local 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(); - *ret = handle_scope.Escape(soa_record); - break; - } + *ret = handle_scope.Escape(soa_record); + break; + } + + ptr += rr_len; + } + + return ARES_SUCCESS; +} +} // anonymous namespace + +ChannelWrap::ChannelWrap(Environment* env, Local object, int timeout) + : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), + timeout_(timeout) { + MakeWeak(); + + Setup(); +} + +void ChannelWrap::MemoryInfo(MemoryTracker* tracker) const { + if (timer_handle_ != nullptr) + tracker->TrackField("timer_handle", *timer_handle_); + tracker->TrackField("task_list", task_list_, "NodeAresTask::List"); +} + +void ChannelWrap::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsInt32()); + const int timeout = args[0].As()->Value(); + Environment* env = Environment::GetCurrent(args); + new ChannelWrap(env, args.This(), timeout); +} + +GetAddrInfoReqWrap::GetAddrInfoReqWrap( + Environment* env, + Local req_wrap_obj, + bool verbatim) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP), + verbatim_(verbatim) {} + +GetNameInfoReqWrap::GetNameInfoReqWrap( + Environment* env, + Local 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(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 NodeAresTask::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("channel", channel); +} + +/* Allocates and returns a new NodeAresTask */ +NodeAresTask* NodeAresTask::Create(ChannelWrap* channel, ares_socket_t sock) { + auto task = new NodeAresTask(); + + 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; +} + +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(this); + uv_timer_init(env()->event_loop(), timer_handle_); + } else if (uv_is_active(reinterpret_cast(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(); + } + + CloseTimer(); +} + + +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; + } + + 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; + } + + ares_free_data(servers); + servers = nullptr; + + /* destroy channel and reset channel */ + ares_destroy(channel_); + + CloseTimer(); + Setup(); +} + +int AnyTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_any); + return 0; +} - ptr += rr_len; - } +int ATraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_a); + return 0; +} - return ARES_SUCCESS; +int AaaaTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_aaaa); + return 0; } +int CaaTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, T_CAA); + return 0; +} -class QueryAnyWrap: public QueryWrap { - public: - QueryAnyWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveAny") { - } +int CnameTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_cname); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_any); - return 0; - } +int MxTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_mx); + return 0; +} - 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 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; - } +int NsTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_ns); + return 0; +} - if (type == ns_t_a) { - CHECK_EQ(static_cast(naddrttls), a_count); - for (uint32_t i = 0; i < a_count; i++) { - Local 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 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(); - } - } +int TxtTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_txt); + return 0; +} - /* 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; - } +int SrvTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_srv); + return 0; +} - CHECK_EQ(aaaa_count, static_cast(naddr6ttls)); - CHECK_EQ(ret->Length(), a_count + aaaa_count); - for (uint32_t i = a_count; i < ret->Length(); i++) { - Local 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(); - } +int PtrTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_ptr); + return 0; +} - /* Parse MX records */ - status = ParseMxReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +int NaptrTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_naptr); + return 0; +} - /* 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 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(); - } +int SoaTraits::Send(QueryWrap* wrap, const char* name) { + wrap->AresQuery(name, ns_c_in, ns_t_soa); + return 0; +} - /* Parse TXT records */ - status = ParseTxtReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } +int AnyTraits::Parse( + QueryAnyWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - /* Parse SRV records */ - status = ParseSrvReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - /* 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 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(); - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - /* Parse NAPTR records */ - status = ParseNaptrReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; - } + Local 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; - /* Parse SOA records */ - Local soa_record = Local(); - status = ParseSoaReply(env(), buf, len, &soa_record); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; + if (type == ns_t_a) { + CHECK_EQ(static_cast(naddrttls), a_count); + for (uint32_t i = 0; i < a_count; i++) { + Local 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(); } - if (!soa_record.IsEmpty()) - ret->Set(context, ret->Length(), soa_record).Check(); - - /* Parse CAA records */ - status = ParseCaaReply(env(), buf, len, ret, true); - if (status != ARES_SUCCESS && status != ARES_ENODATA) { - ParseError(status); - return; + } else { + for (uint32_t i = 0; i < a_count; i++) { + Local 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(); } - - CallOnComplete(ret); } -}; + /* 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 QueryAWrap: public QueryWrap { - public: - QueryAWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolve4") { - } - - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_a); - return 0; - } - - 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 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; - } + CHECK_EQ(aaaa_count, static_cast(naddr6ttls)); + CHECK_EQ(ret->Length(), a_count + aaaa_count); + for (uint32_t i = a_count; i < ret->Length(); i++) { + Local 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; - Local ttls = AddrTTLToArray(env(), - addrttls, - naddrttls); + /* 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; - CallOnComplete(ret, ttls); + for (uint32_t i = old_count; i < ret->Length(); i++) { + Local 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(); } -}; - -class QueryAaaaWrap: public QueryWrap { - public: - QueryAaaaWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolve6") { - } + /* Parse TXT records */ + status = ParseTxtReply(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_aaaa); - return 0; - } + /* Parse SRV records */ + status = ParseSrvReply(env, buf, len, ret, true); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - 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 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; - } + /* 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 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; - Local ttls = AddrTTLToArray(env(), - addrttls, - naddrttls); + /* Parse SOA records */ + Local soa_record = Local(); + status = ParseSoaReply(env, buf, len, &soa_record); + if (status != ARES_SUCCESS && status != ARES_ENODATA) + return status; - CallOnComplete(ret, ttls); - } -}; + if (!soa_record.IsEmpty()) + ret->Set(env->context(), ret->Length(), soa_record).Check(); -class QueryCaaWrap: public QueryWrap { - public: - QueryCaaWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveCaa") { - } + /* 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, T_CAA); - return 0; - } + wrap->CallOnComplete(ret); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryAaaaWrap) - SET_SELF_SIZE(QueryAaaaWrap) +int ATraits::Parse( + QueryAWrap* wrap, + const std::unique_ptr& 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 ret = Array::New(env()->isolate()); - int status = ParseCaaReply(env(), buf, len, ret); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - this->CallOnComplete(ret); - } -}; + ares_addrttl addrttls[256]; + int naddrttls = arraysize(addrttls), status; + Local 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; -class QueryCnameWrap: public QueryWrap { - public: - QueryCnameWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveCname") { - } + Local ttls = AddrTTLToArray(env, addrttls, naddrttls); - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_cname); - return 0; - } + wrap->CallOnComplete(ret, ttls); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryCnameWrap) - SET_SELF_SIZE(QueryCnameWrap) +int AaaaTraits::Parse( + QueryAaaaWrap* wrap, + const std::unique_ptr& 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 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; - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - this->CallOnComplete(ret); - } -}; + ares_addr6ttl addrttls[256]; + int naddrttls = arraysize(addrttls), status; + Local 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; + Local ttls = AddrTTLToArray(env, addrttls, naddrttls); -class QueryMxWrap: public QueryWrap { - public: - QueryMxWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveMx") { - } + wrap->CallOnComplete(ret, ttls); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_mx); - return 0; - } +int CaaTraits::Parse( + QueryCaaWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryMxWrap) - SET_SELF_SIZE(QueryMxWrap) + 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 mx_records = Array::New(env()->isolate()); - int status = ParseMxReply(env(), buf, len, mx_records); + Local ret = Array::New(env->isolate()); + int status = ParseCaaReply(env, buf, len, ret); + if (status != ARES_SUCCESS) + return status; - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + wrap->CallOnComplete(ret); + return 0; +} - this->CallOnComplete(mx_records); - } -}; +int CnameTraits::Parse( + QueryCnameWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; + unsigned char* buf = response->buf.data; + int len = response->buf.size; -class QueryNsWrap: public QueryWrap { - public: - QueryNsWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveNs") { - } + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_ns); - return 0; - } + Local ret = Array::New(env->isolate()); + int type = ns_t_cname; + int status = ParseGeneralReply(env, buf, len, &type, ret); + if (status != ARES_SUCCESS) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryNsWrap) - SET_SELF_SIZE(QueryNsWrap) + wrap->CallOnComplete(ret); + return 0; +} - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); +int MxTraits::Parse( + QueryMxWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int type = ns_t_ns; - Local names = Array::New(env()->isolate()); - int status = ParseGeneralReply(env(), buf, len, &type, names); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - this->CallOnComplete(names); - } -}; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local mx_records = Array::New(env->isolate()); + int status = ParseMxReply(env, buf, len, mx_records); -class QueryTxtWrap: public QueryWrap { - public: - QueryTxtWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveTxt") { - } + if (status != ARES_SUCCESS) + return status; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_txt); - return 0; - } + wrap->CallOnComplete(mx_records); + return 0; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryTxtWrap) - SET_SELF_SIZE(QueryTxtWrap) +int NsTraits::Parse( + QueryNsWrap* wrap, + const std::unique_ptr& 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 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); - } -}; + int type = ns_t_ns; + Local 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 QuerySrvWrap: public QueryWrap { - public: - explicit QuerySrvWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveSrv") { - } +int TxtTraits::Parse( + QueryTxtWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_srv); - return 0; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuerySrvWrap) - SET_SELF_SIZE(QuerySrvWrap) + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); + Local txt_records = Array::New(env->isolate()); + int status = ParseTxtReply(env, buf, len, txt_records); + if (status != ARES_SUCCESS) + return status; - Local srv_records = Array::New(env()->isolate()); - int status = ParseSrvReply(env(), buf, len, srv_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + wrap->CallOnComplete(txt_records); + return 0; +} - this->CallOnComplete(srv_records); - } -}; +int SrvTraits::Parse( + QuerySrvWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; -class QueryPtrWrap: public QueryWrap { - public: - explicit QueryPtrWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolvePtr") { - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_ptr); - 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(QueryPtrWrap) - SET_SELF_SIZE(QueryPtrWrap) + Local srv_records = Array::New(env->isolate()); + int status = ParseSrvReply(env, buf, len, srv_records); + 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(srv_records); + return 0; +} - int type = ns_t_ptr; - Local aliases = Array::New(env()->isolate()); +int PtrTraits::Parse( + QueryPtrWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - int status = ParseGeneralReply(env(), buf, len, &type, aliases); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - this->CallOnComplete(aliases); - } -}; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); -class QueryNaptrWrap: public QueryWrap { - public: - explicit QueryNaptrWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveNaptr") { - } + int type = ns_t_ptr; + Local aliases = Array::New(env->isolate()); - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_naptr); - return 0; - } + int status = ParseGeneralReply(env, buf, len, &type, aliases); + if (status != ARES_SUCCESS) + return status; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QueryNaptrWrap) - SET_SELF_SIZE(QueryNaptrWrap) + wrap->CallOnComplete(aliases); + return 0; +} - protected: - void Parse(unsigned char* buf, int len) override { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); +int NaptrTraits::Parse( + QueryNaptrWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - Local naptr_records = Array::New(env()->isolate()); - int status = ParseNaptrReply(env(), buf, len, naptr_records); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + unsigned char* buf = response->buf.data; + int len = response->buf.size; - this->CallOnComplete(naptr_records); - } -}; + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local naptr_records = Array::New(env->isolate()); + int status = ParseNaptrReply(env, buf, len, naptr_records); + if (status != ARES_SUCCESS) + return status; -class QuerySoaWrap: public QueryWrap { - public: - QuerySoaWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "resolveSoa") { - } + wrap->CallOnComplete(naptr_records); + return 0; +} - int Send(const char* name) override { - AresQuery(name, ns_c_in, ns_t_soa); - return 0; - } +int SoaTraits::Parse( + QuerySoaWrap* wrap, + const std::unique_ptr& response) { + if (UNLIKELY(response->is_host)) + return ARES_EBADRESP; - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuerySoaWrap) - SET_SELF_SIZE(QuerySoaWrap) + 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()); - auto context = env()->context(); - Context::Scope context_scope(context); + 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); + ares_soa_reply* soa_out; + int status = ares_parse_soa_reply(buf, len, &soa_out); - if (status != ARES_SUCCESS) { - ParseError(status); - return; - } + if (status != ARES_SUCCESS) + return status; - Local 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); - } -}; + Local 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)]; -class GetHostByAddrWrap: public QueryWrap { - public: - explicit GetHostByAddrWrap(ChannelWrap* channel, Local req_wrap_obj) - : QueryWrap(channel, req_wrap_obj, "reverse") { + 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. } - int Send(const char* name) override { - 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", 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; - } + 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; +} - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(GetHostByAddrWrap) - SET_SELF_SIZE(GetHostByAddrWrap) +int ReverseTraits::Parse( + GetHostByAddrWrap* wrap, + const std::unique_ptr& 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)); - } -}; + 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 static void Query(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -2312,6 +1826,32 @@ void StrError(const FunctionCallbackInfo& 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 target, Local unused, @@ -2385,7 +1925,6 @@ void Initialize(Local target, env->SetConstructorFunction(target, "ChannelWrap", channel_wrap); } -} // anonymous namespace } // namespace cares_wrap } // namespace node diff --git a/src/cares_wrap.h b/src/cares_wrap.h new file mode 100644 index 00000000000000..bceafcb8b7f46b --- /dev/null +++ b/src/cares_wrap.h @@ -0,0 +1,534 @@ +#ifndef SRC_CARES_WRAP_H_ +#define SRC_CARES_WRAP_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#define CARES_STATICLIB + +#include "async_wrap.h" +#include "base_object.h" +#include "env.h" +#include "memory_tracker.h" +#include "util.h" +#include "node.h" + +#include "ares.h" +#include "v8.h" +#include "uv.h" + +#include + +#ifdef __POSIX__ +# include +#endif // __POSIX__ + +#if defined(__ANDROID__) || \ + defined(__MINGW32__) || \ + defined(__OpenBSD__) || \ + defined(_MSC_VER) + +# include +#else +# include +#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 { + +constexpr int ns_t_cname_or_a = -1; +constexpr int DNS_ESETSRVPENDING = -1000; + +class ChannelWrap; + +inline void safe_free_hostent(struct hostent* host); + +using HostEntPointer = DeleteFnPtr; +using SafeHostEntPointer = DeleteFnPtr; + +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"; +} + +inline 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(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(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(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; +} + + +struct NodeAresTask final : public MemoryRetainer { + ChannelWrap* channel; + ares_socket_t sock; + uv_poll_t poll_watcher; + + inline void MemoryInfo(MemoryTracker* trakcer) const override; + SET_MEMORY_INFO_NAME(NodeAresTask); + SET_SELF_SIZE(NodeAresTask); + + struct Hash { + inline size_t operator()(NodeAresTask* a) const { + return std::hash()(a->sock); + } + }; + + struct Equal { + inline bool operator()(NodeAresTask* a, NodeAresTask* b) const { + return a->sock == b->sock; + } + }; + + static NodeAresTask* Create(ChannelWrap* channel, ares_socket_t sock); + + using List = std::unordered_set; +}; + +class ChannelWrap final : public AsyncWrap { + public: + ChannelWrap(Environment* env, v8::Local object, int timeout); + ~ChannelWrap() override; + + static void New(const v8::FunctionCallbackInfo& 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 NodeAresTask::List* task_list() { return &task_list_; } + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(ChannelWrap) + SET_SELF_SIZE(ChannelWrap) + + static void AresTimeout(uv_timer_t* handle); + + private: + uv_timer_t* timer_handle_ = nullptr; + ares_channel channel_ = nullptr; + bool query_last_ok_ = true; + bool is_servers_default_ = true; + bool library_inited_ = false; + int timeout_; + int active_query_count_ = 0; + NodeAresTask::List task_list_; +}; + +class GetAddrInfoReqWrap final : public ReqWrap { + public: + GetAddrInfoReqWrap(Environment* env, + v8::Local 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_; +}; + +class GetNameInfoReqWrap final : public ReqWrap { + public: + GetNameInfoReqWrap(Environment* env, v8::Local req_wrap_obj); + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) + SET_SELF_SIZE(GetNameInfoReqWrap) +}; + +struct ResponseData final { + int status; + bool is_host; + SafeHostEntPointer host; + MallocedBuffer buf; +}; + +template +class QueryWrap final : public AsyncWrap { + public: + QueryWrap(ChannelWrap* channel, v8::Local req_wrap_obj) + : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP), + channel_(channel), + trace_name_(Traits::name) {} + + ~QueryWrap() { + CHECK_EQ(false, persistent().IsEmpty()); + + // Let Callback() know that this object no longer exists. + if (callback_ptr_ != nullptr) + *callback_ptr_ = nullptr; + } + + int Send(const char* name) { + return Traits::Send(this, name); + } + + 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()); + } + + void ParseError(int status) { + CHECK_NE(status, ARES_SUCCESS); + v8::HandleScope handle_scope(env()->isolate()); + v8::Context::Scope context_scope(env()->context()); + const char* code = ToErrorCodeString(status); + v8::Local 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); + } + + const BaseObjectPtr& channel() const { return channel_; } + + void AfterResponse() { + CHECK(response_data_); + + int status = response_data_->status; + + if (status != ARES_SUCCESS) + return ParseError(status); + + status = Traits::Parse(this, response_data_); + + if (status != ARES_SUCCESS) + ParseError(status); + } + + void* MakeCallbackPointer() { + CHECK_NULL(callback_ptr_); + callback_ptr_ = new QueryWrap*(this); + return callback_ptr_; + } + + static QueryWrap* FromCallbackPointer(void* arg) { + std::unique_ptr*> wrap_ptr { + static_cast**>(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(answer_len); + memcpy(buf_copy, answer_buf, answer_len); + } + + wrap->response_data_ = std::make_unique(); + ResponseData* data = wrap->response_data_.get(); + data->status = status; + data->is_host = false; + data->buf = MallocedBuffer(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(1); + cares_wrap_hostent_cpy(host_copy, host); + } + + wrap->response_data_ = std::make_unique(); + 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> 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( + v8::Local answer, + v8::Local extra = v8::Local()) { + v8::HandleScope handle_scope(env()->isolate()); + v8::Context::Scope context_scope(env()->context()); + v8::Local argv[] = { + v8::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 MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackField("channel", channel_); + if (response_data_) { + tracker->TrackFieldWithSize("response", response_data_->buf.size); + } + } + + SET_MEMORY_INFO_NAME(QueryWrap) + SET_SELF_SIZE(QueryWrap) + + private: + BaseObjectPtr channel_; + + std::unique_ptr 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; +}; + +struct AnyTraits final { + static constexpr const char* name = "resolveAny"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct ATraits final { + static constexpr const char* name = "resolve4"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct AaaaTraits final { + static constexpr const char* name = "resolve6"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct CaaTraits final { + static constexpr const char* name = "resolveCaa"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct CnameTraits final { + static constexpr const char* name = "resolveCname"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct MxTraits final { + static constexpr const char* name = "resolveMx"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct NsTraits final { + static constexpr const char* name = "resolveNs"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct TxtTraits final { + static constexpr const char* name = "resolveTxt"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct SrvTraits final { + static constexpr const char* name = "resolveSrv"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct PtrTraits final { + static constexpr const char* name = "resolvePtr"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct NaptrTraits final { + static constexpr const char* name = "resolveNaptr"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct SoaTraits final { + static constexpr const char* name = "resolveSoa"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +struct ReverseTraits final { + static constexpr const char* name = "reverse"; + static int Send(QueryWrap* wrap, const char* name); + static int Parse( + QueryWrap* wrap, + const std::unique_ptr& response); +}; + +using QueryAnyWrap = QueryWrap; +using QueryAWrap = QueryWrap; +using QueryAaaaWrap = QueryWrap; +using QueryCaaWrap = QueryWrap; +using QueryCnameWrap = QueryWrap; +using QueryMxWrap = QueryWrap; +using QueryNsWrap = QueryWrap; +using QueryTxtWrap = QueryWrap; +using QuerySrvWrap = QueryWrap; +using QueryPtrWrap = QueryWrap; +using QueryNaptrWrap = QueryWrap; +using QuerySoaWrap = QueryWrap; +using GetHostByAddrWrap = QueryWrap; + +} // namespace cares_wrap +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_CARES_WRAP_H_ diff --git a/test/pummel/test-heapdump-dns.js b/test/pummel/test-heapdump-dns.js index 68ce08d96e3242..675ddc09aca61a 100644 --- a/test/pummel/test-heapdump-dns.js +++ b/test/pummel/test-heapdump-dns.js @@ -10,7 +10,7 @@ dns.resolve('localhost', () => {}); validateSnapshotNodes('Node / ChannelWrap', [ { children: [ - { node_name: 'Node / node_ares_task_list', edge_name: 'task_list' }, + { node_name: 'Node / NodeAresTask::List', edge_name: 'task_list' }, // `Node / ChannelWrap` (C++) -> `ChannelWrap` (JS) { node_name: 'ChannelWrap', edge_name: 'wrapped' }, ]