mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2225 lines
64 KiB
2225 lines
64 KiB
// 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.h"
|
|
#include "async-wrap-inl.h"
|
|
#include "env.h"
|
|
#include "env-inl.h"
|
|
#include "node.h"
|
|
#include "req-wrap.h"
|
|
#include "req-wrap-inl.h"
|
|
#include "util.h"
|
|
#include "util-inl.h"
|
|
#include "uv.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
|
|
#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::Integer;
|
|
using v8::Local;
|
|
using v8::Null;
|
|
using v8::Object;
|
|
using v8::String;
|
|
using v8::Value;
|
|
|
|
namespace {
|
|
|
|
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;
|
|
|
|
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 {
|
|
ChannelWrap* channel;
|
|
ares_socket_t sock;
|
|
uv_poll_t poll_watcher;
|
|
};
|
|
|
|
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);
|
|
~ChannelWrap();
|
|
|
|
static void New(const FunctionCallbackInfo<Value>& args);
|
|
|
|
void Setup();
|
|
void EnsureServers();
|
|
void CleanupTimer();
|
|
|
|
inline uv_timer_t* timer_handle() { return timer_handle_; }
|
|
inline ares_channel cares_channel() { return channel_; }
|
|
inline bool query_last_ok() const { return query_last_ok_; }
|
|
inline void set_query_last_ok(bool ok) { query_last_ok_ = ok; }
|
|
inline bool is_servers_default() const { return is_servers_default_; }
|
|
inline void set_is_servers_default(bool is_default) {
|
|
is_servers_default_ = is_default;
|
|
}
|
|
inline node_ares_task_list* task_list() { return &task_list_; }
|
|
|
|
size_t self_size() const override { return sizeof(this); }
|
|
|
|
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_;
|
|
node_ares_task_list task_list_;
|
|
};
|
|
|
|
ChannelWrap::ChannelWrap(Environment* env,
|
|
Local<Object> object)
|
|
: AsyncWrap(env, object, PROVIDER_DNSCHANNEL),
|
|
timer_handle_(nullptr),
|
|
channel_(nullptr),
|
|
query_last_ok_(true),
|
|
is_servers_default_(true),
|
|
library_inited_(false) {
|
|
MakeWeak<ChannelWrap>(this);
|
|
|
|
Setup();
|
|
}
|
|
|
|
void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) {
|
|
CHECK(args.IsConstructCall());
|
|
CHECK_EQ(args.Length(), 0);
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
new ChannelWrap(env, args.This());
|
|
}
|
|
|
|
class GetAddrInfoReqWrap : public ReqWrap<uv_getaddrinfo_t> {
|
|
public:
|
|
GetAddrInfoReqWrap(Environment* env,
|
|
Local<Object> req_wrap_obj,
|
|
bool verbatim);
|
|
~GetAddrInfoReqWrap();
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
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) {
|
|
Wrap(req_wrap_obj, this);
|
|
}
|
|
|
|
GetAddrInfoReqWrap::~GetAddrInfoReqWrap() {
|
|
ClearWrap(object());
|
|
}
|
|
|
|
|
|
class GetNameInfoReqWrap : public ReqWrap<uv_getnameinfo_t> {
|
|
public:
|
|
GetNameInfoReqWrap(Environment* env, Local<Object> req_wrap_obj);
|
|
~GetNameInfoReqWrap();
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
};
|
|
|
|
GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env,
|
|
Local<Object> req_wrap_obj)
|
|
: ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) {
|
|
Wrap(req_wrap_obj, this);
|
|
}
|
|
|
|
GetNameInfoReqWrap::~GetNameInfoReqWrap() {
|
|
ClearWrap(object());
|
|
}
|
|
|
|
|
|
/* 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_handle_t* watcher) {
|
|
node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher,
|
|
reinterpret_cast<uv_poll_t*>(watcher));
|
|
free(task);
|
|
}
|
|
|
|
|
|
/* Allocates and returns a new node_ares_task */
|
|
node_ares_task* ares_task_create(ChannelWrap* channel, ares_socket_t sock) {
|
|
auto task = node::UncheckedMalloc<node_ares_task>(1);
|
|
|
|
if (task == nullptr) {
|
|
/* Out of memory. */
|
|
return nullptr;
|
|
}
|
|
|
|
task->channel = channel;
|
|
task->sock = sock;
|
|
|
|
if (uv_poll_init_socket(channel->env()->event_loop(),
|
|
&task->poll_watcher, sock) < 0) {
|
|
/* This should never happen. */
|
|
free(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 */
|
|
|
|
/* If this is the first socket then start the timer. */
|
|
uv_timer_t* timer_handle = channel->timer_handle();
|
|
if (!uv_is_active(reinterpret_cast<uv_handle_t*>(timer_handle))) {
|
|
CHECK(channel->task_list()->empty());
|
|
uv_timer_start(timer_handle, ChannelWrap::AresTimeout, 1000, 1000);
|
|
}
|
|
|
|
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);
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&task->poll_watcher),
|
|
ares_poll_close_cb);
|
|
|
|
if (channel->task_list()->empty()) {
|
|
uv_timer_stop(channel->timer_handle());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Local<Array> HostentToAddresses(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> addresses = append ? append_to : Array::New(env->isolate());
|
|
size_t offset = addresses->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));
|
|
Local<String> address = OneByteString(env->isolate(), ip);
|
|
addresses->Set(context, i + offset, address).FromJust();
|
|
}
|
|
|
|
return append ? addresses : scope.Escape(addresses);
|
|
}
|
|
|
|
|
|
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).FromJust();
|
|
}
|
|
|
|
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 = 0;
|
|
}
|
|
|
|
if (host->h_aliases != nullptr) {
|
|
idx = 0;
|
|
while (host->h_aliases[idx]) {
|
|
free(host->h_aliases[idx++]);
|
|
}
|
|
free(host->h_aliases);
|
|
host->h_aliases = 0;
|
|
}
|
|
|
|
if (host->h_name != nullptr) {
|
|
free(host->h_name);
|
|
}
|
|
|
|
host->h_addrtype = host->h_length = 0;
|
|
}
|
|
|
|
void cares_wrap_hostent_cpy(struct hostent* dest, 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;
|
|
size_t cur_alias_length;
|
|
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++) {
|
|
cur_alias_length = strlen(src->h_aliases[i]);
|
|
dest->h_aliases[i] = node::Malloc(cur_alias_length + 1);
|
|
memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_length + 1);
|
|
}
|
|
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;
|
|
struct CaresAsyncData {
|
|
QueryWrap* wrap;
|
|
int status;
|
|
bool is_host;
|
|
union {
|
|
hostent* host;
|
|
unsigned char* buf;
|
|
} data;
|
|
int len;
|
|
|
|
uv_async_t async_handle;
|
|
};
|
|
|
|
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;
|
|
|
|
int r;
|
|
if (!library_inited_) {
|
|
// 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. */
|
|
r = ares_init_options(&channel_,
|
|
&options,
|
|
ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB);
|
|
|
|
if (r != ARES_SUCCESS) {
|
|
ares_library_cleanup();
|
|
return env()->ThrowError(ToErrorCodeString(r));
|
|
}
|
|
|
|
library_inited_ = true;
|
|
|
|
/* Initialize the timeout timer. The timer won't be started until the */
|
|
/* first socket is opened. */
|
|
CleanupTimer();
|
|
timer_handle_ = new uv_timer_t();
|
|
timer_handle_->data = static_cast<void*>(this);
|
|
uv_timer_init(env()->event_loop(), timer_handle_);
|
|
}
|
|
|
|
|
|
ChannelWrap::~ChannelWrap() {
|
|
if (library_inited_) {
|
|
// This decreases the reference counter increased by ares_library_init().
|
|
ares_library_cleanup();
|
|
}
|
|
|
|
ares_destroy(channel_);
|
|
CleanupTimer();
|
|
}
|
|
|
|
|
|
void ChannelWrap::CleanupTimer() {
|
|
if (timer_handle_ == nullptr) return;
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(timer_handle_),
|
|
[](uv_handle_t* handle) {
|
|
delete reinterpret_cast<uv_timer_t*>(handle);
|
|
});
|
|
timer_handle_ = nullptr;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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_);
|
|
|
|
Setup();
|
|
}
|
|
|
|
|
|
class QueryWrap : public AsyncWrap {
|
|
public:
|
|
QueryWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
|
|
: AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP),
|
|
channel_(channel) {
|
|
if (env()->in_domain()) {
|
|
req_wrap_obj->Set(env()->domain_string(),
|
|
env()->domain_array()->Get(env()->context(), 0)
|
|
.ToLocalChecked());
|
|
}
|
|
|
|
Wrap(req_wrap_obj, this);
|
|
|
|
// Make sure the channel object stays alive during the query lifetime.
|
|
req_wrap_obj->Set(env()->context(),
|
|
env()->channel_string(),
|
|
channel->object()).FromJust();
|
|
}
|
|
|
|
~QueryWrap() override {
|
|
CHECK_EQ(false, persistent().IsEmpty());
|
|
ClearWrap(object());
|
|
persistent().Reset();
|
|
}
|
|
|
|
// 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();
|
|
ares_query(channel_->cares_channel(), name, dnsclass, type, Callback,
|
|
static_cast<void*>(this));
|
|
}
|
|
|
|
static void CaresAsyncClose(uv_handle_t* handle) {
|
|
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
|
|
auto data = static_cast<struct CaresAsyncData*>(async->data);
|
|
delete data->wrap;
|
|
delete data;
|
|
}
|
|
|
|
static void CaresAsyncCb(uv_async_t* handle) {
|
|
auto data = static_cast<struct CaresAsyncData*>(handle->data);
|
|
|
|
QueryWrap* wrap = data->wrap;
|
|
int status = data->status;
|
|
|
|
if (status != ARES_SUCCESS) {
|
|
wrap->ParseError(status);
|
|
} else if (!data->is_host) {
|
|
unsigned char* buf = data->data.buf;
|
|
wrap->Parse(buf, data->len);
|
|
free(buf);
|
|
} else {
|
|
hostent* host = data->data.host;
|
|
wrap->Parse(host);
|
|
safe_free_hostent(host);
|
|
free(host);
|
|
}
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(handle), CaresAsyncClose);
|
|
}
|
|
|
|
static void Callback(void *arg, int status, int timeouts,
|
|
unsigned char* answer_buf, int answer_len) {
|
|
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
|
|
|
|
unsigned char* buf_copy = nullptr;
|
|
if (status == ARES_SUCCESS) {
|
|
buf_copy = node::Malloc<unsigned char>(answer_len);
|
|
memcpy(buf_copy, answer_buf, answer_len);
|
|
}
|
|
|
|
CaresAsyncData* data = new CaresAsyncData();
|
|
data->status = status;
|
|
data->wrap = wrap;
|
|
data->is_host = false;
|
|
data->data.buf = buf_copy;
|
|
data->len = answer_len;
|
|
|
|
uv_async_t* async_handle = &data->async_handle;
|
|
CHECK_EQ(0, uv_async_init(wrap->env()->event_loop(),
|
|
async_handle,
|
|
CaresAsyncCb));
|
|
|
|
wrap->channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
|
|
async_handle->data = data;
|
|
uv_async_send(async_handle);
|
|
}
|
|
|
|
static void Callback(void *arg, int status, int timeouts,
|
|
struct hostent* host) {
|
|
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
|
|
|
|
struct hostent* host_copy = nullptr;
|
|
if (status == ARES_SUCCESS) {
|
|
host_copy = node::Malloc<hostent>(1);
|
|
cares_wrap_hostent_cpy(host_copy, host);
|
|
}
|
|
|
|
CaresAsyncData* data = new CaresAsyncData();
|
|
data->status = status;
|
|
data->data.host = host_copy;
|
|
data->wrap = wrap;
|
|
data->is_host = true;
|
|
|
|
uv_async_t* async_handle = &data->async_handle;
|
|
CHECK_EQ(0, uv_async_init(wrap->env()->event_loop(),
|
|
async_handle,
|
|
CaresAsyncCb));
|
|
|
|
wrap->channel_->set_query_last_ok(status != ARES_ECONNREFUSED);
|
|
async_handle->data = data;
|
|
uv_async_send(async_handle);
|
|
}
|
|
|
|
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();
|
|
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);
|
|
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();
|
|
}
|
|
|
|
ChannelWrap* channel_;
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
Local<Array> AddrTTLToArray(Environment* env,
|
|
const T* addrttls,
|
|
size_t naddrttls) {
|
|
auto isolate = env->isolate();
|
|
EscapableHandleScope escapable_handle_scope(isolate);
|
|
auto context = env->context();
|
|
|
|
Local<Array> ttls = Array::New(isolate, naddrttls);
|
|
for (size_t i = 0; i < naddrttls; i++) {
|
|
auto value = Integer::New(isolate, addrttls[i].ttl);
|
|
ttls->Set(context, i, value).FromJust();
|
|
}
|
|
|
|
return escapable_handle_scope.Escape(ttls);
|
|
}
|
|
|
|
|
|
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, NULL, 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)).FromJust();
|
|
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] != NULL; i++) {
|
|
ret->Set(context,
|
|
i + offset,
|
|
OneByteString(env->isolate(), host->h_aliases[i])).FromJust();
|
|
}
|
|
} else {
|
|
HostentToAddresses(env, host, ret);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Local<String> exchange_symbol = env->exchange_string();
|
|
Local<String> priority_symbol = env->priority_string();
|
|
Local<String> type_symbol = env->type_string();
|
|
Local<String> mx_symbol = env->dns_mx_string();
|
|
|
|
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,
|
|
exchange_symbol,
|
|
OneByteString(env->isolate(), current->host)).FromJust();
|
|
mx_record->Set(context,
|
|
priority_symbol,
|
|
Integer::New(env->isolate(), current->priority)).FromJust();
|
|
if (need_type)
|
|
mx_record->Set(context, type_symbol, mx_symbol).FromJust();
|
|
|
|
ret->Set(context, i + offset, mx_record).FromJust();
|
|
}
|
|
|
|
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);
|
|
|
|
// 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).FromJust();
|
|
elem->Set(context,
|
|
env->type_string(),
|
|
env->dns_txt_string()).FromJust();
|
|
ret->Set(context, offset + i++, elem).FromJust();
|
|
} else {
|
|
ret->Set(context, offset + i++, txt_chunk).FromJust();
|
|
}
|
|
}
|
|
|
|
txt_chunk = Array::New(env->isolate());
|
|
j = 0;
|
|
}
|
|
|
|
txt_chunk->Set(context, j++, txt).FromJust();
|
|
}
|
|
|
|
// 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).FromJust();
|
|
elem->Set(context,
|
|
env->type_string(),
|
|
env->dns_txt_string()).FromJust();
|
|
ret->Set(context, offset + i, elem).FromJust();
|
|
} else {
|
|
ret->Set(context, offset + i, txt_chunk).FromJust();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Local<String> name_symbol = env->name_string();
|
|
Local<String> port_symbol = env->port_string();
|
|
Local<String> priority_symbol = env->priority_string();
|
|
Local<String> weight_symbol = env->weight_string();
|
|
Local<String> type_symbol = env->type_string();
|
|
Local<String> srv_symbol = env->dns_srv_string();
|
|
|
|
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,
|
|
name_symbol,
|
|
OneByteString(env->isolate(), current->host)).FromJust();
|
|
srv_record->Set(context,
|
|
port_symbol,
|
|
Integer::New(env->isolate(), current->port)).FromJust();
|
|
srv_record->Set(context,
|
|
priority_symbol,
|
|
Integer::New(env->isolate(), current->priority)).FromJust();
|
|
srv_record->Set(context,
|
|
weight_symbol,
|
|
Integer::New(env->isolate(), current->weight)).FromJust();
|
|
if (need_type)
|
|
srv_record->Set(context, type_symbol, srv_symbol).FromJust();
|
|
|
|
ret->Set(context, i + offset, srv_record).FromJust();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Local<String> flags_symbol = env->flags_string();
|
|
Local<String> service_symbol = env->service_string();
|
|
Local<String> regexp_symbol = env->regexp_string();
|
|
Local<String> replacement_symbol = env->replacement_string();
|
|
Local<String> order_symbol = env->order_string();
|
|
Local<String> preference_symbol = env->preference_string();
|
|
Local<String> type_symbol = env->type_string();
|
|
Local<String> naptr_symbol = env->dns_naptr_string();
|
|
|
|
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,
|
|
flags_symbol,
|
|
OneByteString(env->isolate(), current->flags)).FromJust();
|
|
naptr_record->Set(context,
|
|
service_symbol,
|
|
OneByteString(env->isolate(),
|
|
current->service)).FromJust();
|
|
naptr_record->Set(context,
|
|
regexp_symbol,
|
|
OneByteString(env->isolate(),
|
|
current->regexp)).FromJust();
|
|
naptr_record->Set(context,
|
|
replacement_symbol,
|
|
OneByteString(env->isolate(),
|
|
current->replacement)).FromJust();
|
|
naptr_record->Set(context,
|
|
order_symbol,
|
|
Integer::New(env->isolate(), current->order)).FromJust();
|
|
naptr_record->Set(context,
|
|
preference_symbol,
|
|
Integer::New(env->isolate(),
|
|
current->preference)).FromJust();
|
|
if (need_type)
|
|
naptr_record->Set(context, type_symbol, naptr_symbol).FromJust();
|
|
|
|
ret->Set(context, i + offset, naptr_record).FromJust();
|
|
}
|
|
|
|
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();
|
|
|
|
/* Can't use ares_parse_soa_reply() here which can only parse single record */
|
|
unsigned int ancount = cares_get_16bit(buf + 6);
|
|
unsigned char* ptr = buf + NS_HFIXEDSZ;
|
|
int rr_type, rr_len;
|
|
char* name;
|
|
char* rr_name;
|
|
long temp_len; // NOLINT(runtime/int)
|
|
int status = ares_expand_name(ptr, buf, len, &name, &temp_len);
|
|
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) {
|
|
free(name);
|
|
return ARES_EBADRESP;
|
|
}
|
|
ptr += temp_len + NS_QFIXEDSZ;
|
|
|
|
for (unsigned int i = 0; i < ancount; i++) {
|
|
status = ares_expand_name(ptr, buf, len, &rr_name, &temp_len);
|
|
|
|
if (status != ARES_SUCCESS)
|
|
break;
|
|
|
|
ptr += temp_len;
|
|
if (ptr + NS_RRFIXEDSZ > buf + len) {
|
|
free(rr_name);
|
|
status = ARES_EBADRESP;
|
|
break;
|
|
}
|
|
|
|
rr_type = cares_get_16bit(ptr);
|
|
rr_len = cares_get_16bit(ptr + 8);
|
|
ptr += NS_RRFIXEDSZ;
|
|
|
|
/* only need SOA */
|
|
if (rr_type == ns_t_soa) {
|
|
ares_soa_reply soa;
|
|
|
|
status = ares_expand_name(ptr, buf, len, &soa.nsname, &temp_len);
|
|
if (status != ARES_SUCCESS) {
|
|
free(rr_name);
|
|
break;
|
|
}
|
|
ptr += temp_len;
|
|
|
|
status = ares_expand_name(ptr, buf, len, &soa.hostmaster, &temp_len);
|
|
if (status != ARES_SUCCESS) {
|
|
free(rr_name);
|
|
free(soa.nsname);
|
|
break;
|
|
}
|
|
ptr += temp_len;
|
|
|
|
if (ptr + 5 * 4 > buf + len) {
|
|
free(rr_name);
|
|
free(soa.nsname);
|
|
free(soa.hostmaster);
|
|
status = ARES_EBADRESP;
|
|
break;
|
|
}
|
|
|
|
soa.serial = cares_get_32bit(ptr + 0 * 4);
|
|
soa.refresh = cares_get_32bit(ptr + 1 * 4);
|
|
soa.retry = cares_get_32bit(ptr + 2 * 4);
|
|
soa.expire = cares_get_32bit(ptr + 3 * 4);
|
|
soa.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(), soa.nsname)).FromJust();
|
|
soa_record->Set(context,
|
|
env->hostmaster_string(),
|
|
OneByteString(env->isolate(),
|
|
soa.hostmaster)).FromJust();
|
|
soa_record->Set(context,
|
|
env->serial_string(),
|
|
Integer::New(env->isolate(), soa.serial)).FromJust();
|
|
soa_record->Set(context,
|
|
env->refresh_string(),
|
|
Integer::New(env->isolate(), soa.refresh)).FromJust();
|
|
soa_record->Set(context,
|
|
env->retry_string(),
|
|
Integer::New(env->isolate(), soa.retry)).FromJust();
|
|
soa_record->Set(context,
|
|
env->expire_string(),
|
|
Integer::New(env->isolate(), soa.expire)).FromJust();
|
|
soa_record->Set(context,
|
|
env->minttl_string(),
|
|
Integer::New(env->isolate(), soa.minttl)).FromJust();
|
|
soa_record->Set(context,
|
|
env->type_string(),
|
|
env->dns_soa_string()).FromJust();
|
|
|
|
free(soa.nsname);
|
|
free(soa.hostmaster);
|
|
|
|
*ret = handle_scope.Escape(soa_record);
|
|
break;
|
|
}
|
|
|
|
free(rr_name);
|
|
ptr += rr_len;
|
|
}
|
|
|
|
free(name);
|
|
|
|
if (status != ARES_SUCCESS) {
|
|
return status == ARES_EBADNAME ? ARES_EBADRESP : status;
|
|
}
|
|
|
|
return ARES_SUCCESS;
|
|
}
|
|
|
|
|
|
class QueryAnyWrap: public QueryWrap {
|
|
public:
|
|
QueryAnyWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
|
|
: QueryWrap(channel, req_wrap_obj) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_any);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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);
|
|
int a_count = ret->Length();
|
|
if (status != ARES_SUCCESS && status != ARES_ENODATA) {
|
|
ParseError(status);
|
|
return;
|
|
}
|
|
|
|
if (type == ns_t_a) {
|
|
CHECK_EQ(naddrttls, a_count);
|
|
for (int i = 0; i < a_count; i++) {
|
|
Local<Object> obj = Object::New(env()->isolate());
|
|
obj->Set(context, env()->address_string(), ret->Get(i)).FromJust();
|
|
obj->Set(context,
|
|
env()->ttl_string(),
|
|
Integer::New(env()->isolate(), addrttls[i].ttl)).FromJust();
|
|
obj->Set(context,
|
|
env()->type_string(),
|
|
env()->dns_a_string()).FromJust();
|
|
ret->Set(context, i, obj).FromJust();
|
|
}
|
|
} else {
|
|
for (int i = 0; i < a_count; i++) {
|
|
Local<Object> obj = Object::New(env()->isolate());
|
|
obj->Set(context, env()->value_string(), ret->Get(i)).FromJust();
|
|
obj->Set(context,
|
|
env()->type_string(),
|
|
env()->dns_cname_string()).FromJust();
|
|
ret->Set(context, i, obj).FromJust();
|
|
}
|
|
}
|
|
|
|
/* Parse AAAA records */
|
|
ares_addr6ttl addr6ttls[256];
|
|
int naddr6ttls = arraysize(addr6ttls);
|
|
|
|
type = ns_t_aaaa;
|
|
status = ParseGeneralReply(env(),
|
|
buf,
|
|
len,
|
|
&type,
|
|
ret,
|
|
addr6ttls,
|
|
&naddr6ttls);
|
|
int aaaa_count = ret->Length() - a_count;
|
|
if (status != ARES_SUCCESS && status != ARES_ENODATA) {
|
|
ParseError(status);
|
|
return;
|
|
}
|
|
|
|
CHECK_EQ(aaaa_count, naddr6ttls);
|
|
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(i)).FromJust();
|
|
obj->Set(context,
|
|
env()->ttl_string(),
|
|
Integer::New(env()->isolate(), addr6ttls[i].ttl)).FromJust();
|
|
obj->Set(context,
|
|
env()->type_string(),
|
|
env()->dns_aaaa_string()).FromJust();
|
|
ret->Set(context, i, obj).FromJust();
|
|
}
|
|
|
|
/* 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(i)).FromJust();
|
|
obj->Set(context,
|
|
env()->type_string(),
|
|
env()->dns_ns_string()).FromJust();
|
|
ret->Set(context, i, obj).FromJust();
|
|
}
|
|
|
|
/* 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(i)).FromJust();
|
|
obj->Set(context,
|
|
env()->type_string(),
|
|
env()->dns_ptr_string()).FromJust();
|
|
ret->Set(context, i, obj).FromJust();
|
|
}
|
|
|
|
/* 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).FromJust();
|
|
|
|
CallOnComplete(ret);
|
|
}
|
|
};
|
|
|
|
|
|
class QueryAWrap: public QueryWrap {
|
|
public:
|
|
QueryAWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
|
|
: QueryWrap(channel, req_wrap_obj) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_a);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_aaaa);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_cname);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_mx);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_ns);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_txt);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_srv);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_ptr);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_naptr);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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) {
|
|
}
|
|
|
|
int Send(const char* name) override {
|
|
AresQuery(name, ns_c_in, ns_t_soa);
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
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)).FromJust();
|
|
soa_record->Set(context,
|
|
env()->hostmaster_string(),
|
|
OneByteString(env()->isolate(),
|
|
soa_out->hostmaster)).FromJust();
|
|
soa_record->Set(context,
|
|
env()->serial_string(),
|
|
Integer::New(env()->isolate(), soa_out->serial)).FromJust();
|
|
soa_record->Set(context,
|
|
env()->refresh_string(),
|
|
Integer::New(env()->isolate(),
|
|
soa_out->refresh)).FromJust();
|
|
soa_record->Set(context,
|
|
env()->retry_string(),
|
|
Integer::New(env()->isolate(), soa_out->retry)).FromJust();
|
|
soa_record->Set(context,
|
|
env()->expire_string(),
|
|
Integer::New(env()->isolate(), soa_out->expire)).FromJust();
|
|
soa_record->Set(context,
|
|
env()->minttl_string(),
|
|
Integer::New(env()->isolate(), soa_out->minttl)).FromJust();
|
|
|
|
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) {
|
|
}
|
|
|
|
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.
|
|
}
|
|
|
|
ares_gethostbyaddr(channel_->cares_channel(),
|
|
address_buffer,
|
|
length,
|
|
family,
|
|
Callback,
|
|
static_cast<void*>(static_cast<QueryWrap*>(this)));
|
|
return 0;
|
|
}
|
|
|
|
size_t self_size() const override { return sizeof(*this); }
|
|
|
|
protected:
|
|
void Parse(struct hostent* host) override {
|
|
HandleScope handle_scope(env()->isolate());
|
|
Context::Scope context_scope(env()->context());
|
|
this->CallOnComplete(HostentToNames(env(), host));
|
|
}
|
|
};
|
|
|
|
|
|
class GetHostByNameWrap: public QueryWrap {
|
|
public:
|
|
explicit GetHostByNameWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
|
|
: QueryWrap(channel, req_wrap_obj) {
|
|
}
|
|
|
|
int Send(const char* name, int family) override {
|
|
ares_gethostbyname(channel_->cares_channel(),
|
|
name,
|
|
family,
|
|
Callback,
|
|
static_cast<void*>(static_cast<QueryWrap*>(this)));
|
|
return 0;
|
|
}
|
|
|
|
protected:
|
|
void Parse(struct hostent* host) override {
|
|
HandleScope scope(env()->isolate());
|
|
|
|
Local<Array> addresses = HostentToAddresses(env(), host);
|
|
Local<Integer> family = Integer::New(env()->isolate(), host->h_addrtype);
|
|
|
|
this->CallOnComplete(addresses, family);
|
|
}
|
|
};
|
|
|
|
|
|
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>();
|
|
Wrap* wrap = new Wrap(channel, req_wrap_obj);
|
|
|
|
node::Utf8Value name(env->isolate(), string);
|
|
int err = wrap->Send(*name);
|
|
if (err)
|
|
delete wrap;
|
|
|
|
args.GetReturnValue().Set(err);
|
|
}
|
|
|
|
|
|
void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
|
|
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())
|
|
};
|
|
|
|
if (status == 0) {
|
|
int n = 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(n, s);
|
|
n++;
|
|
}
|
|
};
|
|
|
|
const bool verbatim = req_wrap->verbatim();
|
|
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);
|
|
|
|
// Make the callback into JavaScript
|
|
req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
|
|
|
|
delete req_wrap;
|
|
}
|
|
|
|
|
|
void AfterGetNameInfo(uv_getnameinfo_t* req,
|
|
int status,
|
|
const char* hostname,
|
|
const char* service) {
|
|
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;
|
|
}
|
|
|
|
// Make the callback into JavaScript
|
|
req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
|
|
|
|
delete req_wrap;
|
|
}
|
|
|
|
|
|
void IsIP(const FunctionCallbackInfo<Value>& args) {
|
|
node::Utf8Value ip(args.GetIsolate(), args[0]);
|
|
char address_buffer[sizeof(struct in6_addr)];
|
|
|
|
int rc = 0;
|
|
if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0)
|
|
rc = 4;
|
|
else if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0)
|
|
rc = 6;
|
|
|
|
args.GetReturnValue().Set(rc);
|
|
}
|
|
|
|
void IsIPv4(const FunctionCallbackInfo<Value>& args) {
|
|
node::Utf8Value ip(args.GetIsolate(), args[0]);
|
|
char address_buffer[sizeof(struct in_addr)];
|
|
|
|
if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) {
|
|
args.GetReturnValue().Set(true);
|
|
} else {
|
|
args.GetReturnValue().Set(false);
|
|
}
|
|
}
|
|
|
|
void IsIPv6(const FunctionCallbackInfo<Value>& args) {
|
|
node::Utf8Value ip(args.GetIsolate(), args[0]);
|
|
char address_buffer[sizeof(struct in6_addr)];
|
|
|
|
if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) {
|
|
args.GetReturnValue().Set(true);
|
|
} else {
|
|
args.GetReturnValue().Set(false);
|
|
}
|
|
}
|
|
|
|
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 = (args[3]->IsInt32()) ? args[3]->Int32Value() : 0;
|
|
int family;
|
|
|
|
switch (args[2]->Int32Value()) {
|
|
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 = new GetAddrInfoReqWrap(env, req_wrap_obj, args[4]->IsTrue());
|
|
|
|
struct addrinfo hints;
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
hints.ai_family = family;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = flags;
|
|
|
|
int err = uv_getaddrinfo(env->event_loop(),
|
|
req_wrap->req(),
|
|
AfterGetAddrInfo,
|
|
*hostname,
|
|
nullptr,
|
|
&hints);
|
|
req_wrap->Dispatched();
|
|
if (err)
|
|
delete req_wrap;
|
|
|
|
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();
|
|
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);
|
|
|
|
GetNameInfoReqWrap* req_wrap = new GetNameInfoReqWrap(env, req_wrap_obj);
|
|
|
|
int err = uv_getnameinfo(env->event_loop(),
|
|
req_wrap->req(),
|
|
AfterGetNameInfo,
|
|
(struct sockaddr*)&addr,
|
|
NI_NAMEREQD);
|
|
req_wrap->Dispatched();
|
|
if (err)
|
|
delete req_wrap;
|
|
|
|
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);
|
|
|
|
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<Array> ret = Array::New(env->isolate(), 2);
|
|
ret->Set(0, OneByteString(env->isolate(), ip));
|
|
ret->Set(1, Integer::New(env->isolate(), cur->udp_port));
|
|
|
|
server_array->Set(i, ret);
|
|
}
|
|
|
|
ares_free_data(servers);
|
|
|
|
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());
|
|
|
|
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(i)->IsArray());
|
|
|
|
Local<Array> elm = Local<Array>::Cast(arr->Get(i));
|
|
|
|
CHECK(elm->Get(0)->Int32Value());
|
|
CHECK(elm->Get(1)->IsString());
|
|
CHECK(elm->Get(2)->Int32Value());
|
|
|
|
int fam = elm->Get(0)->Int32Value();
|
|
node::Utf8Value ip(env->isolate(), elm->Get(1));
|
|
int port = elm->Get(2)->Int32Value();
|
|
|
|
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());
|
|
|
|
ares_cancel(channel->cares_channel());
|
|
}
|
|
|
|
|
|
void StrError(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
const char* errmsg = ares_strerror(args[0]->Int32Value());
|
|
args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg));
|
|
}
|
|
|
|
|
|
void Initialize(Local<Object> target,
|
|
Local<Value> unused,
|
|
Local<Context> context) {
|
|
Environment* env = Environment::GetCurrent(context);
|
|
|
|
env->SetMethod(target, "getaddrinfo", GetAddrInfo);
|
|
env->SetMethod(target, "getnameinfo", GetNameInfo);
|
|
env->SetMethod(target, "isIP", IsIP);
|
|
env->SetMethod(target, "isIPv4", IsIPv4);
|
|
env->SetMethod(target, "isIPv6", IsIPv6);
|
|
|
|
env->SetMethod(target, "strerror", StrError);
|
|
|
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"),
|
|
Integer::New(env->isolate(), AF_INET));
|
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET6"),
|
|
Integer::New(env->isolate(), AF_INET6));
|
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AF_UNSPEC"),
|
|
Integer::New(env->isolate(), AF_UNSPEC));
|
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_ADDRCONFIG"),
|
|
Integer::New(env->isolate(), AI_ADDRCONFIG));
|
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_V4MAPPED"),
|
|
Integer::New(env->isolate(), AI_V4MAPPED));
|
|
|
|
auto is_construct_call_callback =
|
|
[](const FunctionCallbackInfo<Value>& args) {
|
|
CHECK(args.IsConstructCall());
|
|
ClearWrap(args.This());
|
|
};
|
|
Local<FunctionTemplate> aiw =
|
|
FunctionTemplate::New(env->isolate(), is_construct_call_callback);
|
|
aiw->InstanceTemplate()->SetInternalFieldCount(1);
|
|
AsyncWrap::AddWrapMethods(env, aiw);
|
|
Local<String> addrInfoWrapString =
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap");
|
|
aiw->SetClassName(addrInfoWrapString);
|
|
target->Set(addrInfoWrapString, aiw->GetFunction());
|
|
|
|
Local<FunctionTemplate> niw =
|
|
FunctionTemplate::New(env->isolate(), is_construct_call_callback);
|
|
niw->InstanceTemplate()->SetInternalFieldCount(1);
|
|
AsyncWrap::AddWrapMethods(env, niw);
|
|
Local<String> nameInfoWrapString =
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap");
|
|
niw->SetClassName(nameInfoWrapString);
|
|
target->Set(nameInfoWrapString, niw->GetFunction());
|
|
|
|
Local<FunctionTemplate> qrw =
|
|
FunctionTemplate::New(env->isolate(), is_construct_call_callback);
|
|
qrw->InstanceTemplate()->SetInternalFieldCount(1);
|
|
AsyncWrap::AddWrapMethods(env, qrw);
|
|
Local<String> queryWrapString =
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap");
|
|
qrw->SetClassName(queryWrapString);
|
|
target->Set(queryWrapString, qrw->GetFunction());
|
|
|
|
Local<FunctionTemplate> channel_wrap =
|
|
env->NewFunctionTemplate(ChannelWrap::New);
|
|
channel_wrap->InstanceTemplate()->SetInternalFieldCount(1);
|
|
AsyncWrap::AddWrapMethods(env, channel_wrap);
|
|
|
|
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->SetProtoMethod(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(channelWrapString, channel_wrap->GetFunction());
|
|
}
|
|
|
|
} // anonymous namespace
|
|
} // namespace cares_wrap
|
|
} // namespace node
|
|
|
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(cares_wrap, node::cares_wrap::Initialize)
|
|
|