mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-07 20:28:10 +00:00
524 lines
14 KiB
C++
524 lines
14 KiB
C++
#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 <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;
|
|
|
|
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);
|
|
~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 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,
|
|
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<uv_getnameinfo_t> {
|
|
public:
|
|
GetNameInfoReqWrap(Environment* env, v8::Local<v8::Object> 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<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, 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<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);
|
|
|
|
status = Traits::Parse(this, response_data_);
|
|
|
|
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,
|
|
int status,
|
|
int timeouts,
|
|
unsigned char* answer_buf,
|
|
int answer_len) {
|
|
QueryWrap<Traits>* 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<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 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;
|
|
};
|
|
|
|
struct AnyTraits final {
|
|
static constexpr const char* name = "resolveAny";
|
|
static int Send(QueryWrap<AnyTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<AnyTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct ATraits final {
|
|
static constexpr const char* name = "resolve4";
|
|
static int Send(QueryWrap<ATraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<ATraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct AaaaTraits final {
|
|
static constexpr const char* name = "resolve6";
|
|
static int Send(QueryWrap<AaaaTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<AaaaTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct CaaTraits final {
|
|
static constexpr const char* name = "resolveCaa";
|
|
static int Send(QueryWrap<CaaTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<CaaTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct CnameTraits final {
|
|
static constexpr const char* name = "resolveCname";
|
|
static int Send(QueryWrap<CnameTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<CnameTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct MxTraits final {
|
|
static constexpr const char* name = "resolveMx";
|
|
static int Send(QueryWrap<MxTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<MxTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct NsTraits final {
|
|
static constexpr const char* name = "resolveNs";
|
|
static int Send(QueryWrap<NsTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<NsTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct TxtTraits final {
|
|
static constexpr const char* name = "resolveTxt";
|
|
static int Send(QueryWrap<TxtTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<TxtTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct SrvTraits final {
|
|
static constexpr const char* name = "resolveSrv";
|
|
static int Send(QueryWrap<SrvTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<SrvTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct PtrTraits final {
|
|
static constexpr const char* name = "resolvePtr";
|
|
static int Send(QueryWrap<PtrTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<PtrTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct NaptrTraits final {
|
|
static constexpr const char* name = "resolveNaptr";
|
|
static int Send(QueryWrap<NaptrTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<NaptrTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct SoaTraits final {
|
|
static constexpr const char* name = "resolveSoa";
|
|
static int Send(QueryWrap<SoaTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<SoaTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
struct ReverseTraits final {
|
|
static constexpr const char* name = "reverse";
|
|
static int Send(QueryWrap<ReverseTraits>* wrap, const char* name);
|
|
static int Parse(
|
|
QueryWrap<ReverseTraits>* wrap,
|
|
const std::unique_ptr<ResponseData>& response);
|
|
};
|
|
|
|
using QueryAnyWrap = QueryWrap<AnyTraits>;
|
|
using QueryAWrap = QueryWrap<ATraits>;
|
|
using QueryAaaaWrap = QueryWrap<AaaaTraits>;
|
|
using QueryCaaWrap = QueryWrap<CaaTraits>;
|
|
using QueryCnameWrap = QueryWrap<CnameTraits>;
|
|
using QueryMxWrap = QueryWrap<MxTraits>;
|
|
using QueryNsWrap = QueryWrap<NsTraits>;
|
|
using QueryTxtWrap = QueryWrap<TxtTraits>;
|
|
using QuerySrvWrap = QueryWrap<SrvTraits>;
|
|
using QueryPtrWrap = QueryWrap<PtrTraits>;
|
|
using QueryNaptrWrap = QueryWrap<NaptrTraits>;
|
|
using QuerySoaWrap = QueryWrap<SoaTraits>;
|
|
using GetHostByAddrWrap = QueryWrap<ReverseTraits>;
|
|
|
|
} // namespace cares_wrap
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_CARES_WRAP_H_
|