// 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. #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 #include #include #include #include #ifndef T_CAA # define T_CAA 257 /* Certification Authority Authorization */ #endif // OpenBSD does not define these #ifndef AI_ALL # define AI_ALL 0 #endif #ifndef AI_V4MAPPED # 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::Just; using v8::Local; using v8::Maybe; using v8::Nothing; 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(p[0] << 8U) | (static_cast(p[1])); } void ares_poll_cb(uv_poll_t* watcher, int status, int events) { NodeAresTask* task = ContainerOf(&NodeAresTask::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) { 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) { ChannelWrap* channel = static_cast(data); NodeAresTask* task; NodeAresTask 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 = 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 */ /* 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 HostentToNames(Environment* env, struct hostent* host) { EscapableHandleScope scope(env->isolate()); std::vector> names; for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) names.emplace_back(OneByteString(env->isolate(), host->h_aliases[i])); Local ret = Array::New(env->isolate(), names.data(), names.size()); return scope.Escape(ret); } Local HostentToNames(Environment* env, struct hostent* host, Local names) { size_t offset = names->Length(); 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(); } return names; } template 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(env->isolate(), addrttls[i].ttl); 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) { HandleScope handle_scope(env->isolate()); 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(addrttls), naddrttls); break; case ns_t_aaaa: status = ares_parse_aaaa_reply(buf, len, &host, static_cast(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; 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 && 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(env->context(), ret->Length(), OneByteString(env->isolate(), ptr->h_name)).Check(); return ARES_SUCCESS; } if (*type == ns_t_cname_or_a) *type = ns_t_a; if (*type == ns_t_ns) { HostentToNames(env, ptr.get(), ret); } else if (*type == ns_t_ptr) { uint32_t offset = ret->Length(); 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; 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(env->context(), i + offset, address).Check(); } } return ARES_SUCCESS; } int ParseMxReply( Environment* env, const unsigned char* buf, int len, Local ret, bool need_type = false) { HandleScope handle_scope(env->isolate()); 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 mx_record = Object::New(env->isolate()); mx_record->Set(env->context(), env->exchange_string(), OneByteString(env->isolate(), current->host)).Check(); mx_record->Set(env->context(), env->priority_string(), Integer::New(env->isolate(), current->priority)).Check(); if (need_type) mx_record->Set(env->context(), env->type_string(), env->dns_mx_string()).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) { HandleScope handle_scope(env->isolate()); struct ares_caa_reply* caa_start; int status = ares_parse_caa_reply(buf, len, &caa_start); 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(env->context(), env->dns_critical_string(), Integer::New(env->isolate(), current->critical)).Check(); caa_record->Set(env->context(), OneByteString(env->isolate(), current->property), OneByteString(env->isolate(), current->value)).Check(); if (need_type) caa_record->Set(env->context(), env->type_string(), env->dns_caa_string()).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) { HandleScope handle_scope(env->isolate()); struct ares_txt_ext* txt_out; int status = ares_parse_txt_reply_ext(buf, len, &txt_out); if (status != ARES_SUCCESS) return status; Local 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 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 elem = Object::New(env->isolate()); elem->Set(env->context(), env->entries_string(), txt_chunk).Check(); elem->Set(env->context(), env->type_string(), env->dns_txt_string()).Check(); ret->Set(env->context(), offset + i++, elem).Check(); } else { ret->Set(env->context(), offset + i++, txt_chunk).Check(); } } txt_chunk = Array::New(env->isolate()); j = 0; } 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(env->context(), env->entries_string(), txt_chunk).Check(); elem->Set(env->context(), env->type_string(), env->dns_txt_string()).Check(); ret->Set(env->context(), offset + i, elem).Check(); } else { ret->Set(env->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 ret, bool need_type = false) { HandleScope handle_scope(env->isolate()); 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 srv_record = Object::New(env->isolate()); srv_record->Set(env->context(), env->name_string(), OneByteString(env->isolate(), current->host)).Check(); srv_record->Set(env->context(), env->port_string(), Integer::New(env->isolate(), current->port)).Check(); srv_record->Set(env->context(), env->priority_string(), Integer::New(env->isolate(), current->priority)).Check(); srv_record->Set(env->context(), env->weight_string(), Integer::New(env->isolate(), current->weight)).Check(); if (need_type) srv_record->Set(env->context(), env->type_string(), env->dns_srv_string()).Check(); ret->Set(env->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 ret, bool need_type = false) { HandleScope handle_scope(env->isolate()); 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 naptr_record = Object::New(env->isolate()); naptr_record->Set(env->context(), env->flags_string(), OneByteString(env->isolate(), current->flags)).Check(); naptr_record->Set(env->context(), env->service_string(), OneByteString(env->isolate(), current->service)).Check(); naptr_record->Set(env->context(), env->regexp_string(), OneByteString(env->isolate(), current->regexp)).Check(); naptr_record->Set(env->context(), env->replacement_string(), OneByteString(env->isolate(), current->replacement)).Check(); naptr_record->Set(env->context(), env->order_string(), Integer::New(env->isolate(), current->order)).Check(); naptr_record->Set(env->context(), env->preference_string(), Integer::New(env->isolate(), current->preference)).Check(); if (need_type) naptr_record->Set(env->context(), env->type_string(), env->dns_naptr_string()).Check(); ret->Set(env->context(), i + offset, naptr_record).Check(); } ares_free_data(naptr_start); return ARES_SUCCESS; } int ParseSoaReply( Environment* env, unsigned char* buf, int len, Local* ret) { EscapableHandleScope handle_scope(env->isolate()); // 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; // 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 = nullptr; long temp_len; // NOLINT(runtime/int) int status = ares_expand_name(ptr, buf, len, &name_temp, &temp_len); if (status != ARES_SUCCESS) { // returns EBADRESP in case of invalid input return status == ARES_EBADNAME ? ARES_EBADRESP : status; } const ares_unique_ptr name(name_temp); 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 = nullptr; long rr_temp_len; // NOLINT(runtime/int) int status2 = ares_expand_name(ptr, buf, len, &rr_name_temp, &rr_temp_len); if (status2 != ARES_SUCCESS) return status2 == ARES_EBADNAME ? ARES_EBADRESP : status2; const ares_unique_ptr rr_name(rr_name_temp); 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 = nullptr; long nsname_temp_len; // NOLINT(runtime/int) int status3 = ares_expand_name(ptr, buf, len, &nsname_temp, &nsname_temp_len); if (status3 != ARES_SUCCESS) { return status3 == ARES_EBADNAME ? ARES_EBADRESP : status3; } const ares_unique_ptr nsname(nsname_temp); ptr += nsname_temp_len; char* hostmaster_temp = nullptr; long hostmaster_temp_len; // NOLINT(runtime/int) int status4 = ares_expand_name(ptr, buf, len, &hostmaster_temp, &hostmaster_temp_len); if (status4 != ARES_SUCCESS) { return status4 == ARES_EBADNAME ? ARES_EBADRESP : status4; } const ares_unique_ptr hostmaster(hostmaster_temp); ptr += hostmaster_temp_len; if (ptr + 5 * 4 > buf + len) { return ARES_EBADRESP; } const unsigned int serial = ReadUint32BE(ptr + 0 * 4); const unsigned int refresh = ReadUint32BE(ptr + 1 * 4); const unsigned int retry = ReadUint32BE(ptr + 2 * 4); const unsigned int expire = ReadUint32BE(ptr + 3 * 4); const unsigned int minttl = ReadUint32BE(ptr + 4 * 4); Local soa_record = Object::New(env->isolate()); soa_record->Set(env->context(), env->nsname_string(), OneByteString(env->isolate(), nsname.get())).Check(); soa_record->Set(env->context(), env->hostmaster_string(), OneByteString(env->isolate(), hostmaster.get())).Check(); soa_record->Set(env->context(), env->serial_string(), Integer::NewFromUnsigned(env->isolate(), serial)).Check(); soa_record->Set(env->context(), env->refresh_string(), Integer::New(env->isolate(), refresh)).Check(); soa_record->Set(env->context(), env->retry_string(), Integer::New(env->isolate(), retry)).Check(); soa_record->Set(env->context(), env->expire_string(), Integer::New(env->isolate(), expire)).Check(); soa_record->Set(env->context(), env->minttl_string(), Integer::NewFromUnsigned(env->isolate(), minttl)).Check(); soa_record->Set(env->context(), env->type_string(), env->dns_soa_string()).Check(); *ret = handle_scope.Escape(soa_record); break; } ptr += rr_len; } return ARES_SUCCESS; } } // anonymous namespace ChannelWrap::ChannelWrap( Environment* env, Local object, int timeout, int tries) : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), timeout_(timeout), tries_(tries) { 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(), 2); CHECK(args[0]->IsInt32()); CHECK(args[1]->IsInt32()); const int timeout = args[0].As()->Value(); const int tries = args[1].As()->Value(); Environment* env = Environment::GetCurrent(args); new ChannelWrap(env, args.This(), timeout, tries); } 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_; options.tries = tries_; 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 | ARES_OPT_TRIES; 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; } int ATraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_a); return 0; } 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; } int CnameTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_cname); return 0; } int MxTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_mx); return 0; } int NsTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_ns); return 0; } int TxtTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_txt); return 0; } int SrvTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_srv); return 0; } int PtrTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_ptr); return 0; } int NaptrTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_naptr); return 0; } int SoaTraits::Send(QueryWrap* wrap, const char* name) { wrap->AresQuery(name, ns_c_in, ns_t_soa); return 0; } int AnyTraits::Parse( QueryAnyWrap* 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; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->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) return status; 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(); } } 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(); } } /* 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; 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; /* 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; 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(); } /* Parse TXT records */ status = ParseTxtReply(env, buf, len, ret, true); if (status != ARES_SUCCESS && status != ARES_ENODATA) return status; /* Parse SRV records */ status = ParseSrvReply(env, buf, len, ret, true); if (status != ARES_SUCCESS && status != ARES_ENODATA) return status; /* 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; /* Parse SOA records */ Local soa_record = Local(); status = ParseSoaReply(env, buf, len, &soa_record); if (status != ARES_SUCCESS && status != ARES_ENODATA) return status; if (!soa_record.IsEmpty()) ret->Set(env->context(), ret->Length(), soa_record).Check(); /* Parse CAA records */ status = ParseCaaReply(env, buf, len, ret, true); if (status != ARES_SUCCESS && status != ARES_ENODATA) return status; wrap->CallOnComplete(ret); return 0; } int ATraits::Parse( QueryAWrap* 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; Environment* env = wrap->env(); 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) return status; Local ttls = AddrTTLToArray(env, addrttls, naddrttls); wrap->CallOnComplete(ret, ttls); return 0; } int AaaaTraits::Parse( QueryAaaaWrap* 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; Environment* env = wrap->env(); 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) return status; Local ttls = AddrTTLToArray(env, addrttls, naddrttls); wrap->CallOnComplete(ret, ttls); return 0; } int CaaTraits::Parse( QueryCaaWrap* 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; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local ret = Array::New(env->isolate()); int status = ParseCaaReply(env, buf, len, ret); if (status != ARES_SUCCESS) return status; wrap->CallOnComplete(ret); return 0; } 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; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); 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; wrap->CallOnComplete(ret); return 0; } int MxTraits::Parse( QueryMxWrap* 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; 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); if (status != ARES_SUCCESS) return status; wrap->CallOnComplete(mx_records); return 0; } int NsTraits::Parse( QueryNsWrap* 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; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); 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; } int TxtTraits::Parse( QueryTxtWrap* 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; Environment* env = wrap->env(); 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; wrap->CallOnComplete(txt_records); return 0; } int SrvTraits::Parse( QuerySrvWrap* 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; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local srv_records = Array::New(env->isolate()); int status = ParseSrvReply(env, buf, len, srv_records); if (status != ARES_SUCCESS) return status; wrap->CallOnComplete(srv_records); return 0; } int PtrTraits::Parse( QueryPtrWrap* wrap, const std::unique_ptr& response) { if (UNLIKELY(response->is_host)) return ARES_EBADRESP; unsigned char* buf = response->buf.data; int len = response->buf.size; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); int type = ns_t_ptr; Local aliases = Array::New(env->isolate()); int status = ParseGeneralReply(env, buf, len, &type, aliases); if (status != ARES_SUCCESS) return status; wrap->CallOnComplete(aliases); return 0; } int NaptrTraits::Parse( QueryNaptrWrap* wrap, const std::unique_ptr& response) { if (UNLIKELY(response->is_host)) return ARES_EBADRESP; unsigned char* buf = response->buf.data; int len = response->buf.size; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local naptr_records = Array::New(env->isolate()); int status = ParseNaptrReply(env, buf, len, naptr_records); if (status != ARES_SUCCESS) return status; wrap->CallOnComplete(naptr_records); return 0; } int SoaTraits::Parse( QuerySoaWrap* wrap, const std::unique_ptr& response) { if (UNLIKELY(response->is_host)) return ARES_EBADRESP; unsigned char* buf = response->buf.data; int len = response->buf.size; Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); ares_soa_reply* soa_out; int status = ares_parse_soa_reply(buf, len, &soa_out); if (status != ARES_SUCCESS) return status; Local soa_record = Object::New(env->isolate()); soa_record->Set(env->context(), env->nsname_string(), OneByteString(env->isolate(), soa_out->nsname)).Check(); soa_record->Set(env->context(), env->hostmaster_string(), OneByteString(env->isolate(), soa_out->hostmaster)).Check(); soa_record->Set(env->context(), env->serial_string(), Integer::NewFromUnsigned( env->isolate(), soa_out->serial)).Check(); soa_record->Set(env->context(), env->refresh_string(), Integer::New(env->isolate(), soa_out->refresh)).Check(); soa_record->Set(env->context(), env->retry_string(), Integer::New(env->isolate(), soa_out->retry)).Check(); soa_record->Set(env->context(), env->expire_string(), Integer::New(env->isolate(), soa_out->expire)).Check(); soa_record->Set(env->context(), env->minttl_string(), Integer::NewFromUnsigned( env->isolate(), soa_out->minttl)).Check(); ares_free_data(soa_out); wrap->CallOnComplete(soa_record); return 0; } int ReverseTraits::Send(GetHostByAddrWrap* wrap, const char* name) { int length, family; char address_buffer[sizeof(struct in6_addr)]; if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { length = sizeof(struct in_addr); family = AF_INET; } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { length = sizeof(struct in6_addr); family = AF_INET6; } else { return UV_EINVAL; // So errnoException() reports a proper error. } TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( TRACING_CATEGORY_NODE2(dns, native), "reverse", wrap, "name", TRACE_STR_COPY(name), "family", family == AF_INET ? "ipv4" : "ipv6"); ares_gethostbyaddr( wrap->channel()->cares_channel(), address_buffer, length, family, GetHostByAddrWrap::Callback, wrap->MakeCallbackPointer()); return 0; } int ReverseTraits::Parse( GetHostByAddrWrap* wrap, const std::unique_ptr& response) { if (UNLIKELY(!response->is_host)) return ARES_EBADRESP; struct hostent* host = response->host.get(); Environment* env = wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); wrap->CallOnComplete(HostentToNames(env, host)); return 0; } namespace { template static void Query(const FunctionCallbackInfo& 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 req_wrap_obj = args[0].As(); Local string = args[1].As(); auto wrap = std::make_unique(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 req_wrap { static_cast(req->data)}; Environment* env = req_wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local argv[] = { Integer::New(env->isolate(), status), Null(env->isolate()) }; uint32_t n = 0; const bool verbatim = req_wrap->verbatim(); if (status == 0) { Local results = Array::New(env->isolate()); auto add = [&] (bool want_ipv4, bool want_ipv6) -> Maybe { 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( &(reinterpret_cast(p->ai_addr)->sin_addr)); } else if (want_ipv6 && p->ai_family == AF_INET6) { addr = reinterpret_cast( &(reinterpret_cast(p->ai_addr)->sin6_addr)); } else { continue; } char ip[INET6_ADDRSTRLEN]; if (uv_inet_ntop(p->ai_family, addr, ip, sizeof(ip))) continue; Local s = OneByteString(env->isolate(), ip); if (results->Set(env->context(), n, s).IsNothing()) return Nothing(); n++; } return Just(true); }; if (add(true, verbatim).IsNothing()) return; if (verbatim == false) { if (add(false, true).IsNothing()) return; } // 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 req_wrap { static_cast(req->data)}; Environment* env = req_wrap->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local argv[] = { Integer::New(env->isolate(), status), Null(env->isolate()), Null(env->isolate()) }; if (status == 0) { // Success Local js_hostname = OneByteString(env->isolate(), hostname); Local 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(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& 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 val = String::NewFromUtf8(isolate, canonical_ip) .ToLocalChecked(); args.GetReturnValue().Set(val); } void GetAddrInfo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); CHECK(args[1]->IsString()); CHECK(args[2]->IsInt32()); CHECK(args[4]->IsBoolean()); Local req_wrap_obj = args[0].As(); node::Utf8Value hostname(env->isolate(), args[1]); int32_t flags = 0; if (args[3]->IsInt32()) { flags = args[3].As()->Value(); } int family; switch (args[2].As()->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(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& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); CHECK(args[1]->IsString()); CHECK(args[2]->IsUint32()); Local req_wrap_obj = args[0].As(); 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(&addr)) == 0 || uv_ip6_addr(*ip, port, reinterpret_cast(&addr)) == 0); auto req_wrap = std::make_unique(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(&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& args) { Environment* env = Environment::GetCurrent(args); ChannelWrap* channel; ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); Local 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(&cur->addr); int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); CHECK_EQ(err, 0); Local 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& 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 arr = args[0].As(); uint32_t len = arr->Length(); if (len == 0) { int rv = ares_set_servers(channel->cares_channel(), nullptr); return args.GetReturnValue().Set(rv); } std::vector 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 elm = arr->Get(env->context(), i).ToLocalChecked().As(); 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 SetLocalAddress(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); ChannelWrap* channel; ASSIGN_OR_RETURN_UNWRAP(&channel, args.Holder()); CHECK_EQ(args.Length(), 2); CHECK(args[0]->IsString()); Isolate* isolate = args.GetIsolate(); node::Utf8Value ip0(isolate, args[0]); unsigned char addr0[sizeof(struct in6_addr)]; unsigned char addr1[sizeof(struct in6_addr)]; int type0 = 0; // This function accepts 2 arguments. The first may be either an IPv4 // address or an IPv6 address. If present, the second argument must be the // other type of address. Otherwise, the unspecified type of IP is set // to 0 (any). if (uv_inet_pton(AF_INET, *ip0, &addr0) == 0) { ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr0)); type0 = 4; } else if (uv_inet_pton(AF_INET6, *ip0, &addr0) == 0) { ares_set_local_ip6(channel->cares_channel(), addr0); type0 = 6; } else { THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); return; } if (!args[1]->IsUndefined()) { CHECK(args[1]->IsString()); node::Utf8Value ip1(isolate, args[1]); if (uv_inet_pton(AF_INET, *ip1, &addr1) == 0) { if (type0 == 4) { THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv4 addresses."); return; } else { ares_set_local_ip4(channel->cares_channel(), ReadUint32BE(addr1)); } } else if (uv_inet_pton(AF_INET6, *ip1, &addr1) == 0) { if (type0 == 6) { THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv6 addresses."); return; } else { ares_set_local_ip6(channel->cares_channel(), addr1); } } else { THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); return; } } else { // No second arg specified if (type0 == 4) { memset(&addr1, 0, sizeof(addr1)); ares_set_local_ip6(channel->cares_channel(), addr1); } else { ares_set_local_ip4(channel->cares_channel(), 0); } } } void Cancel(const FunctionCallbackInfo& 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& 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)); } } // 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, Local 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 aiw = BaseObject::MakeLazilyInitializedJSTemplate(env); aiw->Inherit(AsyncWrap::GetConstructorTemplate(env)); env->SetConstructorFunction(target, "GetAddrInfoReqWrap", aiw); Local niw = BaseObject::MakeLazilyInitializedJSTemplate(env); niw->Inherit(AsyncWrap::GetConstructorTemplate(env)); env->SetConstructorFunction(target, "GetNameInfoReqWrap", niw); Local qrw = BaseObject::MakeLazilyInitializedJSTemplate(env); qrw->Inherit(AsyncWrap::GetConstructorTemplate(env)); env->SetConstructorFunction(target, "QueryReqWrap", qrw); Local channel_wrap = env->NewFunctionTemplate(ChannelWrap::New); channel_wrap->InstanceTemplate()->SetInternalFieldCount( ChannelWrap::kInternalFieldCount); channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env)); env->SetProtoMethod(channel_wrap, "queryAny", Query); env->SetProtoMethod(channel_wrap, "queryA", Query); env->SetProtoMethod(channel_wrap, "queryAaaa", Query); env->SetProtoMethod(channel_wrap, "queryCaa", Query); env->SetProtoMethod(channel_wrap, "queryCname", Query); env->SetProtoMethod(channel_wrap, "queryMx", Query); env->SetProtoMethod(channel_wrap, "queryNs", Query); env->SetProtoMethod(channel_wrap, "queryTxt", Query); env->SetProtoMethod(channel_wrap, "querySrv", Query); env->SetProtoMethod(channel_wrap, "queryPtr", Query); env->SetProtoMethod(channel_wrap, "queryNaptr", Query); env->SetProtoMethod(channel_wrap, "querySoa", Query); env->SetProtoMethod(channel_wrap, "getHostByAddr", Query); env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); env->SetProtoMethod(channel_wrap, "setServers", SetServers); env->SetProtoMethod(channel_wrap, "setLocalAddress", SetLocalAddress); env->SetProtoMethod(channel_wrap, "cancel", Cancel); env->SetConstructorFunction(target, "ChannelWrap", channel_wrap); } } // namespace cares_wrap } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, node::cares_wrap::Initialize)