/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v26.2.0
src/cares_wrap.h
475 строк
14 KB
theanarkh
dns: support max timeout
10 июл 2025, 18:50
Не верифицирован
10 июл 2025, 18:50
c44fa8d
Код
Авторство
О чём код?
#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 "node.h" #include "node_internals.h" #include "permission/permission.h" #include "util.h" #include "ares.h" #include "v8.h" #include "uv.h" #include <unordered_set> #ifdef __POSIX__ # include <netdb.h> #endif // __POSIX__ # include <ares_nameser.h> namespace node { namespace cares_wrap { constexpr int ns_t_cname_or_a = -1; constexpr int DNS_ESETSRVPENDING = -1000; constexpr uint8_t DNS_ORDER_VERBATIM = 0; constexpr uint8_t DNS_ORDER_IPV4_FIRST = 1; constexpr uint8_t DNS_ORDER_IPV6_FIRST = 2; class ChannelWrap; inline void safe_free_hostent(struct hostent* host); using HostEntPointer = DeleteFnPtr<hostent, ares_free_hostent>; using SafeHostEntPointer = DeleteFnPtr<hostent, safe_free_hostent>; 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<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; } 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<ares_socket_t>()(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<NodeAresTask*, Hash, Equal>; }; class ChannelWrap final : public AsyncWrap { public: ChannelWrap(Environment* env, v8::Local<v8::Object> object, int timeout, int tries, int max_timeout); ~ChannelWrap() override; static void New(const v8::FunctionCallbackInfo<v8::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 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 max_timeout_; int active_query_count_ = 0; NodeAresTask::List task_list_; }; class GetAddrInfoReqWrap final : public ReqWrap<uv_getaddrinfo_t> { public: GetAddrInfoReqWrap(Environment* env, v8::Local<v8::Object> req_wrap_obj, uint8_t order); SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) SET_SELF_SIZE(GetAddrInfoReqWrap) uint8_t order() const { return order_; } private: const uint8_t order_; }; class GetNameInfoReqWrap final : public ReqWrap<uv_getnameinfo_t> { public: GetNameInfoReqWrap(Environment* env, v8::Local<v8::Object> req_wrap_obj); SET_INSUFFICIENT_PERMISSION_ERROR_CALLBACK(permission::PermissionScope::kNet) SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) SET_SELF_SIZE(GetNameInfoReqWrap) }; struct ResponseData final { int status; bool is_host; SafeHostEntPointer host; MallocedBuffer<unsigned char> buf; }; template <typename Traits> class QueryWrap final : public AsyncWrap { public: QueryWrap(ChannelWrap* channel, v8::Local<v8::Object> 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, ares_dns_class_t dnsclass, ares_dns_rec_type_t type) { permission::PermissionScope scope = permission::PermissionScope::kNet; Environment* env_holder = env(); if (!env_holder->permission()->is_granted(env_holder, scope, name)) [[unlikely]] { QueuePermissionModelResponseCallback(name); return; } channel_->EnsureServers(); TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( TRACING_CATEGORY_NODE2(dns, native), trace_name_, this, "name", TRACE_STR_COPY(name)); ares_query_dnsrec(channel_->cares_channel(), name, dnsclass, type, Callback, MakeCallbackPointer(), nullptr); } SET_INSUFFICIENT_PERMISSION_ERROR_CALLBACK(permission::PermissionScope::kNet) 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<v8::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); } const BaseObjectPtr<ChannelWrap>& channel() const { return channel_; } void AfterResponse() { CHECK(response_data_); int status = response_data_->status; if (status != ARES_SUCCESS) return ParseError(status); if (!Traits::Parse(this, response_data_).To(&status)) { return ParseError(ARES_ECANCELLED); } if (status != ARES_SUCCESS) ParseError(status); } void* MakeCallbackPointer() { CHECK_NULL(callback_ptr_); callback_ptr_ = new QueryWrap<Traits>*(this); return callback_ptr_; } static QueryWrap<Traits>* FromCallbackPointer(void* arg) { std::unique_ptr<QueryWrap<Traits>*> wrap_ptr { static_cast<QueryWrap<Traits>**>(arg) }; QueryWrap<Traits>* wrap = *wrap_ptr.get(); if (wrap == nullptr) return nullptr; wrap->callback_ptr_ = nullptr; return wrap; } static void Callback(void* arg, ares_status_t status, size_t timeouts, const ares_dns_record_t* dnsrec) { QueryWrap<Traits>* wrap = FromCallbackPointer(arg); if (wrap == nullptr) return; unsigned char* buf_copy = nullptr; size_t answer_len = 0; if (status == ARES_SUCCESS) { // No need to explicitly call ares_free_string here, // as it is a wrapper around free, which is already // invoked when MallocedBuffer is destructed. ares_dns_write(dnsrec, &buf_copy, &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<Traits>* 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 QueuePermissionModelResponseCallback(const char* resource) { BaseObjectPtr<QueryWrap<Traits>> strong_ref{this}; const std::string res{resource}; env()->SetImmediate([this, strong_ref, res](Environment*) { InsufficientPermissionError(res); // Delete once strong_ref goes out of scope. Detach(); }); channel_->set_query_last_ok(true); channel_->ModifyActivityQueryCount(-1); } void QueueResponseCallback(int status) { BaseObjectPtr<QueryWrap<Traits>> 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<v8::Value> answer, v8::Local<v8::Value> extra = v8::Local<v8::Value>()) { v8::HandleScope handle_scope(env()->isolate()); v8::Context::Scope context_scope(env()->context()); v8::Local<v8::Value> 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<Traits>) private: BaseObjectPtr<ChannelWrap> channel_; 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<Traits>** callback_ptr_ = nullptr; }; #define QUERY_TYPES(V) \ V(Reverse, reverse, getHostByAddr) \ V(A, resolve4, queryA) \ V(Any, resolveAny, queryAny) \ V(Aaaa, resolve6, queryAaaa) \ V(Caa, resolveCaa, queryCaa) \ V(Cname, resolveCname, queryCname) \ V(Mx, resolveMx, queryMx) \ V(Naptr, resolveNaptr, queryNaptr) \ V(Ns, resolveNs, queryNs) \ V(Ptr, resolvePtr, queryPtr) \ V(Srv, resolveSrv, querySrv) \ V(Soa, resolveSoa, querySoa) \ V(Tlsa, resolveTlsa, queryTlsa) \ V(Txt, resolveTxt, queryTxt) // All query type handlers share the same basic structure, so we can simplify // the code a bit by using a macro to define that structure. #define TYPE_TRAITS(Name, label) \ struct Name##Traits final { \ static constexpr const char* name = #label; \ static int Send(QueryWrap<Name##Traits>* wrap, const char* name); \ static v8::Maybe<int> Parse( \ QueryWrap<Name##Traits>* wrap, \ const std::unique_ptr<ResponseData>& response); \ }; \ using Query##Name##Wrap = QueryWrap<Name##Traits>; #define V(NAME, LABEL, _) TYPE_TRAITS(NAME, LABEL) QUERY_TYPES(V) #undef V #undef TYPE_TRAITS } // namespace cares_wrap } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_CARES_WRAP_H_