| // Copyright Joyent, Inc. and other Node contributors. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a |
| // copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to permit |
| // persons to whom the Software is furnished to do so, subject to the |
| // following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| // 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 "env-inl.h" |
| #include "memory_tracker-inl.h" |
| #include "node.h" |
| #include "req_wrap-inl.h" |
| #include "util-inl.h" |
| #include "uv.h" |
| |
| #include <cerrno> |
| #include <cstring> |
| #include <memory> |
| #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 |
| |
| #if defined(__OpenBSD__) |
| # define AI_V4MAPPED 0 |
| #endif |
| |
| namespace node { |
| namespace cares_wrap { |
| |
| using v8::Array; |
| using v8::Context; |
| using v8::EscapableHandleScope; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::HandleScope; |
| using v8::Int32; |
| using v8::Integer; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Null; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| namespace { |
| |
| Mutex ares_library_mutex; |
| |
| inline uint16_t cares_get_16bit(const unsigned char* p) { |
| return static_cast<uint32_t>(p[0] << 8U) | (static_cast<uint32_t>(p[1])); |
| } |
| |
| inline uint32_t cares_get_32bit(const unsigned char* p) { |
| return static_cast<uint32_t>(p[0] << 24U) | |
| static_cast<uint32_t>(p[1] << 16U) | |
| static_cast<uint32_t>(p[2] << 8U) | |
| static_cast<uint32_t>(p[3]); |
| } |
| |
| 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); |
| ChannelWrap* channel = task->channel; |
| |
| /* Reset the idle timer */ |
| uv_timer_again(channel->timer_handle()); |
| |
| if (status < 0) { |
| /* An error happened. Just pretend that the socket is both readable and */ |
| /* writable. */ |
| ares_process_fd(channel->cares_channel(), task->sock, task->sock); |
| return; |
| } |
| |
| /* Process DNS responses */ |
| ares_process_fd(channel->cares_channel(), |
| events & UV_READABLE ? task->sock : ARES_SOCKET_BAD, |
| events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD); |
| } |
| |
| |
| 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; |
| } |
| |
| |
| /* Callback from ares when socket operation is started */ |
| void ares_sockstate_cb(void* data, |
| ares_socket_t sock, |
| int read, |
| int write) { |
| ChannelWrap* channel = static_cast<ChannelWrap*>(data); |
| node_ares_task* task; |
| |
| node_ares_task lookup_task; |
| lookup_task.sock = sock; |
| auto it = channel->task_list()->find(&lookup_task); |
| |
| task = (it == channel->task_list()->end()) ? nullptr : *it; |
| |
| if (read || write) { |
| if (!task) { |
| /* New socket */ |
| channel->StartTimer(); |
| |
| task = ares_task_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 */ |
| /* eventually time out. */ |
| return; |
| } |
| |
| channel->task_list()->insert(task); |
| } |
| |
| /* This should never fail. If it fails anyway, the query will eventually */ |
| /* time out. */ |
| uv_poll_start(&task->poll_watcher, |
| (read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0), |
| ares_poll_cb); |
| |
| } else { |
| /* read == 0 and write == 0 this is c-ares's way of notifying us that */ |
| /* the socket is now closed. We must free the data associated with */ |
| /* socket. */ |
| CHECK(task && |
| "When an ares socket is closed we should have a handle for it"); |
| |
| channel->task_list()->erase(it); |
| channel->env()->CloseHandle(&task->poll_watcher, ares_poll_close_cb); |
| |
| if (channel->task_list()->empty()) { |
| channel->CloseTimer(); |
| } |
| } |
| } |
| |
| |
| Local<Array> HostentToNames(Environment* env, |
| struct hostent* host, |
| Local<Array> append_to = Local<Array>()) { |
| 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(); |
| } |
| |
| 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(); |
| } |
| |
| |
| 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(); |
| |
| MaybeStackBuffer<Local<Value>, 8> ttls(naddrttls); |
| for (size_t i = 0; i < naddrttls; i++) |
| ttls[i] = Integer::NewFromUnsigned(isolate, addrttls[i].ttl); |
| |
| return Array::New(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) { |
| HandleScope handle_scope(env->isolate()); |
| auto context = env->context(); |
| hostent* host; |
| |
| int status; |
| switch (*type) { |
| case ns_t_a: |
| case ns_t_cname: |
| case ns_t_cname_or_a: |
| status = ares_parse_a_reply(buf, |
| len, |
| &host, |
| static_cast<ares_addrttl*>(addrttls), |
| naddrttls); |
| break; |
| case ns_t_aaaa: |
| status = ares_parse_aaaa_reply(buf, |
| len, |
| &host, |
| static_cast<ares_addr6ttl*>(addrttls), |
| naddrttls); |
| break; |
| case ns_t_ns: |
| status = ares_parse_ns_reply(buf, len, &host); |
| break; |
| case ns_t_ptr: |
| status = ares_parse_ptr_reply(buf, len, nullptr, 0, AF_INET, &host); |
| break; |
| default: |
| CHECK(0 && "Bad NS type"); |
| break; |
| } |
| |
| if (status != ARES_SUCCESS) |
| return status; |
| |
| /* 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]) || |
| *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->Length(), |
| OneByteString(env->isolate(), host->h_name)).Check(); |
| ares_free_hostent(host); |
| return ARES_SUCCESS; |
| } |
| |
| if (*type == ns_t_cname_or_a) |
| *type = ns_t_a; |
| |
| if (*type == ns_t_ns) { |
| HostentToNames(env, host, 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(); |
| } |
| } 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)); |
| auto address = OneByteString(env->isolate(), ip); |
| ret->Set(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) { |
| 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) { |
| 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, |
| env->exchange_string(), |
| OneByteString(env->isolate(), current->host)).Check(); |
| mx_record->Set(context, |
| env->priority_string(), |
| Integer::New(env->isolate(), current->priority)).Check(); |
| if (need_type) |
| mx_record->Set(context, |
| env->type_string(), |
| env->dns_mx_string()).Check(); |
| |
| ret->Set(context, i + offset, mx_record).Check(); |
| } |
| |
| ares_free_data(mx_start); |
| return ARES_SUCCESS; |
| } |
| |
| 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) { |
| return status; |
| } |
| |
| Local<Array> txt_chunk; |
| |
| struct ares_txt_ext* current = txt_out; |
| uint32_t i = 0, j; |
| uint32_t offset = ret->Length(); |
| for (j = 0; current != nullptr; current = current->next) { |
| Local<String> txt = |
| OneByteString(env->isolate(), current->txt, current->length); |
| |
| // New record found - write out the current chunk |
| if (current->record_start) { |
| 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, |
| env->type_string(), |
| env->dns_txt_string()).Check(); |
| ret->Set(context, offset + i++, elem).Check(); |
| } else { |
| ret->Set(context, offset + i++, txt_chunk).Check(); |
| } |
| } |
| |
| txt_chunk = Array::New(env->isolate()); |
| j = 0; |
| } |
| |
| txt_chunk->Set(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, |
| env->type_string(), |
| env->dns_txt_string()).Check(); |
| ret->Set(context, offset + i, elem).Check(); |
| } else { |
| ret->Set(context, offset + i, txt_chunk).Check(); |
| } |
| } |
| |
| ares_free_data(txt_out); |
| return ARES_SUCCESS; |
| } |
| |
| |
| 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) { |
| 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, |
| env->name_string(), |
| OneByteString(env->isolate(), current->host)).Check(); |
| srv_record->Set(context, |
| env->port_string(), |
| Integer::New(env->isolate(), current->port)).Check(); |
| srv_record->Set(context, |
| env->priority_string(), |
| Integer::New(env->isolate(), current->priority)).Check(); |
| srv_record->Set(context, |
| env->weight_string(), |
| Integer::New(env->isolate(), current->weight)).Check(); |
| if (need_type) |
| srv_record->Set(context, |
| env->type_string(), |
| env->dns_srv_string()).Check(); |
| |
| ret->Set(context, i + offset, srv_record).Check(); |
| } |
| |
| ares_free_data(srv_start); |
| return ARES_SUCCESS; |
| } |
| |
| |
| 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) { |
| 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, |
| env->flags_string(), |
| OneByteString(env->isolate(), current->flags)).Check(); |
| naptr_record->Set(context, |
| env->service_string(), |
| OneByteString(env->isolate(), |
| current->service)).Check(); |
| naptr_record->Set(context, |
| env->regexp_string(), |
| OneByteString(env->isolate(), |
| current->regexp)).Check(); |
| naptr_record->Set(context, |
| env->replacement_string(), |
| OneByteString(env->isolate(), |
| current->replacement)).Check(); |
| naptr_record->Set(context, |
| env->order_string(), |
| Integer::New(env->isolate(), current->order)).Check(); |
| naptr_record->Set(context, |
| env->preference_string(), |
| Integer::New(env->isolate(), |
| current->preference)).Check(); |
| if (need_type) |
| naptr_record->Set(context, |
| env->type_string(), |
| env->dns_naptr_string()).Check(); |
| |
| ret->Set(context, i + offset, naptr_record).Check(); |
| } |
| |
| ares_free_data(naptr_start); |
| return ARES_SUCCESS; |
| } |
| |
| |
| 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 { |
| void operator()(char* ptr) const noexcept { ares_free_string(ptr); } |
| }; |
| using ares_unique_ptr = std::unique_ptr<char[], AresDeleter>; |
| |
| // Can't use ares_parse_soa_reply() here which can only parse single record |
| const unsigned int ancount = cares_get_16bit(buf + 6); |
| unsigned char* ptr = buf + NS_HFIXEDSZ; |
| char* name_temp; |
| long temp_len; // NOLINT(runtime/int) |
| int status = ares_expand_name(ptr, buf, len, &name_temp, &temp_len); |
| const ares_unique_ptr name(name_temp); |
| if (status != ARES_SUCCESS) { |
| // returns EBADRESP in case of invalid input |
| return status == ARES_EBADNAME ? ARES_EBADRESP : status; |
| } |
| |
| if (ptr + temp_len + NS_QFIXEDSZ > buf + len) { |
| return ARES_EBADRESP; |
| } |
| ptr += temp_len + NS_QFIXEDSZ; |
| |
| for (unsigned int i = 0; i < ancount; i++) { |
| char* rr_name_temp; |
| long rr_temp_len; // NOLINT(runtime/int) |
| int status2 = ares_expand_name(ptr, buf, len, &rr_name_temp, &rr_temp_len); |
| const ares_unique_ptr rr_name(rr_name_temp); |
| |
| if (status2 != ARES_SUCCESS) |
| return status2 == ARES_EBADNAME ? ARES_EBADRESP : status2; |
| |
| ptr += rr_temp_len; |
| if (ptr + NS_RRFIXEDSZ > buf + len) { |
| return ARES_EBADRESP; |
| } |
| |
| const int rr_type = cares_get_16bit(ptr); |
| const int rr_len = cares_get_16bit(ptr + 8); |
| ptr += NS_RRFIXEDSZ; |
| |
| // only need SOA |
| if (rr_type == ns_t_soa) { |
| char* nsname_temp; |
| long nsname_temp_len; // NOLINT(runtime/int) |
| |
| int status3 = ares_expand_name(ptr, buf, len, |
| &nsname_temp, |
| &nsname_temp_len); |
| const ares_unique_ptr nsname(nsname_temp); |
| if (status3 != ARES_SUCCESS) { |
| return status3 == ARES_EBADNAME ? ARES_EBADRESP : status3; |
| } |
| ptr += nsname_temp_len; |
| |
| char* hostmaster_temp; |
| long hostmaster_temp_len; // NOLINT(runtime/int) |
| int status4 = ares_expand_name(ptr, buf, len, |
| &hostmaster_temp, |
| &hostmaster_temp_len); |
| const ares_unique_ptr hostmaster(hostmaster_temp); |
| if (status4 != ARES_SUCCESS) { |
| return status4 == ARES_EBADNAME ? ARES_EBADRESP : status4; |
| } |
| ptr += hostmaster_temp_len; |
| |
| if (ptr + 5 * 4 > buf + len) { |
| return ARES_EBADRESP; |
| } |
| |
| const unsigned int serial = cares_get_32bit(ptr + 0 * 4); |
| const unsigned int refresh = cares_get_32bit(ptr + 1 * 4); |
| const unsigned int retry = cares_get_32bit(ptr + 2 * 4); |
| const unsigned int expire = cares_get_32bit(ptr + 3 * 4); |
| const unsigned int minttl = cares_get_32bit(ptr + 4 * 4); |
| |
| Local<Object> soa_record = Object::New(env->isolate()); |
| soa_record->Set(context, |
| env->nsname_string(), |
| OneByteString(env->isolate(), nsname.get())).Check(); |
| soa_record->Set(context, |
| env->hostmaster_string(), |
| OneByteString(env->isolate(), |
| hostmaster.get())).Check(); |
| soa_record->Set(context, |
| env->serial_string(), |
| Integer::NewFromUnsigned(env->isolate(), serial)).Check(); |
| soa_record->Set(context, |
| env->refresh_string(), |
| Integer::New(env->isolate(), refresh)).Check(); |
| soa_record->Set(context, |
| env->retry_string(), |
| Integer::New(env->isolate(), retry)).Check(); |
| soa_record->Set(context, |
| env->expire_string(), |
| Integer::New(env->isolate(), expire)).Check(); |
| soa_record->Set(context, |
| env->minttl_string(), |
| Integer::NewFromUnsigned(env->isolate(), minttl)).Check(); |
| soa_record->Set(context, |
| env->type_string(), |
| env->dns_soa_string()).Check(); |
| |
| |
| *ret = handle_scope.Escape(soa_record); |
| break; |
| } |
| |
| ptr += rr_len; |
| } |
| |
| return ARES_SUCCESS; |
| } |
| |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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(); |
| } |
| } |
| |
| /* 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; |
| } |
| |
| 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(); |
| } |
| |
| /* Parse MX records */ |
| status = ParseMxReply(env(), buf, len, ret, true); |
| if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
| ParseError(status); |
| return; |
| } |
| |
| /* 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; |
| } |
| |
| /* Parse SRV records */ |
| status = ParseSrvReply(env(), buf, len, ret, true); |
| if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
| return; |
| } |
| |
| /* 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(); |
| } |
| |
| /* Parse NAPTR records */ |
| status = ParseNaptrReply(env(), buf, len, ret, true); |
| if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
| ParseError(status); |
| return; |
| } |
| |
| /* 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(); |
| |
| CallOnComplete(ret); |
| } |
| }; |
| |
| |
| class QueryAWrap: public QueryWrap { |
| public: |
| QueryAWrap(ChannelWrap* channel, Local<Object> 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<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; |
| } |
| |
| Local<Array> ttls = AddrTTLToArray<ares_addrttl>(env(), |
| addrttls, |
| naddrttls); |
| |
| CallOnComplete(ret, ttls); |
| } |
| }; |
| |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| Local<Array> ttls = AddrTTLToArray<ares_addr6ttl>(env(), |
| addrttls, |
| naddrttls); |
| |
| CallOnComplete(ret, ttls); |
| } |
| }; |
| |
| |
| class QueryCnameWrap: public QueryWrap { |
| public: |
| QueryCnameWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveCname") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_cname); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QueryCnameWrap) |
| SET_SELF_SIZE(QueryCnameWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| HandleScope handle_scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| |
| 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; |
| } |
| |
| this->CallOnComplete(ret); |
| } |
| }; |
| |
| |
| class QueryMxWrap: public QueryWrap { |
| public: |
| QueryMxWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveMx") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_mx); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QueryMxWrap) |
| SET_SELF_SIZE(QueryMxWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| HandleScope handle_scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| |
| Local<Array> mx_records = Array::New(env()->isolate()); |
| int status = ParseMxReply(env(), buf, len, mx_records); |
| |
| if (status != ARES_SUCCESS) { |
| ParseError(status); |
| return; |
| } |
| |
| this->CallOnComplete(mx_records); |
| } |
| }; |
| |
| |
| class QueryNsWrap: public QueryWrap { |
| public: |
| QueryNsWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveNs") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_ns); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QueryNsWrap) |
| SET_SELF_SIZE(QueryNsWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| HandleScope handle_scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| |
| 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; |
| } |
| |
| this->CallOnComplete(names); |
| } |
| }; |
| |
| |
| class QueryTxtWrap: public QueryWrap { |
| public: |
| QueryTxtWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveTxt") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_txt); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QueryTxtWrap) |
| SET_SELF_SIZE(QueryTxtWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| HandleScope handle_scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| |
| Local<Array> txt_records = Array::New(env()->isolate()); |
| int status = ParseTxtReply(env(), buf, len, txt_records); |
| if (status != ARES_SUCCESS) { |
| ParseError(status); |
| return; |
| } |
| |
| this->CallOnComplete(txt_records); |
| } |
| }; |
| |
| |
| class QuerySrvWrap: public QueryWrap { |
| public: |
| explicit QuerySrvWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveSrv") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_srv); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QuerySrvWrap) |
| SET_SELF_SIZE(QuerySrvWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| 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; |
| } |
| |
| this->CallOnComplete(srv_records); |
| } |
| }; |
| |
| class QueryPtrWrap: public QueryWrap { |
| public: |
| explicit QueryPtrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolvePtr") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_ptr); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QueryPtrWrap) |
| SET_SELF_SIZE(QueryPtrWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| 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) { |
| ParseError(status); |
| return; |
| } |
| |
| this->CallOnComplete(aliases); |
| } |
| }; |
| |
| class QueryNaptrWrap: public QueryWrap { |
| public: |
| explicit QueryNaptrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveNaptr") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_naptr); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QueryNaptrWrap) |
| SET_SELF_SIZE(QueryNaptrWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| 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) { |
| ParseError(status); |
| return; |
| } |
| |
| this->CallOnComplete(naptr_records); |
| } |
| }; |
| |
| |
| class QuerySoaWrap: public QueryWrap { |
| public: |
| QuerySoaWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "resolveSoa") { |
| } |
| |
| int Send(const char* name) override { |
| AresQuery(name, ns_c_in, ns_t_soa); |
| return 0; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(QuerySoaWrap) |
| SET_SELF_SIZE(QuerySoaWrap) |
| |
| protected: |
| void Parse(unsigned char* buf, int len) override { |
| HandleScope handle_scope(env()->isolate()); |
| auto context = env()->context(); |
| Context::Scope context_scope(context); |
| |
| ares_soa_reply* soa_out; |
| int status = ares_parse_soa_reply(buf, len, &soa_out); |
| |
| if (status != ARES_SUCCESS) { |
| ParseError(status); |
| return; |
| } |
| |
| 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); |
| } |
| }; |
| |
| |
| class GetHostByAddrWrap: public QueryWrap { |
| public: |
| explicit GetHostByAddrWrap(ChannelWrap* channel, Local<Object> req_wrap_obj) |
| : QueryWrap(channel, req_wrap_obj, "reverse") { |
| } |
| |
| 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; |
| } |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(GetHostByAddrWrap) |
| SET_SELF_SIZE(GetHostByAddrWrap) |
| |
| protected: |
| void Parse(struct hostent* host) override { |
| HandleScope handle_scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| this->CallOnComplete(HostentToNames(env(), host)); |
| } |
| }; |
| |
| |
| template <class Wrap> |
| static void Query(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| ChannelWrap* channel; |
| ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); |
| |
| CHECK_EQ(false, args.IsConstructCall()); |
| CHECK(args[0]->IsObject()); |
| CHECK(args[1]->IsString()); |
| |
| Local<Object> req_wrap_obj = args[0].As<Object>(); |
| Local<String> string = args[1].As<String>(); |
| auto wrap = std::make_unique<Wrap>(channel, req_wrap_obj); |
| |
| node::Utf8Value name(env->isolate(), string); |
| channel->ModifyActivityQueryCount(1); |
| int err = wrap->Send(*name); |
| if (err) { |
| channel->ModifyActivityQueryCount(-1); |
| } else { |
| // Release ownership of the pointer allowing the ownership to be transferred |
| USE(wrap.release()); |
| } |
| |
| args.GetReturnValue().Set(err); |
| } |
| |
| |
| void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { |
| std::unique_ptr<GetAddrInfoReqWrap> req_wrap { |
| static_cast<GetAddrInfoReqWrap*>(req->data)}; |
| Environment* env = req_wrap->env(); |
| |
| HandleScope handle_scope(env->isolate()); |
| Context::Scope context_scope(env->context()); |
| |
| Local<Value> argv[] = { |
| Integer::New(env->isolate(), status), |
| Null(env->isolate()) |
| }; |
| |
| uint64_t n = 0; |
| const bool verbatim = req_wrap->verbatim(); |
| |
| if (status == 0) { |
| Local<Array> results = Array::New(env->isolate()); |
| |
| auto add = [&] (bool want_ipv4, bool want_ipv6) { |
| for (auto p = res; p != nullptr; p = p->ai_next) { |
| CHECK_EQ(p->ai_socktype, SOCK_STREAM); |
| |
| const char* addr; |
| if (want_ipv4 && p->ai_family == AF_INET) { |
| addr = reinterpret_cast<char*>( |
| &(reinterpret_cast<struct sockaddr_in*>(p->ai_addr)->sin_addr)); |
| } else if (want_ipv6 && p->ai_family == AF_INET6) { |
| addr = reinterpret_cast<char*>( |
| &(reinterpret_cast<struct sockaddr_in6*>(p->ai_addr)->sin6_addr)); |
| } else { |
| continue; |
| } |
| |
| char ip[INET6_ADDRSTRLEN]; |
| if (uv_inet_ntop(p->ai_family, addr, ip, sizeof(ip))) |
| continue; |
| |
| Local<String> s = OneByteString(env->isolate(), ip); |
| results->Set(env->context(), n, s).Check(); |
| n++; |
| } |
| }; |
| |
| add(true, verbatim); |
| if (verbatim == false) |
| add(false, true); |
| |
| // No responses were found to return |
| if (n == 0) { |
| argv[0] = Integer::New(env->isolate(), UV_EAI_NODATA); |
| } |
| |
| argv[1] = results; |
| } |
| |
| uv_freeaddrinfo(res); |
| |
| TRACE_EVENT_NESTABLE_ASYNC_END2( |
| TRACING_CATEGORY_NODE2(dns, native), "lookup", req_wrap.get(), |
| "count", n, "verbatim", verbatim); |
| |
| // Make the callback into JavaScript |
| req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); |
| } |
| |
| |
| void AfterGetNameInfo(uv_getnameinfo_t* req, |
| int status, |
| const char* hostname, |
| const char* service) { |
| std::unique_ptr<GetNameInfoReqWrap> req_wrap { |
| static_cast<GetNameInfoReqWrap*>(req->data)}; |
| Environment* env = req_wrap->env(); |
| |
| HandleScope handle_scope(env->isolate()); |
| Context::Scope context_scope(env->context()); |
| |
| Local<Value> argv[] = { |
| Integer::New(env->isolate(), status), |
| Null(env->isolate()), |
| Null(env->isolate()) |
| }; |
| |
| if (status == 0) { |
| // Success |
| Local<String> js_hostname = OneByteString(env->isolate(), hostname); |
| Local<String> js_service = OneByteString(env->isolate(), service); |
| argv[1] = js_hostname; |
| argv[2] = js_service; |
| } |
| |
| TRACE_EVENT_NESTABLE_ASYNC_END2( |
| TRACING_CATEGORY_NODE2(dns, native), "lookupService", req_wrap.get(), |
| "hostname", TRACE_STR_COPY(hostname), |
| "service", TRACE_STR_COPY(service)); |
| |
| // Make the callback into JavaScript |
| req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); |
| } |
| |
| using ParseIPResult = |
| decltype(static_cast<ares_addr_port_node*>(nullptr)->addr); |
| |
| int ParseIP(const char* ip, ParseIPResult* result = nullptr) { |
| ParseIPResult tmp; |
| if (result == nullptr) result = &tmp; |
| if (0 == uv_inet_pton(AF_INET, ip, result)) return 4; |
| if (0 == uv_inet_pton(AF_INET6, ip, result)) return 6; |
| return 0; |
| } |
| |
| void CanonicalizeIP(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| node::Utf8Value ip(isolate, args[0]); |
| |
| ParseIPResult result; |
| const int rc = ParseIP(*ip, &result); |
| if (rc == 0) return; |
| |
| char canonical_ip[INET6_ADDRSTRLEN]; |
| const int af = (rc == 4 ? AF_INET : AF_INET6); |
| CHECK_EQ(0, uv_inet_ntop(af, &result, canonical_ip, sizeof(canonical_ip))); |
| Local<String> val = String::NewFromUtf8(isolate, canonical_ip) |
| .ToLocalChecked(); |
| args.GetReturnValue().Set(val); |
| } |
| |
| void GetAddrInfo(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| |
| CHECK(args[0]->IsObject()); |
| CHECK(args[1]->IsString()); |
| CHECK(args[2]->IsInt32()); |
| CHECK(args[4]->IsBoolean()); |
| Local<Object> req_wrap_obj = args[0].As<Object>(); |
| node::Utf8Value hostname(env->isolate(), args[1]); |
| |
| int32_t flags = 0; |
| if (args[3]->IsInt32()) { |
| flags = args[3].As<Int32>()->Value(); |
| } |
| |
| int family; |
| |
| switch (args[2].As<Int32>()->Value()) { |
| case 0: |
| family = AF_UNSPEC; |
| break; |
| case 4: |
| family = AF_INET; |
| break; |
| case 6: |
| family = AF_INET6; |
| break; |
| default: |
| CHECK(0 && "bad address family"); |
| } |
| |
| auto req_wrap = std::make_unique<GetAddrInfoReqWrap>(env, |
| req_wrap_obj, |
| args[4]->IsTrue()); |
| |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = family; |
| hints.ai_socktype = SOCK_STREAM; |
| hints.ai_flags = flags; |
| |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( |
| TRACING_CATEGORY_NODE2(dns, native), "lookup", req_wrap.get(), |
| "hostname", TRACE_STR_COPY(*hostname), |
| "family", |
| family == AF_INET ? "ipv4" : family == AF_INET6 ? "ipv6" : "unspec"); |
| |
| int err = req_wrap->Dispatch(uv_getaddrinfo, |
| AfterGetAddrInfo, |
| *hostname, |
| nullptr, |
| &hints); |
| if (err == 0) |
| // Release ownership of the pointer allowing the ownership to be transferred |
| USE(req_wrap.release()); |
| |
| args.GetReturnValue().Set(err); |
| } |
| |
| |
| void GetNameInfo(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| |
| CHECK(args[0]->IsObject()); |
| CHECK(args[1]->IsString()); |
| CHECK(args[2]->IsUint32()); |
| Local<Object> req_wrap_obj = args[0].As<Object>(); |
| node::Utf8Value ip(env->isolate(), args[1]); |
| const unsigned port = args[2]->Uint32Value(env->context()).FromJust(); |
| struct sockaddr_storage addr; |
| |
| CHECK(uv_ip4_addr(*ip, port, reinterpret_cast<sockaddr_in*>(&addr)) == 0 || |
| uv_ip6_addr(*ip, port, reinterpret_cast<sockaddr_in6*>(&addr)) == 0); |
| |
| auto req_wrap = std::make_unique<GetNameInfoReqWrap>(env, req_wrap_obj); |
| |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( |
| TRACING_CATEGORY_NODE2(dns, native), "lookupService", req_wrap.get(), |
| "ip", TRACE_STR_COPY(*ip), "port", port); |
| |
| int err = req_wrap->Dispatch(uv_getnameinfo, |
| AfterGetNameInfo, |
| reinterpret_cast<struct sockaddr*>(&addr), |
| NI_NAMEREQD); |
| if (err == 0) |
| // Release ownership of the pointer allowing the ownership to be transferred |
| USE(req_wrap.release()); |
| |
| args.GetReturnValue().Set(err); |
| } |
| |
| |
| void GetServers(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| ChannelWrap* channel; |
| ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); |
| |
| Local<Array> server_array = Array::New(env->isolate()); |
| |
| ares_addr_port_node* servers; |
| |
| int r = ares_get_servers_ports(channel->cares_channel(), &servers); |
| CHECK_EQ(r, ARES_SUCCESS); |
| auto cleanup = OnScopeLeave([&]() { ares_free_data(servers); }); |
| |
| ares_addr_port_node* cur = servers; |
| |
| for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) { |
| char ip[INET6_ADDRSTRLEN]; |
| |
| const void* caddr = static_cast<const void*>(&cur->addr); |
| int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); |
| CHECK_EQ(err, 0); |
| |
| Local<Value> ret[] = { |
| OneByteString(env->isolate(), ip), |
| Integer::New(env->isolate(), cur->udp_port) |
| }; |
| |
| if (server_array->Set(env->context(), i, |
| Array::New(env->isolate(), ret, arraysize(ret))) |
| .IsNothing()) { |
| return; |
| } |
| } |
| |
| args.GetReturnValue().Set(server_array); |
| } |
| |
| |
| void SetServers(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| ChannelWrap* channel; |
| ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); |
| |
| if (channel->active_query_count()) { |
| return args.GetReturnValue().Set(DNS_ESETSRVPENDING); |
| } |
| |
| CHECK(args[0]->IsArray()); |
| |
| Local<Array> arr = Local<Array>::Cast(args[0]); |
| |
| uint32_t len = arr->Length(); |
| |
| if (len == 0) { |
| int rv = ares_set_servers(channel->cares_channel(), nullptr); |
| return args.GetReturnValue().Set(rv); |
| } |
| |
| std::vector<ares_addr_port_node> servers(len); |
| ares_addr_port_node* last = nullptr; |
| |
| int err; |
| |
| for (uint32_t i = 0; i < len; i++) { |
| CHECK(arr->Get(env->context(), i).ToLocalChecked()->IsArray()); |
| |
| Local<Array> elm = |
| Local<Array>::Cast(arr->Get(env->context(), i).ToLocalChecked()); |
| |
| CHECK(elm->Get(env->context(), |
| 0).ToLocalChecked()->Int32Value(env->context()).FromJust()); |
| CHECK(elm->Get(env->context(), 1).ToLocalChecked()->IsString()); |
| CHECK(elm->Get(env->context(), |
| 2).ToLocalChecked()->Int32Value(env->context()).FromJust()); |
| |
| int fam = elm->Get(env->context(), 0) |
| .ToLocalChecked()->Int32Value(env->context()).FromJust(); |
| node::Utf8Value ip(env->isolate(), |
| elm->Get(env->context(), 1).ToLocalChecked()); |
| int port = elm->Get(env->context(), 2) |
| .ToLocalChecked()->Int32Value(env->context()).FromJust(); |
| |
| ares_addr_port_node* cur = &servers[i]; |
| |
| cur->tcp_port = cur->udp_port = port; |
| switch (fam) { |
| case 4: |
| cur->family = AF_INET; |
| err = uv_inet_pton(AF_INET, *ip, &cur->addr); |
| break; |
| case 6: |
| cur->family = AF_INET6; |
| err = uv_inet_pton(AF_INET6, *ip, &cur->addr); |
| break; |
| default: |
| CHECK(0 && "Bad address family."); |
| } |
| |
| if (err) |
| break; |
| |
| cur->next = nullptr; |
| |
| if (last != nullptr) |
| last->next = cur; |
| |
| last = cur; |
| } |
| |
| if (err == 0) |
| err = ares_set_servers_ports(channel->cares_channel(), &servers[0]); |
| else |
| err = ARES_EBADSTR; |
| |
| if (err == ARES_SUCCESS) |
| channel->set_is_servers_default(false); |
| |
| args.GetReturnValue().Set(err); |
| } |
| |
| void Cancel(const FunctionCallbackInfo<Value>& args) { |
| ChannelWrap* channel; |
| ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); |
| |
| TRACE_EVENT_INSTANT0(TRACING_CATEGORY_NODE2(dns, native), |
| "cancel", TRACE_EVENT_SCOPE_THREAD); |
| |
| ares_cancel(channel->cares_channel()); |
| } |
| |
| const char EMSG_ESETSRVPENDING[] = "There are pending queries."; |
| void StrError(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| int code = args[0]->Int32Value(env->context()).FromJust(); |
| const char* errmsg = (code == DNS_ESETSRVPENDING) ? |
| EMSG_ESETSRVPENDING : |
| ares_strerror(code); |
| args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg)); |
| } |
| |
| |
| void Initialize(Local<Object> target, |
| Local<Value> unused, |
| Local<Context> context, |
| void* priv) { |
| Environment* env = Environment::GetCurrent(context); |
| |
| env->SetMethod(target, "getaddrinfo", GetAddrInfo); |
| env->SetMethod(target, "getnameinfo", GetNameInfo); |
| env->SetMethodNoSideEffect(target, "canonicalizeIP", CanonicalizeIP); |
| |
| env->SetMethod(target, "strerror", StrError); |
| |
| target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"), |
| Integer::New(env->isolate(), AF_INET)).Check(); |
| target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET6"), |
| Integer::New(env->isolate(), AF_INET6)).Check(); |
| target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), |
| "AF_UNSPEC"), |
| Integer::New(env->isolate(), AF_UNSPEC)).Check(); |
| target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), |
| "AI_ADDRCONFIG"), |
| Integer::New(env->isolate(), AI_ADDRCONFIG)).Check(); |
| target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), |
| "AI_ALL"), |
| Integer::New(env->isolate(), AI_ALL)).Check(); |
| target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), |
| "AI_V4MAPPED"), |
| Integer::New(env->isolate(), AI_V4MAPPED)).Check(); |
| |
| Local<FunctionTemplate> aiw = |
| BaseObject::MakeLazilyInitializedJSTemplate(env); |
| aiw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
| Local<String> addrInfoWrapString = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"); |
| aiw->SetClassName(addrInfoWrapString); |
| target->Set(env->context(), |
| addrInfoWrapString, |
| aiw->GetFunction(context).ToLocalChecked()).Check(); |
| |
| Local<FunctionTemplate> niw = |
| BaseObject::MakeLazilyInitializedJSTemplate(env); |
| niw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
| Local<String> nameInfoWrapString = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"); |
| niw->SetClassName(nameInfoWrapString); |
| target->Set(env->context(), |
| nameInfoWrapString, |
| niw->GetFunction(context).ToLocalChecked()).Check(); |
| |
| Local<FunctionTemplate> qrw = |
| BaseObject::MakeLazilyInitializedJSTemplate(env); |
| qrw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
| Local<String> queryWrapString = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"); |
| qrw->SetClassName(queryWrapString); |
| target->Set(env->context(), |
| queryWrapString, |
| qrw->GetFunction(context).ToLocalChecked()).Check(); |
| |
| Local<FunctionTemplate> channel_wrap = |
| env->NewFunctionTemplate(ChannelWrap::New); |
| channel_wrap->InstanceTemplate()->SetInternalFieldCount( |
| ChannelWrap::kInternalFieldCount); |
| channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
| |
| env->SetProtoMethod(channel_wrap, "queryAny", Query<QueryAnyWrap>); |
| env->SetProtoMethod(channel_wrap, "queryA", Query<QueryAWrap>); |
| env->SetProtoMethod(channel_wrap, "queryAaaa", Query<QueryAaaaWrap>); |
| env->SetProtoMethod(channel_wrap, "queryCname", Query<QueryCnameWrap>); |
| env->SetProtoMethod(channel_wrap, "queryMx", Query<QueryMxWrap>); |
| env->SetProtoMethod(channel_wrap, "queryNs", Query<QueryNsWrap>); |
| env->SetProtoMethod(channel_wrap, "queryTxt", Query<QueryTxtWrap>); |
| env->SetProtoMethod(channel_wrap, "querySrv", Query<QuerySrvWrap>); |
| env->SetProtoMethod(channel_wrap, "queryPtr", Query<QueryPtrWrap>); |
| env->SetProtoMethod(channel_wrap, "queryNaptr", Query<QueryNaptrWrap>); |
| env->SetProtoMethod(channel_wrap, "querySoa", Query<QuerySoaWrap>); |
| env->SetProtoMethod(channel_wrap, "getHostByAddr", Query<GetHostByAddrWrap>); |
| |
| env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); |
| env->SetProtoMethod(channel_wrap, "setServers", SetServers); |
| env->SetProtoMethod(channel_wrap, "cancel", Cancel); |
| |
| Local<String> channelWrapString = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap"); |
| channel_wrap->SetClassName(channelWrapString); |
| target->Set(env->context(), channelWrapString, |
| channel_wrap->GetFunction(context).ToLocalChecked()).Check(); |
| } |
| |
| } // anonymous namespace |
| } // namespace cares_wrap |
| } // namespace node |
| |
| NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, node::cares_wrap::Initialize) |