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.
 
 
 
 
 
 

1397 lines
39 KiB

#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 "tree.h"
#include "util.h"
#include "uv.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#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::Function;
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;
class GetAddrInfoReqWrap : public ReqWrap<uv_getaddrinfo_t> {
public:
GetAddrInfoReqWrap(Environment* env, Local<Object> req_wrap_obj);
size_t self_size() const override { return sizeof(*this); }
};
GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env,
Local<Object> req_wrap_obj)
: ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) {
Wrap(req_wrap_obj, this);
}
static void NewGetAddrInfoReqWrap(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
}
class GetNameInfoReqWrap : public ReqWrap<uv_getnameinfo_t> {
public:
GetNameInfoReqWrap(Environment* env, Local<Object> req_wrap_obj);
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);
}
static void NewGetNameInfoReqWrap(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
}
static void NewQueryReqWrap(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
}
static int cmp_ares_tasks(const ares_task_t* a, const ares_task_t* b) {
if (a->sock < b->sock)
return -1;
if (a->sock > b->sock)
return 1;
return 0;
}
RB_GENERATE_STATIC(ares_task_list, ares_task_t, node, cmp_ares_tasks)
/* This is called once per second by loop->timer. It is used to constantly */
/* call back into c-ares for possibly processing timeouts. */
static void ares_timeout(uv_timer_t* handle) {
Environment* env = Environment::from_cares_timer_handle(handle);
CHECK_EQ(false, RB_EMPTY(env->cares_task_list()));
ares_process_fd(env->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD);
}
static void ares_poll_cb(uv_poll_t* watcher, int status, int events) {
ares_task_t* task = ContainerOf(&ares_task_t::poll_watcher, watcher);
Environment* env = task->env;
/* Reset the idle timer */
uv_timer_again(env->cares_timer_handle());
if (status < 0) {
/* An error happened. Just pretend that the socket is both readable and */
/* writable. */
ares_process_fd(env->cares_channel(), task->sock, task->sock);
return;
}
/* Process DNS responses */
ares_process_fd(env->cares_channel(),
events & UV_READABLE ? task->sock : ARES_SOCKET_BAD,
events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD);
}
static void ares_poll_close_cb(uv_handle_t* watcher) {
ares_task_t* task = ContainerOf(&ares_task_t::poll_watcher,
reinterpret_cast<uv_poll_t*>(watcher));
free(task);
}
/* Allocates and returns a new ares_task_t */
static ares_task_t* ares_task_create(Environment* env, ares_socket_t sock) {
ares_task_t* task = static_cast<ares_task_t*>(malloc(sizeof(*task)));
if (task == nullptr) {
/* Out of memory. */
return nullptr;
}
task->env = env;
task->sock = sock;
if (uv_poll_init_socket(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 */
static void ares_sockstate_cb(void* data,
ares_socket_t sock,
int read,
int write) {
Environment* env = static_cast<Environment*>(data);
ares_task_t* task;
ares_task_t lookup_task;
lookup_task.sock = sock;
task = RB_FIND(ares_task_list, env->cares_task_list(), &lookup_task);
if (read || write) {
if (!task) {
/* New socket */
/* If this is the first socket then start the timer. */
uv_timer_t* timer_handle = env->cares_timer_handle();
if (!uv_is_active(reinterpret_cast<uv_handle_t*>(timer_handle))) {
CHECK(RB_EMPTY(env->cares_task_list()));
uv_timer_start(timer_handle, ares_timeout, 1000, 1000);
}
task = ares_task_create(env, 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 the query */
/* will eventually time out. */
return;
}
RB_INSERT(ares_task_list, env->cares_task_list(), 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");
RB_REMOVE(ares_task_list, env->cares_task_list(), task);
uv_close(reinterpret_cast<uv_handle_t*>(&task->poll_watcher),
ares_poll_close_cb);
if (RB_EMPTY(env->cares_task_list())) {
uv_timer_stop(env->cares_timer_handle());
}
}
}
static Local<Array> HostentToAddresses(Environment* env, struct hostent* host) {
EscapableHandleScope scope(env->isolate());
Local<Array> addresses = Array::New(env->isolate());
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(i, address);
}
return scope.Escape(addresses);
}
static Local<Array> HostentToNames(Environment* env, struct hostent* host) {
EscapableHandleScope scope(env->isolate());
Local<Array> names = Array::New(env->isolate());
for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) {
Local<String> address = OneByteString(env->isolate(), host->h_aliases[i]);
names->Set(i, address);
}
return scope.Escape(names);
}
class QueryWrap : public AsyncWrap {
public:
QueryWrap(Environment* env, Local<Object> req_wrap_obj)
: AsyncWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP) {
if (env->in_domain())
req_wrap_obj->Set(env->domain_string(), env->domain_array()->Get(0));
}
virtual ~QueryWrap() override {
CHECK_EQ(false, persistent().IsEmpty());
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* GetQueryArg() {
return static_cast<void*>(this);
}
static void Callback(void *arg, int status, int timeouts,
unsigned char* answer_buf, int answer_len) {
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
if (status != ARES_SUCCESS) {
wrap->ParseError(status);
} else {
wrap->Parse(answer_buf, answer_len);
}
delete wrap;
}
static void Callback(void *arg, int status, int timeouts,
struct hostent* host) {
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
if (status != ARES_SUCCESS) {
wrap->ParseError(status);
} else {
wrap->Parse(host);
}
delete wrap;
}
void CallOnComplete(Local<Value> answer) {
HandleScope handle_scope(env()->isolate());
Context::Scope context_scope(env()->context());
Local<Value> argv[] = {
Integer::New(env()->isolate(), 0),
answer
};
MakeCallback(env()->oncomplete_string(), ARRAY_SIZE(argv), argv);
}
void CallOnComplete(Local<Value> answer, Local<Value> family) {
HandleScope handle_scope(env()->isolate());
Context::Scope context_scope(env()->context());
Local<Value> argv[] = {
Integer::New(env()->isolate(), 0),
answer,
family
};
MakeCallback(env()->oncomplete_string(), ARRAY_SIZE(argv), argv);
}
void ParseError(int status) {
CHECK_NE(status, ARES_SUCCESS);
HandleScope handle_scope(env()->isolate());
Context::Scope context_scope(env()->context());
Local<Value> arg;
switch (status) {
#define V(code) \
case ARES_ ## code: \
arg = FIXED_ONE_BYTE_STRING(env()->isolate(), #code); \
break;
V(ENODATA)
V(EFORMERR)
V(ESERVFAIL)
V(ENOTFOUND)
V(ENOTIMP)
V(EREFUSED)
V(EBADQUERY)
V(EBADNAME)
V(EBADFAMILY)
V(EBADRESP)
V(ECONNREFUSED)
V(ETIMEOUT)
V(EOF)
V(EFILE)
V(ENOMEM)
V(EDESTRUCTION)
V(EBADSTR)
V(EBADFLAGS)
V(ENONAME)
V(EBADHINTS)
V(ENOTINITIALIZED)
V(ELOADIPHLPAPI)
V(EADDRGETNETWORKPARAMS)
V(ECANCELLED)
#undef V
default:
arg = FIXED_ONE_BYTE_STRING(env()->isolate(), "UNKNOWN_ARES_ERROR");
break;
}
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();
};
};
class QueryAWrap: public QueryWrap {
public:
QueryAWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_a,
Callback,
GetQueryArg());
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());
struct hostent* host;
int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> addresses = HostentToAddresses(env(), host);
ares_free_hostent(host);
this->CallOnComplete(addresses);
}
};
class QueryAaaaWrap: public QueryWrap {
public:
QueryAaaaWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_aaaa,
Callback,
GetQueryArg());
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());
struct hostent* host;
int status = ares_parse_aaaa_reply(buf, len, &host, nullptr, nullptr);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> addresses = HostentToAddresses(env(), host);
ares_free_hostent(host);
this->CallOnComplete(addresses);
}
};
class QueryCnameWrap: public QueryWrap {
public:
QueryCnameWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_cname,
Callback,
GetQueryArg());
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());
struct hostent* host;
int status = ares_parse_a_reply(buf, len, &host, nullptr, nullptr);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
// A cname lookup always returns a single record but we follow the
// common API here.
Local<Array> result = Array::New(env()->isolate(), 1);
result->Set(0, OneByteString(env()->isolate(), host->h_name));
ares_free_hostent(host);
this->CallOnComplete(result);
}
};
class QueryMxWrap: public QueryWrap {
public:
QueryMxWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_mx,
Callback,
GetQueryArg());
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());
struct ares_mx_reply* mx_start;
int status = ares_parse_mx_reply(buf, len, &mx_start);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> mx_records = Array::New(env()->isolate());
Local<String> exchange_symbol = env()->exchange_string();
Local<String> priority_symbol = env()->priority_string();
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(exchange_symbol,
OneByteString(env()->isolate(), current->host));
mx_record->Set(priority_symbol,
Integer::New(env()->isolate(), current->priority));
mx_records->Set(i, mx_record);
}
ares_free_data(mx_start);
this->CallOnComplete(mx_records);
}
};
class QueryNsWrap: public QueryWrap {
public:
QueryNsWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_ns,
Callback,
GetQueryArg());
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());
struct hostent* host;
int status = ares_parse_ns_reply(buf, len, &host);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> names = HostentToNames(env(), host);
ares_free_hostent(host);
this->CallOnComplete(names);
}
};
class QueryTxtWrap: public QueryWrap {
public:
QueryTxtWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_txt,
Callback,
GetQueryArg());
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());
struct ares_txt_ext* txt_out;
int status = ares_parse_txt_reply_ext(buf, len, &txt_out);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> txt_records = Array::New(env()->isolate());
Local<Array> txt_chunk;
struct ares_txt_ext* current = txt_out;
uint32_t i = 0;
for (uint32_t 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())
txt_records->Set(i++, txt_chunk);
txt_chunk = Array::New(env()->isolate());
j = 0;
}
txt_chunk->Set(j++, txt);
}
// Push last chunk if it isn't empty
if (!txt_chunk.IsEmpty())
txt_records->Set(i, txt_chunk);
ares_free_data(txt_out);
this->CallOnComplete(txt_records);
}
};
class QuerySrvWrap: public QueryWrap {
public:
explicit QuerySrvWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_srv,
Callback,
GetQueryArg());
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());
struct ares_srv_reply* srv_start;
int status = ares_parse_srv_reply(buf, len, &srv_start);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> srv_records = Array::New(env()->isolate());
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();
ares_srv_reply* current = srv_start;
for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
Local<Object> srv_record = Object::New(env()->isolate());
srv_record->Set(name_symbol,
OneByteString(env()->isolate(), current->host));
srv_record->Set(port_symbol,
Integer::New(env()->isolate(), current->port));
srv_record->Set(priority_symbol,
Integer::New(env()->isolate(), current->priority));
srv_record->Set(weight_symbol,
Integer::New(env()->isolate(), current->weight));
srv_records->Set(i, srv_record);
}
ares_free_data(srv_start);
this->CallOnComplete(srv_records);
}
};
class QueryPtrWrap: public QueryWrap {
public:
explicit QueryPtrWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_ptr,
Callback,
GetQueryArg());
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());
struct hostent* host;
int status = ares_parse_ptr_reply(buf, len, NULL, 0, AF_INET, &host);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> aliases = Array::New(env()->isolate());
for (uint32_t i = 0; host->h_aliases[i] != NULL; i++) {
aliases->Set(i, OneByteString(env()->isolate(), host->h_aliases[i]));
}
ares_free_hostent(host);
this->CallOnComplete(aliases);
}
};
class QueryNaptrWrap: public QueryWrap {
public:
explicit QueryNaptrWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_naptr,
Callback,
GetQueryArg());
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_naptr_reply* naptr_start;
int status = ares_parse_naptr_reply(buf, len, &naptr_start);
if (status != ARES_SUCCESS) {
ParseError(status);
return;
}
Local<Array> naptr_records = Array::New(env()->isolate());
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();
ares_naptr_reply* current = naptr_start;
for (uint32_t i = 0; current != nullptr; ++i, current = current->next) {
Local<Object> naptr_record = Object::New(env()->isolate());
naptr_record->Set(flags_symbol,
OneByteString(env()->isolate(), current->flags));
naptr_record->Set(service_symbol,
OneByteString(env()->isolate(), current->service));
naptr_record->Set(regexp_symbol,
OneByteString(env()->isolate(), current->regexp));
naptr_record->Set(replacement_symbol,
OneByteString(env()->isolate(), current->replacement));
naptr_record->Set(order_symbol,
Integer::New(env()->isolate(), current->order));
naptr_record->Set(preference_symbol,
Integer::New(env()->isolate(), current->preference));
naptr_records->Set(i, naptr_record);
}
ares_free_data(naptr_start);
this->CallOnComplete(naptr_records);
}
};
class QuerySoaWrap: public QueryWrap {
public:
QuerySoaWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name) override {
ares_query(env()->cares_channel(),
name,
ns_c_in,
ns_t_soa,
Callback,
GetQueryArg());
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_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(env()->nsname_string(),
OneByteString(env()->isolate(), soa_out->nsname));
soa_record->Set(env()->hostmaster_string(),
OneByteString(env()->isolate(), soa_out->hostmaster));
soa_record->Set(env()->serial_string(),
Integer::New(env()->isolate(), soa_out->serial));
soa_record->Set(env()->refresh_string(),
Integer::New(env()->isolate(), soa_out->refresh));
soa_record->Set(env()->retry_string(),
Integer::New(env()->isolate(), soa_out->retry));
soa_record->Set(env()->expire_string(),
Integer::New(env()->isolate(), soa_out->expire));
soa_record->Set(env()->minttl_string(),
Integer::New(env()->isolate(), soa_out->minttl));
ares_free_data(soa_out);
this->CallOnComplete(soa_record);
}
};
class GetHostByAddrWrap: public QueryWrap {
public:
explicit GetHostByAddrWrap(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, 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(env()->cares_channel(),
address_buffer,
length,
family,
Callback,
GetQueryArg());
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(Environment* env, Local<Object> req_wrap_obj)
: QueryWrap(env, req_wrap_obj) {
}
int Send(const char* name, int family) override {
ares_gethostbyname(env()->cares_channel(),
name,
family,
Callback,
GetQueryArg());
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);
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(env, 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) {
// Success
struct addrinfo *address;
int n = 0;
// Create the response array.
Local<Array> results = Array::New(env->isolate());
char ip[INET6_ADDRSTRLEN];
const char *addr;
// Iterate over the IPv4 responses again this time creating javascript
// strings for each IP and filling the results array.
address = res;
while (address) {
CHECK_EQ(address->ai_socktype, SOCK_STREAM);
// Ignore random ai_family types.
if (address->ai_family == AF_INET) {
// Juggle pointers
addr = reinterpret_cast<char*>(&(reinterpret_cast<struct sockaddr_in*>(
address->ai_addr)->sin_addr));
int err = uv_inet_ntop(address->ai_family,
addr,
ip,
INET6_ADDRSTRLEN);
if (err)
continue;
// Create JavaScript string
Local<String> s = OneByteString(env->isolate(), ip);
results->Set(n, s);
n++;
}
// Increment
address = address->ai_next;
}
// Iterate over the IPv6 responses putting them in the array.
address = res;
while (address) {
CHECK_EQ(address->ai_socktype, SOCK_STREAM);
// Ignore random ai_family types.
if (address->ai_family == AF_INET6) {
// Juggle pointers
addr = reinterpret_cast<char*>(&(reinterpret_cast<struct sockaddr_in6*>(
address->ai_addr)->sin6_addr));
int err = uv_inet_ntop(address->ai_family,
addr,
ip,
INET6_ADDRSTRLEN);
if (err)
continue;
// Create JavaScript string
Local<String> s = OneByteString(env->isolate(), ip);
results->Set(n, s);
n++;
}
// Increment
address = address->ai_next;
}
// 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(), ARRAY_SIZE(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(), ARRAY_SIZE(argv), argv);
delete req_wrap;
}
static 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);
}
static 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);
}
}
static 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);
}
}
static void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsObject());
CHECK(args[1]->IsString());
CHECK(args[2]->IsInt32());
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");
ABORT();
}
GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj);
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);
}
static 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);
}
static void GetServers(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Array> server_array = Array::New(env->isolate());
ares_addr_node* servers;
int r = ares_get_servers(env->cares_channel(), &servers);
CHECK_EQ(r, ARES_SUCCESS);
ares_addr_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<String> addr = OneByteString(env->isolate(), ip);
server_array->Set(i, addr);
}
ares_free_data(servers);
args.GetReturnValue().Set(server_array);
}
static void SetServers(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
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(env->cares_channel(), nullptr);
return args.GetReturnValue().Set(rv);
}
ares_addr_node* servers = new ares_addr_node[len];
ares_addr_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());
int fam = elm->Get(0)->Int32Value();
node::Utf8Value ip(env->isolate(), elm->Get(1));
ares_addr_node* cur = &servers[i];
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.");
ABORT();
}
if (err)
break;
cur->next = nullptr;
if (last != nullptr)
last->next = cur;
last = cur;
}
if (err == 0)
err = ares_set_servers(env->cares_channel(), &servers[0]);
else
err = ARES_EBADSTR;
delete[] servers;
args.GetReturnValue().Set(err);
}
static 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));
}
static void CaresTimerCloseCb(uv_handle_t* handle) {
Environment* env = Environment::from_cares_timer_handle(
reinterpret_cast<uv_timer_t*>(handle));
env->FinishHandleCleanup(handle);
}
static void CaresTimerClose(Environment* env,
uv_handle_t* handle,
void* arg) {
uv_close(handle, CaresTimerCloseCb);
}
static void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context) {
Environment* env = Environment::GetCurrent(context);
int r = ares_library_init(ARES_LIB_INIT_ALL);
CHECK_EQ(r, ARES_SUCCESS);
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 = env;
/* We do the call to ares_init_option for caller. */
r = ares_init_options(env->cares_channel_ptr(),
&options,
ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB);
CHECK_EQ(r, ARES_SUCCESS);
/* Initialize the timeout timer. The timer won't be started until the */
/* first socket is opened. */
uv_timer_init(env->event_loop(), env->cares_timer_handle());
env->RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(env->cares_timer_handle()),
CaresTimerClose,
nullptr);
env->SetMethod(target, "queryA", Query<QueryAWrap>);
env->SetMethod(target, "queryAaaa", Query<QueryAaaaWrap>);
env->SetMethod(target, "queryCname", Query<QueryCnameWrap>);
env->SetMethod(target, "queryMx", Query<QueryMxWrap>);
env->SetMethod(target, "queryNs", Query<QueryNsWrap>);
env->SetMethod(target, "queryTxt", Query<QueryTxtWrap>);
env->SetMethod(target, "querySrv", Query<QuerySrvWrap>);
env->SetMethod(target, "queryPtr", Query<QueryPtrWrap>);
env->SetMethod(target, "queryNaptr", Query<QueryNaptrWrap>);
env->SetMethod(target, "querySoa", Query<QuerySoaWrap>);
env->SetMethod(target, "getHostByAddr", Query<GetHostByAddrWrap>);
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);
env->SetMethod(target, "getServers", GetServers);
env->SetMethod(target, "setServers", SetServers);
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));
Local<FunctionTemplate> aiw =
FunctionTemplate::New(env->isolate(), NewGetAddrInfoReqWrap);
aiw->InstanceTemplate()->SetInternalFieldCount(1);
aiw->SetClassName(
FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"));
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"),
aiw->GetFunction());
Local<FunctionTemplate> niw =
FunctionTemplate::New(env->isolate(), NewGetNameInfoReqWrap);
niw->InstanceTemplate()->SetInternalFieldCount(1);
niw->SetClassName(
FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"));
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"),
niw->GetFunction());
Local<FunctionTemplate> qrw =
FunctionTemplate::New(env->isolate(), NewQueryReqWrap);
qrw->InstanceTemplate()->SetInternalFieldCount(1);
qrw->SetClassName(
FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"));
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"),
qrw->GetFunction());
}
} // namespace cares_wrap
} // namespace node
NODE_MODULE_CONTEXT_AWARE_BUILTIN(cares_wrap, node::cares_wrap::Initialize)