#ifndef SRC_CARES_WRAP_H_ #define SRC_CARES_WRAP_H_ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #define CARES_STATICLIB #include "async_wrap.h" #include "base_object.h" #include "env.h" #include "memory_tracker.h" #include "util.h" #include "node.h" #include "ares.h" #include "v8.h" #include "uv.h" #include #ifdef __POSIX__ # include #endif // __POSIX__ # include namespace node { namespace cares_wrap { constexpr int ns_t_cname_or_a = -1; constexpr int DNS_ESETSRVPENDING = -1000; class ChannelWrap; inline void safe_free_hostent(struct hostent* host); using HostEntPointer = DeleteFnPtr; using SafeHostEntPointer = DeleteFnPtr; inline const char* ToErrorCodeString(int status) { switch (status) { #define V(code) case ARES_##code: return #code; V(EADDRGETNETWORKPARAMS) V(EBADFAMILY) V(EBADFLAGS) V(EBADHINTS) V(EBADNAME) V(EBADQUERY) V(EBADRESP) V(EBADSTR) V(ECANCELLED) V(ECONNREFUSED) V(EDESTRUCTION) V(EFILE) V(EFORMERR) V(ELOADIPHLPAPI) V(ENODATA) V(ENOMEM) V(ENONAME) V(ENOTFOUND) V(ENOTIMP) V(ENOTINITIALIZED) V(EOF) V(EREFUSED) V(ESERVFAIL) V(ETIMEOUT) #undef V } return "UNKNOWN_ARES_ERROR"; } inline void cares_wrap_hostent_cpy( struct hostent* dest, const struct hostent* src) { dest->h_addr_list = nullptr; dest->h_addrtype = 0; dest->h_aliases = nullptr; dest->h_length = 0; dest->h_name = nullptr; /* copy `h_name` */ size_t name_size = strlen(src->h_name) + 1; dest->h_name = node::Malloc(name_size); memcpy(dest->h_name, src->h_name, name_size); /* copy `h_aliases` */ size_t alias_count; for (alias_count = 0; src->h_aliases[alias_count] != nullptr; alias_count++) { } dest->h_aliases = node::Malloc(alias_count + 1); for (size_t i = 0; i < alias_count; i++) { const size_t cur_alias_size = strlen(src->h_aliases[i]) + 1; dest->h_aliases[i] = node::Malloc(cur_alias_size); memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_size); } dest->h_aliases[alias_count] = nullptr; /* copy `h_addr_list` */ size_t list_count; for (list_count = 0; src->h_addr_list[list_count] != nullptr; list_count++) { } dest->h_addr_list = node::Malloc(list_count + 1); for (size_t i = 0; i < list_count; i++) { dest->h_addr_list[i] = node::Malloc(src->h_length); memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length); } dest->h_addr_list[list_count] = nullptr; /* work after work */ dest->h_length = src->h_length; dest->h_addrtype = src->h_addrtype; } struct NodeAresTask final : public MemoryRetainer { ChannelWrap* channel; ares_socket_t sock; uv_poll_t poll_watcher; inline void MemoryInfo(MemoryTracker* trakcer) const override; SET_MEMORY_INFO_NAME(NodeAresTask) SET_SELF_SIZE(NodeAresTask) struct Hash { inline size_t operator()(NodeAresTask* a) const { return std::hash()(a->sock); } }; struct Equal { inline bool operator()(NodeAresTask* a, NodeAresTask* b) const { return a->sock == b->sock; } }; static NodeAresTask* Create(ChannelWrap* channel, ares_socket_t sock); using List = std::unordered_set; }; class ChannelWrap final : public AsyncWrap { public: ChannelWrap( Environment* env, v8::Local object, int timeout, int tries); ~ChannelWrap() override; static void New(const v8::FunctionCallbackInfo& args); void Setup(); void EnsureServers(); void StartTimer(); void CloseTimer(); void ModifyActivityQueryCount(int count); inline uv_timer_t* timer_handle() { return timer_handle_; } inline ares_channel cares_channel() { return channel_; } inline void set_query_last_ok(bool ok) { query_last_ok_ = ok; } inline void set_is_servers_default(bool is_default) { is_servers_default_ = is_default; } inline int active_query_count() { return active_query_count_; } inline NodeAresTask::List* task_list() { return &task_list_; } void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(ChannelWrap) SET_SELF_SIZE(ChannelWrap) static void AresTimeout(uv_timer_t* handle); private: uv_timer_t* timer_handle_ = nullptr; ares_channel channel_ = nullptr; bool query_last_ok_ = true; bool is_servers_default_ = true; bool library_inited_ = false; int timeout_; int tries_; int active_query_count_ = 0; NodeAresTask::List task_list_; }; class GetAddrInfoReqWrap final : public ReqWrap { public: GetAddrInfoReqWrap(Environment* env, v8::Local req_wrap_obj, bool verbatim); SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) SET_SELF_SIZE(GetAddrInfoReqWrap) bool verbatim() const { return verbatim_; } private: const bool verbatim_; }; class GetNameInfoReqWrap final : public ReqWrap { public: GetNameInfoReqWrap(Environment* env, v8::Local req_wrap_obj); SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) SET_SELF_SIZE(GetNameInfoReqWrap) }; struct ResponseData final { int status; bool is_host; SafeHostEntPointer host; MallocedBuffer buf; }; template class QueryWrap final : public AsyncWrap { public: QueryWrap(ChannelWrap* channel, v8::Local req_wrap_obj) : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP), channel_(channel), trace_name_(Traits::name) {} ~QueryWrap() { CHECK_EQ(false, persistent().IsEmpty()); // Let Callback() know that this object no longer exists. if (callback_ptr_ != nullptr) *callback_ptr_ = nullptr; } int Send(const char* name) { return Traits::Send(this, name); } void AresQuery(const char* name, int dnsclass, int type) { channel_->EnsureServers(); TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, "name", TRACE_STR_COPY(name)); ares_query( channel_->cares_channel(), name, dnsclass, type, Callback, MakeCallbackPointer()); } void ParseError(int status) { CHECK_NE(status, ARES_SUCCESS); v8::HandleScope handle_scope(env()->isolate()); v8::Context::Scope context_scope(env()->context()); const char* code = ToErrorCodeString(status); v8::Local arg = OneByteString(env()->isolate(), code); TRACE_EVENT_NESTABLE_ASYNC_END1( TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, "error", status); MakeCallback(env()->oncomplete_string(), 1, &arg); } const BaseObjectPtr& channel() const { return channel_; } void AfterResponse() { CHECK(response_data_); int status = response_data_->status; if (status != ARES_SUCCESS) return ParseError(status); status = Traits::Parse(this, response_data_); if (status != ARES_SUCCESS) ParseError(status); } void* MakeCallbackPointer() { CHECK_NULL(callback_ptr_); callback_ptr_ = new QueryWrap*(this); return callback_ptr_; } static QueryWrap* FromCallbackPointer(void* arg) { std::unique_ptr*> wrap_ptr { static_cast**>(arg) }; QueryWrap* wrap = *wrap_ptr.get(); if (wrap == nullptr) return nullptr; wrap->callback_ptr_ = nullptr; return wrap; } static void Callback( void* arg, int status, int timeouts, unsigned char* answer_buf, int answer_len) { QueryWrap* wrap = FromCallbackPointer(arg); if (wrap == nullptr) return; unsigned char* buf_copy = nullptr; if (status == ARES_SUCCESS) { buf_copy = node::Malloc(answer_len); memcpy(buf_copy, answer_buf, answer_len); } wrap->response_data_ = std::make_unique(); ResponseData* data = wrap->response_data_.get(); data->status = status; data->is_host = false; data->buf = MallocedBuffer(buf_copy, answer_len); wrap->QueueResponseCallback(status); } static void Callback( void* arg, int status, int timeouts, struct hostent* host) { QueryWrap* wrap = FromCallbackPointer(arg); if (wrap == nullptr) return; struct hostent* host_copy = nullptr; if (status == ARES_SUCCESS) { host_copy = node::Malloc(1); cares_wrap_hostent_cpy(host_copy, host); } wrap->response_data_ = std::make_unique(); ResponseData* data = wrap->response_data_.get(); data->status = status; data->host.reset(host_copy); data->is_host = true; wrap->QueueResponseCallback(status); } void QueueResponseCallback(int status) { BaseObjectPtr> strong_ref{this}; env()->SetImmediate([this, strong_ref](Environment*) { AfterResponse(); // Delete once strong_ref goes out of scope. Detach(); }); channel_->set_query_last_ok(status != ARES_ECONNREFUSED); channel_->ModifyActivityQueryCount(-1); } void CallOnComplete( v8::Local answer, v8::Local extra = v8::Local()) { v8::HandleScope handle_scope(env()->isolate()); v8::Context::Scope context_scope(env()->context()); v8::Local argv[] = { v8::Integer::New(env()->isolate(), 0), answer, extra }; const int argc = arraysize(argv) - extra.IsEmpty(); TRACE_EVENT_NESTABLE_ASYNC_END0( TRACING_CATEGORY_NODE2(dns, native), trace_name_, this); MakeCallback(env()->oncomplete_string(), argc, argv); } void MemoryInfo(MemoryTracker* tracker) const override { tracker->TrackField("channel", channel_); if (response_data_) { tracker->TrackFieldWithSize("response", response_data_->buf.size); } } SET_MEMORY_INFO_NAME(QueryWrap) SET_SELF_SIZE(QueryWrap) private: BaseObjectPtr channel_; std::unique_ptr response_data_; const char* trace_name_; // Pointer to pointer to 'this' that can be reset from the destructor, // in order to let Callback() know that 'this' no longer exists. QueryWrap** callback_ptr_ = nullptr; }; struct AnyTraits final { static constexpr const char* name = "resolveAny"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct ATraits final { static constexpr const char* name = "resolve4"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct AaaaTraits final { static constexpr const char* name = "resolve6"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct CaaTraits final { static constexpr const char* name = "resolveCaa"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct CnameTraits final { static constexpr const char* name = "resolveCname"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct MxTraits final { static constexpr const char* name = "resolveMx"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct NsTraits final { static constexpr const char* name = "resolveNs"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct TxtTraits final { static constexpr const char* name = "resolveTxt"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct SrvTraits final { static constexpr const char* name = "resolveSrv"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct PtrTraits final { static constexpr const char* name = "resolvePtr"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct NaptrTraits final { static constexpr const char* name = "resolveNaptr"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct SoaTraits final { static constexpr const char* name = "resolveSoa"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; struct ReverseTraits final { static constexpr const char* name = "reverse"; static int Send(QueryWrap* wrap, const char* name); static int Parse( QueryWrap* wrap, const std::unique_ptr& response); }; using QueryAnyWrap = QueryWrap; using QueryAWrap = QueryWrap; using QueryAaaaWrap = QueryWrap; using QueryCaaWrap = QueryWrap; using QueryCnameWrap = QueryWrap; using QueryMxWrap = QueryWrap; using QueryNsWrap = QueryWrap; using QueryTxtWrap = QueryWrap; using QuerySrvWrap = QueryWrap; using QueryPtrWrap = QueryWrap; using QueryNaptrWrap = QueryWrap; using QuerySoaWrap = QueryWrap; using GetHostByAddrWrap = QueryWrap; } // namespace cares_wrap } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_CARES_WRAP_H_