Initial commit
This commit is contained in:
commit
9213b14663
2
ares/.gitignore
vendored
Normal file
2
ares/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
build
|
||||||
|
.lock-wscript
|
369
ares/ares.cc
Normal file
369
ares/ares.cc
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
|
||||||
|
#include "dns_parser.h"
|
||||||
|
|
||||||
|
#include <node.h>
|
||||||
|
#include <v8.h>
|
||||||
|
#include <ares.h>
|
||||||
|
#include <ares_dns.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
# ifndef ns_t_a
|
||||||
|
# include <nameser.h>
|
||||||
|
# endif
|
||||||
|
#endif // __OpenBSD__
|
||||||
|
|
||||||
|
namespace ares {
|
||||||
|
|
||||||
|
class Channel : public node::ObjectWrap {
|
||||||
|
public:
|
||||||
|
Channel();
|
||||||
|
virtual ~Channel();
|
||||||
|
|
||||||
|
static void Initialize(v8::Handle<v8::Object> target);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static v8::Persistent<v8::FunctionTemplate> constructor_template;
|
||||||
|
|
||||||
|
static v8::Handle<v8::Value> New(const v8::Arguments& args);
|
||||||
|
static v8::Handle<v8::Value> Query(const v8::Arguments& args);
|
||||||
|
static v8::Handle<v8::Value> SetServers(const v8::Arguments& args);
|
||||||
|
static v8::Handle<v8::Value> Timeout(const v8::Arguments& args);
|
||||||
|
static v8::Handle<v8::Value> ProcessFD(const v8::Arguments& args);
|
||||||
|
|
||||||
|
ares_channel channel;
|
||||||
|
|
||||||
|
static void SockStateCb(void *data, int sock, int read, int write);
|
||||||
|
static void QueryCb(void *arg, int status, int timeouts, unsigned char* abuf, int alen);
|
||||||
|
|
||||||
|
struct ares_addr_node *servers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
v8::Persistent<v8::FunctionTemplate> Channel::constructor_template;
|
||||||
|
|
||||||
|
static v8::Persistent<v8::String> callback_symbol;
|
||||||
|
|
||||||
|
|
||||||
|
static void Initialize(v8::Handle<v8::Object> target) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
int r = ares_library_init(ARES_LIB_INIT_ALL);
|
||||||
|
if (0 != r) {
|
||||||
|
// TODO
|
||||||
|
// ThrowException(Exception::Error(String::New(ares_strerror(r))));
|
||||||
|
assert(r == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
target->Set(v8::String::NewSymbol("SOCKET_BAD"), v8::Integer::New(ARES_SOCKET_BAD));
|
||||||
|
|
||||||
|
target->Set(v8::String::NewSymbol("AF_INET"), v8::Integer::New(AF_INET));
|
||||||
|
target->Set(v8::String::NewSymbol("AF_INET6"), v8::Integer::New(AF_INET6));
|
||||||
|
|
||||||
|
target->Set(v8::String::NewSymbol("NODATA"), v8::Integer::New(ARES_ENODATA));
|
||||||
|
target->Set(v8::String::NewSymbol("FORMERR"), v8::Integer::New(ARES_EFORMERR));
|
||||||
|
target->Set(v8::String::NewSymbol("BADRESP"), v8::Integer::New(ARES_EBADRESP));
|
||||||
|
target->Set(v8::String::NewSymbol("NOTFOUND"), v8::Integer::New(ARES_ENOTFOUND));
|
||||||
|
target->Set(v8::String::NewSymbol("BADNAME"), v8::Integer::New(ARES_EBADNAME));
|
||||||
|
target->Set(v8::String::NewSymbol("TIMEOUT"), v8::Integer::New(ARES_ETIMEOUT));
|
||||||
|
target->Set(v8::String::NewSymbol("CONNREFUSED"), v8::Integer::New(ARES_ECONNREFUSED));
|
||||||
|
target->Set(v8::String::NewSymbol("NOMEM"), v8::Integer::New(ARES_ENOMEM));
|
||||||
|
target->Set(v8::String::NewSymbol("DESTRUCTION"), v8::Integer::New(ARES_EDESTRUCTION));
|
||||||
|
|
||||||
|
// Only occur if the ARES_FLAG_NOCHECKRESP flag was specified
|
||||||
|
target->Set(v8::String::NewSymbol("NOTIMP"), v8::Integer::New(ARES_ENOTIMP));
|
||||||
|
target->Set(v8::String::NewSymbol("EREFUSED"), v8::Integer::New(ARES_EREFUSED));
|
||||||
|
target->Set(v8::String::NewSymbol("SERVFAIL"), v8::Integer::New(ARES_ESERVFAIL));
|
||||||
|
|
||||||
|
Channel::Initialize(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Channel::Initialize(v8::Handle<v8::Object> target) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(Channel::New);
|
||||||
|
constructor_template = v8::Persistent<v8::FunctionTemplate>::New(t);
|
||||||
|
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
|
constructor_template->SetClassName(v8::String::NewSymbol("Channel"));
|
||||||
|
|
||||||
|
using namespace v8;
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Channel::Query);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "timeout", Channel::Timeout);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "processFD", Channel::ProcessFD);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "setServers", Channel::SetServers);
|
||||||
|
|
||||||
|
target->Set(v8::String::NewSymbol("Channel"), constructor_template->GetFunction());
|
||||||
|
|
||||||
|
callback_symbol = NODE_PSYMBOL("callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel::Channel() : servers(0) {
|
||||||
|
}
|
||||||
|
Channel::~Channel() {
|
||||||
|
delete [] servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> Channel::New(const v8::Arguments& args) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
struct ares_options options;
|
||||||
|
int optmask = 0;
|
||||||
|
|
||||||
|
Channel *c = new Channel();
|
||||||
|
c->Wrap(args.This());
|
||||||
|
|
||||||
|
if (args.Length() > 0) {
|
||||||
|
if(!args[0]->IsObject()) {
|
||||||
|
return v8::ThrowException(v8::Exception::TypeError(
|
||||||
|
v8::String::New("Bad Options Argument")));
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Object> options_o = v8::Local<v8::Object>::Cast(args[0]);
|
||||||
|
|
||||||
|
v8::Local<v8::Value> cb = options_o->Get(v8::String::NewSymbol("SOCK_STATE_CB"));
|
||||||
|
if (!cb.IsEmpty()) {
|
||||||
|
c->handle_->Set(callback_symbol, cb);
|
||||||
|
options.sock_state_cb_data = c;
|
||||||
|
options.sock_state_cb = Channel::SockStateCb;
|
||||||
|
optmask |= ARES_OPT_SOCK_STATE_CB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_init_options(&c->channel, &options, optmask);
|
||||||
|
|
||||||
|
return args.This();
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> Channel::Query(const v8::Arguments& args) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
Channel *c = node::ObjectWrap::Unwrap<Channel>(args.Holder());
|
||||||
|
assert(c);
|
||||||
|
|
||||||
|
if (!args[0]->IsString()) {
|
||||||
|
return v8::ThrowException(v8::Exception::TypeError(
|
||||||
|
v8::String::New("First argument must be a name")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args[1]->IsInt32()) {
|
||||||
|
return v8::ThrowException(v8::Exception::TypeError(
|
||||||
|
v8::String::New("Second argument must be a query type!!")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args[2]->IsFunction()) {
|
||||||
|
return v8::ThrowException(v8::Exception::TypeError(
|
||||||
|
v8::String::New("Third argument must be a callback")));
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::String::Utf8Value name(args[0]->ToString());
|
||||||
|
int type = args[1]->Int32Value();
|
||||||
|
|
||||||
|
ares_query(c->channel, *name, ns_c_in, type, QueryCb, node::cb_persist(args[2]));
|
||||||
|
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> Channel::Timeout(const v8::Arguments& args) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder());
|
||||||
|
assert(c);
|
||||||
|
|
||||||
|
if (!args[0]->IsInt32()) {
|
||||||
|
return v8::ThrowException(v8::Exception::Error(
|
||||||
|
v8::String::New("First argument must be an integer number of milliseconds")));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval tvbuf, maxtv, *ret;
|
||||||
|
|
||||||
|
int64_t time = args[0]->IntegerValue();
|
||||||
|
maxtv.tv_sec = time/1000;
|
||||||
|
maxtv.tv_usec = (time % 1000) * 1000;
|
||||||
|
|
||||||
|
ret = ares_timeout(c->channel, (time > 0) ? &maxtv : NULL, &tvbuf);
|
||||||
|
|
||||||
|
return scope.Close(v8::Integer::New(ret ? ret->tv_sec * 1000 + ret->tv_usec / 1000 : -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> Channel::ProcessFD(const v8::Arguments& args) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder());
|
||||||
|
assert(c);
|
||||||
|
|
||||||
|
int read_fd, write_fd;
|
||||||
|
|
||||||
|
if (!args[0]->IsInt32()) {
|
||||||
|
return v8::ThrowException(v8::Exception::Error(
|
||||||
|
v8::String::New("First argument must be a file descriptor or SOCKET_BAD")));
|
||||||
|
}
|
||||||
|
|
||||||
|
read_fd = args[0]->Int32Value();
|
||||||
|
|
||||||
|
if (args.Length() > 1) {
|
||||||
|
if (!args[1]->IsInt32()) {
|
||||||
|
return v8::ThrowException(v8::Exception::Error(
|
||||||
|
v8::String::New("Second argument must be a file descriptor or SOCKET_BAD")));
|
||||||
|
}
|
||||||
|
write_fd = args[1]->Int32Value();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
write_fd = ARES_SOCKET_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_process_fd(c->channel, read_fd, write_fd);
|
||||||
|
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_addr(v8::Handle<v8::Value> v, ares_addr_node &a) {
|
||||||
|
memset(&a, 0, sizeof(a));
|
||||||
|
|
||||||
|
if (!v->IsString()) return false;
|
||||||
|
|
||||||
|
v8::String::Utf8Value s(v->ToString());
|
||||||
|
|
||||||
|
// avoiding buffer overflows in the following strcat
|
||||||
|
// 2001:0db8:85a3:08d3:1319:8a2e:0370:7334
|
||||||
|
// 39 = max ipv6 address.
|
||||||
|
if (s.length() > INET6_ADDRSTRLEN) return false;
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET, *s, &(a.addr.addr4)) > 0) {
|
||||||
|
a.family = AF_INET;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (inet_pton(AF_INET6, *s, &(a.addr.addr6)) > 0) {
|
||||||
|
a.family = AF_INET6;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> Channel::SetServers(const v8::Arguments& args) {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
v8::HandleScope scope;
|
||||||
|
Channel *c = node::ObjectWrap::Unwrap<Channel>(args.Holder());
|
||||||
|
assert(c);
|
||||||
|
|
||||||
|
if (args.Length() == 0) {
|
||||||
|
status = ares_set_servers(c->channel, NULL);
|
||||||
|
if (ARES_SUCCESS != status) return v8::ThrowException(dns_parser::AresException(status));
|
||||||
|
|
||||||
|
delete [] c->servers;
|
||||||
|
c->servers = 0;
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0]->IsString()) {
|
||||||
|
ares_addr_node *servers = new ares_addr_node[args.Length()];
|
||||||
|
for (int i = 0, l = args.Length(); i < l; i++) {
|
||||||
|
if (i > 0) servers[i-1].next = &servers[i];
|
||||||
|
if (!parse_addr(args[i], servers[i])) {
|
||||||
|
return v8::ThrowException(v8::Exception::Error(
|
||||||
|
v8::String::New("Arguments must be strings with valid IP addresses")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status = ares_set_servers(c->channel, servers);
|
||||||
|
if (ARES_SUCCESS != status) return v8::ThrowException(dns_parser::AresException(status));
|
||||||
|
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0]->IsArray()) {
|
||||||
|
v8::Local<v8::Array> a = v8::Local<v8::Array>::Cast(args[0]);
|
||||||
|
|
||||||
|
if (args.Length() == 0) {
|
||||||
|
status = ares_set_servers(c->channel, NULL);
|
||||||
|
if (ARES_SUCCESS != status) return v8::ThrowException(dns_parser::AresException(status));
|
||||||
|
|
||||||
|
delete [] c->servers;
|
||||||
|
c->servers = 0;
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_addr_node *servers = new ares_addr_node[args.Length()];
|
||||||
|
for (int i = 0, l = a->Length(); i < l; i++) {
|
||||||
|
if (i > 0) servers[i-1].next = &servers[i];
|
||||||
|
if (!parse_addr(a->Get(i), servers[i])) {
|
||||||
|
return v8::ThrowException(v8::Exception::Error(
|
||||||
|
v8::String::New("Arguments must be strings with valid IP addresses")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ares_set_servers(c->channel, servers);
|
||||||
|
if (ARES_SUCCESS != status) return v8::ThrowException(dns_parser::AresException(status));
|
||||||
|
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
return v8::ThrowException(v8::Exception::Error(
|
||||||
|
v8::String::New("Arguments must be strings with valid IP addresses")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Channel::SockStateCb(void *data, int sock, int read, int write) {
|
||||||
|
Channel *c = static_cast<Channel*>(data);
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
v8::Local<v8::Value> callback_v = c->handle_->Get(callback_symbol);
|
||||||
|
if (!callback_v->IsFunction()) return;
|
||||||
|
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(callback_v);
|
||||||
|
|
||||||
|
v8::Local<v8::Value> argv[3];
|
||||||
|
|
||||||
|
argv[0] = v8::Integer::New(sock);
|
||||||
|
argv[1] = v8::Integer::New(read);
|
||||||
|
argv[2] = v8::Integer::New(write);
|
||||||
|
|
||||||
|
v8::TryCatch try_catch;
|
||||||
|
|
||||||
|
callback->Call(c->handle_, 3, argv);
|
||||||
|
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
node::FatalException(try_catch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Channel::QueryCb(void *arg, int status, int timeouts, unsigned char* abuf, int alen) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
v8::Persistent<v8::Function> *cb = node::cb_unwrap(arg);
|
||||||
|
v8::Local<v8::Value> result;
|
||||||
|
|
||||||
|
if (status != ARES_SUCCESS && 0 == alen) {
|
||||||
|
result = dns_parser::AresException(status);
|
||||||
|
} else {
|
||||||
|
result = dns_parser::parse_dns_response(abuf, alen, &status);
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::TryCatch try_catch;
|
||||||
|
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
(*cb)->Call(v8::Context::GetCurrent()->Global(), 1, &result);
|
||||||
|
} else {
|
||||||
|
v8::Local<v8::Value> argv[2] = { v8::Local<v8::Value>::New(v8::Null()), result };
|
||||||
|
|
||||||
|
(*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
node::cb_destroy(cb);
|
||||||
|
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
node::FatalException(try_catch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ares
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
init (v8::Handle<v8::Object> target) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
ares::Initialize(target);
|
||||||
|
dns_parser::Initialize(target);
|
||||||
|
}
|
586
ares/dns_parser.cc
Normal file
586
ares/dns_parser.cc
Normal file
@ -0,0 +1,586 @@
|
|||||||
|
#include "dns_parser.h"
|
||||||
|
|
||||||
|
#include <node.h>
|
||||||
|
#include <v8.h>
|
||||||
|
|
||||||
|
#include <ares.h>
|
||||||
|
#include <ares_dns.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
# ifndef ns_t_a
|
||||||
|
# include <nameser.h>
|
||||||
|
# endif
|
||||||
|
#endif // __OpenBSD__
|
||||||
|
|
||||||
|
namespace dns_parser {
|
||||||
|
|
||||||
|
static v8::Persistent<v8::String> question_symbol, answer_symbol, authority_symbol, additional_symbol;
|
||||||
|
static v8::Persistent<v8::String> type_symbol, ntype_symbol, class_symbol, nclass_symbol, ttl_symbol, rdata_symbol, text_symbol, data_symbol;
|
||||||
|
static v8::Persistent<v8::String> priority_symbol, weight_symbol, port_symbol, name_symbol, exchange_symbol, target_symbol;
|
||||||
|
static v8::Persistent<v8::String> mname_symbol, rname_symbol, serial_symbol, refresh_symbol, retry_symbol, expire_symbol, minimum_symbol;
|
||||||
|
|
||||||
|
static v8::Persistent<v8::String> space_symbol;
|
||||||
|
|
||||||
|
typedef int (*ParseRRData)(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r);
|
||||||
|
|
||||||
|
struct RR_TYPE {
|
||||||
|
const char *s;
|
||||||
|
int n;
|
||||||
|
ParseRRData parse_in; /* only parse class IN */
|
||||||
|
v8::Persistent<v8::String> symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RR_CLASS {
|
||||||
|
const char *s;
|
||||||
|
int n;
|
||||||
|
v8::Persistent<v8::String> symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parse_char_string(const unsigned char *&rr_data, int &rr_len, v8::Local<v8::String> &s) {
|
||||||
|
if (rr_len <= 0) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
int len = (unsigned int) rr_data[0];
|
||||||
|
rr_data++; rr_len--;
|
||||||
|
if (len > rr_len) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
s = v8::String::New((const char*) rr_data, len);
|
||||||
|
rr_data += len; rr_len -= len;
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_A(const unsigned char *, int , const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
if (rr_len == sizeof(struct in_addr)) {
|
||||||
|
struct in_addr a;
|
||||||
|
memcpy(&a, rr_data, sizeof(a));
|
||||||
|
|
||||||
|
char ip[INET_ADDRSTRLEN];
|
||||||
|
ip[0] = '\0';
|
||||||
|
::inet_ntop(AF_INET, &a, ip, INET_ADDRSTRLEN);
|
||||||
|
|
||||||
|
v8::Local<v8::String> address = v8::String::New(ip);
|
||||||
|
r->Set(text_symbol, address);
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
return ARES_EBADRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_AAAA(const unsigned char *, int , const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
if (rr_len == sizeof(struct in6_addr)) {
|
||||||
|
struct in6_addr a;
|
||||||
|
memcpy(&a, rr_data, sizeof(a));
|
||||||
|
|
||||||
|
char ip[INET6_ADDRSTRLEN];
|
||||||
|
ip[0] = '\0';
|
||||||
|
::inet_ntop(AF_INET6, &a, ip, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
v8::Local<v8::String> address = v8::String::New(ip);
|
||||||
|
r->Set(text_symbol, address);
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
return ARES_EBADRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_MX(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (rr_len < 2) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
uint16_t priority;
|
||||||
|
memcpy(&priority, rr_data, sizeof(priority));
|
||||||
|
priority = ntohs(priority);
|
||||||
|
rr_data += sizeof(priority); rr_len -= 2;
|
||||||
|
|
||||||
|
char *hostname;
|
||||||
|
long len;
|
||||||
|
status = ares_expand_name(rr_data, abuf, alen, &hostname, &len);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
v8::Local<v8::String> v8_hostname = v8::String::New(hostname);
|
||||||
|
free(hostname);
|
||||||
|
if (len != rr_len) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
v8::Local<v8::Integer> v8_priority = v8::Integer::New(priority);
|
||||||
|
r->Set(priority_symbol, v8_priority);
|
||||||
|
r->Set(exchange_symbol, v8_hostname);
|
||||||
|
|
||||||
|
v8::Local<v8::String> text = v8::String::Concat(v8_priority->ToString(), space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_hostname);
|
||||||
|
r->Set(text_symbol, text);
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_NS(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
char *hostname;
|
||||||
|
long len;
|
||||||
|
status = ares_expand_name(rr_data, abuf, alen, &hostname, &len);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
v8::Local<v8::String> v8_hostname = v8::String::New(hostname);
|
||||||
|
free(hostname);
|
||||||
|
if (len != rr_len) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
r->Set(text_symbol, v8_hostname);
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_SOA(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
int status;
|
||||||
|
long len;
|
||||||
|
|
||||||
|
char *mname; /* primary NS */
|
||||||
|
status = ares_expand_name(rr_data, abuf, alen, &mname, &len);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
v8::Local<v8::String> v8_mname = v8::String::New(mname);
|
||||||
|
free(mname);
|
||||||
|
if (len > rr_len) return ARES_EBADRESP;
|
||||||
|
rr_data += len; rr_len -= len;
|
||||||
|
|
||||||
|
char *rname; /* owner mailbox */
|
||||||
|
status = ares_expand_name(rr_data, abuf, alen, &rname, &len);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
v8::Local<v8::String> v8_rname = v8::String::New(rname);
|
||||||
|
free(rname);
|
||||||
|
if (len > rr_len) return ARES_EBADRESP;
|
||||||
|
rr_data += len; rr_len -= len;
|
||||||
|
|
||||||
|
if (rr_len != 20) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
uint32_t serial, refresh, retry, expire, minimum;
|
||||||
|
memcpy(&serial, rr_data, sizeof(uint32_t)); serial = ntohl(serial); rr_data += sizeof(uint32_t);
|
||||||
|
memcpy(&refresh, rr_data, sizeof(uint32_t)); refresh = ntohl(refresh); rr_data += sizeof(uint32_t);
|
||||||
|
memcpy(&retry, rr_data, sizeof(uint32_t)); retry = ntohl(retry); rr_data += sizeof(uint32_t);
|
||||||
|
memcpy(&expire, rr_data, sizeof(uint32_t)); expire = ntohl(expire); rr_data += sizeof(uint32_t);
|
||||||
|
memcpy(&minimum, rr_data, sizeof(uint32_t)); minimum = ntohl(minimum);
|
||||||
|
|
||||||
|
v8::Local<v8::Integer>
|
||||||
|
v8_serial = v8::Integer:: New(serial),
|
||||||
|
v8_refresh = v8::Integer:: New(refresh),
|
||||||
|
v8_retry = v8::Integer:: New(retry),
|
||||||
|
v8_expire = v8::Integer:: New(expire),
|
||||||
|
v8_minimum = v8::Integer:: New(minimum);
|
||||||
|
|
||||||
|
v8::Local<v8::String> text = v8::String::Concat(v8_mname, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_rname); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_serial->ToString()); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_refresh->ToString()); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_retry->ToString()); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_expire->ToString()); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_minimum->ToString());
|
||||||
|
r->Set(text_symbol, text);
|
||||||
|
|
||||||
|
r->Set(mname_symbol, v8_mname);
|
||||||
|
r->Set(rname_symbol, v8_rname);
|
||||||
|
r->Set(serial_symbol, v8_serial);
|
||||||
|
r->Set(refresh_symbol, v8_refresh);
|
||||||
|
r->Set(retry_symbol, v8_retry);
|
||||||
|
r->Set(expire_symbol, v8_expire);
|
||||||
|
r->Set(minimum_symbol, v8_minimum);
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_TXT(const unsigned char *, int , const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
int status, i;
|
||||||
|
|
||||||
|
if (0 == rr_len) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
v8::Local<v8::Array> list = v8::Array::New();
|
||||||
|
v8::Local<v8::String> s, text = v8::String::New(""), quote = v8::String::NewSymbol("\"");
|
||||||
|
for (i = 0; 0 < rr_len; i++) {
|
||||||
|
status = parse_char_string(rr_data, rr_len, s);
|
||||||
|
if (ARES_SUCCESS != status) return status;
|
||||||
|
|
||||||
|
list->Set(v8::Integer::New(i), s);
|
||||||
|
|
||||||
|
if (i != 0) text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, quote);
|
||||||
|
/* TODO: escaping */
|
||||||
|
text = v8::String::Concat(text, s);
|
||||||
|
text = v8::String::Concat(text, quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->Set(data_symbol, list);
|
||||||
|
r->Set(text_symbol, text);
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseRRData_SRV(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (rr_len < 6) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
uint16_t priority, weight, port;
|
||||||
|
memcpy(&priority, rr_data, sizeof(uint16_t)); priority = ntohl(priority); rr_data += sizeof(uint16_t); rr_len -= sizeof(uint16_t);
|
||||||
|
memcpy(&weight, rr_data, sizeof(uint16_t)); weight = ntohl(weight); rr_data += sizeof(uint16_t); rr_len -= sizeof(uint16_t);
|
||||||
|
memcpy(&port, rr_data, sizeof(uint16_t)); port = ntohl(port); rr_data += sizeof(uint16_t); rr_len -= sizeof(uint16_t);
|
||||||
|
|
||||||
|
char *target;
|
||||||
|
long len;
|
||||||
|
status = ares_expand_name(rr_data, abuf, alen, &target, &len);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
v8::Local<v8::String> v8_target = v8::String::New(target);
|
||||||
|
free(target);
|
||||||
|
if (len != rr_len) return ARES_EBADRESP;
|
||||||
|
|
||||||
|
v8::Local<v8::Integer>
|
||||||
|
v8_priority = v8::Integer:: New(priority),
|
||||||
|
v8_weight = v8::Integer:: New(weight),
|
||||||
|
v8_port = v8::Integer:: New(port);
|
||||||
|
|
||||||
|
r->Set(priority_symbol, v8_priority);
|
||||||
|
r->Set(weight_symbol, v8_weight);
|
||||||
|
r->Set(port_symbol, v8_port);
|
||||||
|
r->Set(target_symbol, v8_target);
|
||||||
|
|
||||||
|
v8::Local<v8::String> text = v8::String::Concat(v8_priority->ToString(), space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_weight->ToString()); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_port->ToString()); text = v8::String::Concat(text, space_symbol);
|
||||||
|
text = v8::String::Concat(text, v8_target);
|
||||||
|
r->Set(text_symbol, text);
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* indented ones are deprecated/experimental */
|
||||||
|
static RR_TYPE rr_types[] = {
|
||||||
|
{ "A", 1, ParseRRData_A, v8::Persistent<v8::String>() },
|
||||||
|
{ "NS", 2, ParseRRData_NS, v8::Persistent<v8::String>() },
|
||||||
|
{ "MD", 3, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MF", 4, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "CNAME", 5, ParseRRData_NS, v8::Persistent<v8::String>() }, /* same RRDATA as NS */
|
||||||
|
{ "SOA", 6, ParseRRData_SOA, v8::Persistent<v8::String>() },
|
||||||
|
{ "MB", 7, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MG", 8, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MR", 9, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "NULL", 10, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "WKS", 11, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "PTR", 12, ParseRRData_NS, v8::Persistent<v8::String>() }, /* same RRDATA as NS */
|
||||||
|
{ "HINFO", 13, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MINFO", 14, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MX", 15, ParseRRData_MX, v8::Persistent<v8::String>() },
|
||||||
|
{ "TXT", 16, ParseRRData_TXT, v8::Persistent<v8::String>() },
|
||||||
|
{ "RP", 17, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "AFSDB", 18, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "SIG", 24, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "KEY", 25, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "AAAA", 28, ParseRRData_AAAA, v8::Persistent<v8::String>() },
|
||||||
|
{ "LOC", 29, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "SRV", 33, ParseRRData_SRV, v8::Persistent<v8::String>() },
|
||||||
|
{ "NAPTR", 35, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "KX", 36, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "CERT", 37, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "DNAME", 39, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "OPT", 41, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "APL", 42, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "DS", 43, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "SSHFP", 44, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "IPSECKEY", 45, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "RRSIG", 46, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "NSEC", 47, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "DNSKEY", 48, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "DHCID", 49, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "NSEC3", 50, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "NSEC3PARAM", 51, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "HIP", 55, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "SPF", 99, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "TKEY", 249, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "TSIG", 250, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "IXFR", 251, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "AXFR", 252, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MAILB", 253, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "MAILA", 254, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "ANY", 255, 0, v8::Persistent<v8::String>() }, /* aka "*" */
|
||||||
|
{ "TA", 32768, 0, v8::Persistent<v8::String>() },
|
||||||
|
{ "DLV", 32769, 0, v8::Persistent<v8::String>() }
|
||||||
|
};
|
||||||
|
|
||||||
|
static RR_CLASS rr_classes[] = {
|
||||||
|
{ "IN", 1, v8::Persistent<v8::String>() }, /* the Internet */
|
||||||
|
{ "CS", 2, v8::Persistent<v8::String>() }, /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
|
||||||
|
{ "CH", 3, v8::Persistent<v8::String>() }, /* the CHAOS class */
|
||||||
|
{ "HS", 4, v8::Persistent<v8::String>() }, /* Hesiod [Dyer 87] */
|
||||||
|
{ "ANY", 255, v8::Persistent<v8::String>() } /* same value as in rr_type, so no name conflict */
|
||||||
|
};
|
||||||
|
|
||||||
|
void Initialize(v8::Handle<v8::Object> target) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
using namespace v8;
|
||||||
|
|
||||||
|
int r = ares_library_init(ARES_LIB_INIT_ALL);
|
||||||
|
if (0 != r) {
|
||||||
|
// TODO
|
||||||
|
// ThrowException(Exception::Error(String::New(ares_strerror(r))));
|
||||||
|
assert(r == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
question_symbol = NODE_PSYMBOL("question");
|
||||||
|
answer_symbol = NODE_PSYMBOL("answer");
|
||||||
|
authority_symbol = NODE_PSYMBOL("authority");
|
||||||
|
additional_symbol = NODE_PSYMBOL("additional");
|
||||||
|
|
||||||
|
type_symbol = NODE_PSYMBOL("type");
|
||||||
|
ntype_symbol = NODE_PSYMBOL("ntype");
|
||||||
|
class_symbol = NODE_PSYMBOL("class");
|
||||||
|
nclass_symbol = NODE_PSYMBOL("nclass");
|
||||||
|
ttl_symbol = NODE_PSYMBOL("ttl");
|
||||||
|
rdata_symbol = NODE_PSYMBOL("rdata");
|
||||||
|
text_symbol = NODE_PSYMBOL("text");
|
||||||
|
data_symbol = NODE_PSYMBOL("data");
|
||||||
|
|
||||||
|
priority_symbol = NODE_PSYMBOL("priority");
|
||||||
|
weight_symbol = NODE_PSYMBOL("weight");
|
||||||
|
port_symbol = NODE_PSYMBOL("port");
|
||||||
|
name_symbol = NODE_PSYMBOL("name");
|
||||||
|
exchange_symbol = NODE_PSYMBOL("exchange");
|
||||||
|
target_symbol = NODE_PSYMBOL("target");
|
||||||
|
|
||||||
|
mname_symbol = NODE_PSYMBOL("mname");
|
||||||
|
rname_symbol = NODE_PSYMBOL("rname");
|
||||||
|
serial_symbol = NODE_PSYMBOL("serial");
|
||||||
|
refresh_symbol = NODE_PSYMBOL("refresh");
|
||||||
|
retry_symbol = NODE_PSYMBOL("retry");
|
||||||
|
expire_symbol = NODE_PSYMBOL("expire");
|
||||||
|
minimum_symbol = NODE_PSYMBOL("minimum");
|
||||||
|
|
||||||
|
space_symbol = NODE_PSYMBOL(" ");;
|
||||||
|
|
||||||
|
v8::Local<v8::Object> types = v8::Object::New();
|
||||||
|
for (unsigned int i = 0; i < sizeof(rr_types)/sizeof(rr_types[0]); i++) {
|
||||||
|
rr_types[i].symbol = NODE_PSYMBOL(rr_types[i].s);
|
||||||
|
target->Set(rr_types[i].symbol, v8::Integer::New(rr_types[i].n));
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < sizeof(rr_classes)/sizeof(rr_classes[0]); i++) {
|
||||||
|
rr_classes[i].symbol = NODE_PSYMBOL(rr_classes[i].s);
|
||||||
|
target->Set(rr_classes[i].symbol, v8::Integer::New(rr_classes[i].n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *ares_errno_string(int errorno) {
|
||||||
|
#define ERRNO_CASE(e) case ARES_##e: return #e;
|
||||||
|
switch (errorno) {
|
||||||
|
ERRNO_CASE(SUCCESS)
|
||||||
|
ERRNO_CASE(ENODATA)
|
||||||
|
ERRNO_CASE(EFORMERR)
|
||||||
|
ERRNO_CASE(ESERVFAIL)
|
||||||
|
ERRNO_CASE(ENOTFOUND)
|
||||||
|
ERRNO_CASE(ENOTIMP)
|
||||||
|
ERRNO_CASE(EREFUSED)
|
||||||
|
ERRNO_CASE(EBADQUERY)
|
||||||
|
ERRNO_CASE(EBADNAME)
|
||||||
|
ERRNO_CASE(EBADFAMILY)
|
||||||
|
ERRNO_CASE(EBADRESP)
|
||||||
|
ERRNO_CASE(ECONNREFUSED)
|
||||||
|
ERRNO_CASE(ETIMEOUT)
|
||||||
|
ERRNO_CASE(EOF)
|
||||||
|
ERRNO_CASE(EFILE)
|
||||||
|
ERRNO_CASE(ENOMEM)
|
||||||
|
ERRNO_CASE(EDESTRUCTION)
|
||||||
|
ERRNO_CASE(EBADSTR)
|
||||||
|
ERRNO_CASE(EBADFLAGS)
|
||||||
|
ERRNO_CASE(ENONAME)
|
||||||
|
ERRNO_CASE(EBADHINTS)
|
||||||
|
ERRNO_CASE(ENOTINITIALIZED)
|
||||||
|
ERRNO_CASE(ELOADIPHLPAPI)
|
||||||
|
ERRNO_CASE(EADDRGETNETWORKPARAMS)
|
||||||
|
ERRNO_CASE(ECANCELLED)
|
||||||
|
default:
|
||||||
|
assert(0 && "Unhandled c-ares errno");
|
||||||
|
return "(UNKNOWN)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> AresException(int status) {
|
||||||
|
v8::Local<v8::String> code = v8::String::NewSymbol(ares_errno_string(status));
|
||||||
|
v8::Local<v8::String> message = v8::String::NewSymbol(ares_strerror(status));
|
||||||
|
|
||||||
|
v8::Local<v8::String> cons1 = v8::String::Concat(code, v8::String::NewSymbol(", "));
|
||||||
|
v8::Local<v8::String> cons2 = v8::String::Concat(cons1, message);
|
||||||
|
|
||||||
|
v8::Local<v8::Value> e = v8::Exception::Error(cons2);
|
||||||
|
|
||||||
|
v8::Local<v8::Object> obj = e->ToObject();
|
||||||
|
obj->Set(v8::String::NewSymbol("errno"), v8::Integer::New(status));
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_type(v8::Local<v8::Object> &r, int rr_type) {
|
||||||
|
r->Set(ntype_symbol, v8::Integer::New(rr_type));
|
||||||
|
for (unsigned int i = 0; i < sizeof(rr_types)/sizeof(rr_types[0]); i++) {
|
||||||
|
if (rr_type == rr_types[i].n) {
|
||||||
|
r->Set(type_symbol, rr_types[i].symbol);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void set_class(v8::Local<v8::Object> &r, int rr_class) {
|
||||||
|
r->Set(nclass_symbol, v8::Integer::New(rr_class));
|
||||||
|
for (unsigned int i = 0; i < sizeof(rr_classes)/sizeof(rr_classes[0]); i++) {
|
||||||
|
if (rr_class == rr_classes[i].n) {
|
||||||
|
r->Set(class_symbol, rr_classes[i].symbol);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_read_questions(const unsigned char *abuf, int alen, const unsigned char * &aptr, int count, v8::Local<v8::Array> list) {
|
||||||
|
int status;
|
||||||
|
long len;
|
||||||
|
|
||||||
|
/* skip each question */
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int rr_type, rr_class;
|
||||||
|
char *rr_name;
|
||||||
|
v8::Local<v8::Object> r = v8::Object::New();
|
||||||
|
|
||||||
|
/* Decode the RR up to the data field. */
|
||||||
|
status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
aptr += len;
|
||||||
|
if (aptr + QFIXEDSZ > abuf + alen) {
|
||||||
|
free(rr_name);
|
||||||
|
return ARES_EBADRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
rr_type = DNS_RR_TYPE(aptr);
|
||||||
|
rr_class = DNS_RR_CLASS(aptr);
|
||||||
|
aptr += QFIXEDSZ;
|
||||||
|
|
||||||
|
if (aptr > abuf + alen) {
|
||||||
|
free(rr_name);
|
||||||
|
return ARES_EBADRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->Set(name_symbol, v8::String::New(rr_name));
|
||||||
|
set_type(r, rr_type);
|
||||||
|
set_class(r, rr_class);
|
||||||
|
free(rr_name);
|
||||||
|
|
||||||
|
list->Set(v8::Integer::New(i), r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_read_answers(const unsigned char *abuf, int alen, const unsigned char * &aptr, int count, v8::Local<v8::Array> list) {
|
||||||
|
int status;
|
||||||
|
long len;
|
||||||
|
|
||||||
|
/* Examine each resource record (RR) in turn. */
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int rr_type, rr_class, rr_len, rr_ttl;
|
||||||
|
char *rr_name;
|
||||||
|
const unsigned char *rr_data;
|
||||||
|
v8::Local<v8::Object> r = v8::Object::New();
|
||||||
|
|
||||||
|
/* Decode the RR up to the data field. */
|
||||||
|
status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
|
||||||
|
if (status != ARES_SUCCESS) break;
|
||||||
|
aptr += len;
|
||||||
|
if (aptr + RRFIXEDSZ > abuf + alen) {
|
||||||
|
free(rr_name);
|
||||||
|
return ARES_EBADRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
rr_type = DNS_RR_TYPE(aptr);
|
||||||
|
rr_class = DNS_RR_CLASS(aptr);
|
||||||
|
rr_ttl = DNS_RR_TTL(aptr);
|
||||||
|
rr_len = DNS_RR_LEN(aptr);
|
||||||
|
aptr += RRFIXEDSZ;
|
||||||
|
rr_data = aptr;
|
||||||
|
aptr += rr_len;
|
||||||
|
|
||||||
|
if (aptr > abuf + alen) {
|
||||||
|
free(rr_name);
|
||||||
|
return ARES_EBADRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->Set(name_symbol, v8::String::New(rr_name));
|
||||||
|
set_class(r, rr_class);
|
||||||
|
r->Set(ttl_symbol, v8::Integer::New(rr_ttl));
|
||||||
|
free(rr_name);
|
||||||
|
|
||||||
|
r->Set(ntype_symbol, v8::Integer::New(rr_type));
|
||||||
|
for (unsigned int t = 0; t < sizeof(rr_types)/sizeof(rr_types[0]); t++) {
|
||||||
|
if (rr_type == rr_types[t].n) {
|
||||||
|
r->Set(type_symbol, rr_types[t].symbol);
|
||||||
|
if (C_IN == rr_class && rr_types[t].parse_in) {
|
||||||
|
status = rr_types[t].parse_in(abuf, alen, rr_data, rr_len, r);
|
||||||
|
if (status != ARES_SUCCESS) return status;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r->Set(rdata_symbol, v8::String::New((const char*) rr_data, rr_len));
|
||||||
|
|
||||||
|
list->Set(v8::Integer::New(i), r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ARES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> parse_dns_response(const unsigned char *abuf, int alen, int *pstatus) {
|
||||||
|
unsigned int qdcount, ancount, nscount, arcount;
|
||||||
|
int status;
|
||||||
|
const unsigned char *aptr;
|
||||||
|
|
||||||
|
if (pstatus) *pstatus = ARES_SUCCESS;
|
||||||
|
|
||||||
|
v8::Local<v8::Array> qd = v8::Array::New(), an = v8::Array::New(), ns = v8::Array::New(), ar = v8::Array::New();
|
||||||
|
v8::Local<v8::Object> msg = v8::Object::New();
|
||||||
|
|
||||||
|
/* Give up if abuf doesn't have room for a header. */
|
||||||
|
if (alen < HFIXEDSZ) {
|
||||||
|
status = ARES_EBADRESP;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetch the question and answer count from the header. */
|
||||||
|
qdcount = DNS_HEADER_QDCOUNT(abuf);
|
||||||
|
ancount = DNS_HEADER_ANCOUNT(abuf);
|
||||||
|
nscount = DNS_HEADER_NSCOUNT(abuf);
|
||||||
|
arcount = DNS_HEADER_ARCOUNT(abuf);
|
||||||
|
if (qdcount != 1) {
|
||||||
|
status = ARES_EBADRESP;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
aptr = abuf + HFIXEDSZ;
|
||||||
|
|
||||||
|
status = parse_read_questions(abuf, alen, aptr, qdcount, qd);
|
||||||
|
if (status != ARES_SUCCESS) goto error;
|
||||||
|
|
||||||
|
status = parse_read_answers(abuf, alen, aptr, ancount, an);
|
||||||
|
if (status != ARES_SUCCESS) goto error;
|
||||||
|
|
||||||
|
status = parse_read_answers(abuf, alen, aptr, nscount, ns);
|
||||||
|
if (status != ARES_SUCCESS) goto error;
|
||||||
|
|
||||||
|
status = parse_read_answers(abuf, alen, aptr, arcount, ar);
|
||||||
|
if (status != ARES_SUCCESS) goto error;
|
||||||
|
|
||||||
|
msg->Set(question_symbol, qd);
|
||||||
|
msg->Set(answer_symbol, an);
|
||||||
|
msg->Set(authority_symbol, ns);
|
||||||
|
msg->Set(additional_symbol, ar);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (pstatus) *pstatus = status;
|
||||||
|
return AresException(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
ares/dns_parser.h
Normal file
14
ares/dns_parser.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef DNS_PARSER_H_
|
||||||
|
#define DNS_PARSER_H_
|
||||||
|
|
||||||
|
#include <v8.h>
|
||||||
|
|
||||||
|
namespace dns_parser {
|
||||||
|
|
||||||
|
void Initialize(v8::Handle<v8::Object> target);
|
||||||
|
|
||||||
|
v8::Local<v8::Value> parse_dns_response(const unsigned char *abuf, int alen, int *status);
|
||||||
|
v8::Local<v8::Value> AresException(int status);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // DNS_PARSER_H_
|
17
ares/wscript
Normal file
17
ares/wscript
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
srcdir = '.'
|
||||||
|
blddir = 'build'
|
||||||
|
VERSION = '0.0.1'
|
||||||
|
|
||||||
|
def set_options(opt):
|
||||||
|
opt.tool_options('compiler_cxx')
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
conf.check_tool('compiler_cxx')
|
||||||
|
conf.check_tool('node_addon')
|
||||||
|
|
||||||
|
conf.env['CXXFLAGS'] += [ '-O2', '-g', '-Wall', '-Wshadow', '-W' ]
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
|
||||||
|
obj.target = 'ares'
|
||||||
|
obj.source = 'ares.cc dns_parser.cc'
|
123
dns_native.js
Normal file
123
dns_native.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
var ares = require('./ares/build/default/ares');
|
||||||
|
|
||||||
|
|
||||||
|
function rec_toString() {
|
||||||
|
var out = "";
|
||||||
|
|
||||||
|
console.log(this);
|
||||||
|
|
||||||
|
out += ";; Question Section:\n";
|
||||||
|
for (var i = 0, len = this.question.length; i < len; i++) {
|
||||||
|
var q = this.question[i];
|
||||||
|
out += q.name + "\t" + (q.class || q.nclass) + "\t" + (q.type || q.ntype) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out += ";; Answer Section:\n";
|
||||||
|
for (var i = 0, len = this.answer.length; i < len; i++) {
|
||||||
|
var r = this.answer[i];
|
||||||
|
out += r.name + "\t" + r.ttl + "\t" + (r.class || r.nclass) + "\t" + (r.type || r.ntype) + "\t" + (r.text || '<unkown type>') + "\n";
|
||||||
|
}
|
||||||
|
out += ";; Authority Section:\n";
|
||||||
|
for (var i = 0, len = this.authority.length; i < len; i++) {
|
||||||
|
var r = this.authority[i];
|
||||||
|
out += r.name + "\t" + r.ttl + "\t" + (r.class || r.nclass) + "\t" + (r.type || r.ntype) + "\t" + (r.text || '<unkown type>') + "\n";
|
||||||
|
}
|
||||||
|
out += ";; Additional Section:\n";
|
||||||
|
for (var i = 0, len = this.additional.length; i < len; i++) {
|
||||||
|
var r = this.additional[i];
|
||||||
|
out += r.name + "\t" + r.ttl + "\t" + (r.class || r.nclass) + "\t" + (r.type || r.ntype) + "\t" + (r.text || '<unkown type>') + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newChannel(servers) {
|
||||||
|
var channel;
|
||||||
|
var watchers = {};
|
||||||
|
var activeWatchers = {};
|
||||||
|
var timer = new process.Timer();
|
||||||
|
|
||||||
|
timer.callback = function () {
|
||||||
|
channel.processFD(ares.SOCKET_BAD, ares.SOCKET_BAD);
|
||||||
|
updateTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTimer() {
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
// Were just checking to see if activeWatchers is empty or not
|
||||||
|
for (var socket in activeWatchers) {
|
||||||
|
if (activeWatchers.hasOwnProperty(socket)) {
|
||||||
|
var timeout = channel.timeout(-1);
|
||||||
|
|
||||||
|
timer.start(timeout, 0);
|
||||||
|
// Short circuit the loop on first find.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel = new ares.Channel({SOCK_STATE_CB: function (socket, read, write) {
|
||||||
|
var watcher;
|
||||||
|
|
||||||
|
if (socket in watchers) {
|
||||||
|
watcher = watchers[socket].watcher;
|
||||||
|
} else {
|
||||||
|
watcher = new process.IOWatcher();
|
||||||
|
watchers[socket] = { read: read, write: write, watcher: watcher };
|
||||||
|
|
||||||
|
watcher.callback = function(read, write) {
|
||||||
|
channel.processFD(read ? socket : ares.SOCKET_BAD, write ? socket : ares.SOCKET_BAD);
|
||||||
|
updateTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher.stop();
|
||||||
|
|
||||||
|
if (!(read || write)) {
|
||||||
|
delete activeWatchers[socket];
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
watcher.set(socket, read == 1, write == 1);
|
||||||
|
watcher.start();
|
||||||
|
activeWatchers[socket] = watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimer();
|
||||||
|
}});
|
||||||
|
if (servers) {
|
||||||
|
channel.setServers(servers);
|
||||||
|
}
|
||||||
|
/* wrapper mapping type strings to numerical values */
|
||||||
|
channel._query = channel.query;
|
||||||
|
channel.query = function(domain, type, callback) {
|
||||||
|
channel._query(domain, ares[type], function(err, result) {
|
||||||
|
if (result) result.toString = rec_toString;
|
||||||
|
// console.log(result);
|
||||||
|
callback(err, result);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
};
|
||||||
|
|
||||||
|
var channel = newChannel();
|
||||||
|
|
||||||
|
exports.query = channel.query;
|
||||||
|
|
||||||
|
exports.newChannel = newChannel;
|
||||||
|
|
||||||
|
// ERROR CODES
|
||||||
|
exports.NODATA = ares.NODATA;
|
||||||
|
exports.FORMERR = ares.FORMERR;
|
||||||
|
exports.BADRESP = ares.BADRESP;
|
||||||
|
exports.NOTFOUND = ares.NOTFOUND;
|
||||||
|
exports.BADNAME = ares.BADNAME;
|
||||||
|
exports.TIMEOUT = ares.TIMEOUT;
|
||||||
|
exports.CONNREFUSED = ares.CONNREFUSED;
|
||||||
|
exports.NOMEM = ares.NOMEM;
|
||||||
|
exports.DESTRUCTION = ares.DESTRUCTION;
|
||||||
|
|
||||||
|
exports.NOTIMP = ares.NOTIMP;
|
||||||
|
exports.EREFUSED = ares.EREFUSED;
|
||||||
|
exports.SERVFAIL = ares.SERVFAIL;
|
36
main.js
Normal file
36
main.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
var http = require('http');
|
||||||
|
var dns = require('./dns_native');
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
http.createServer(function (req, res) {
|
||||||
|
var parts, type, domain, ns;
|
||||||
|
|
||||||
|
parts = url.parse(req.url).pathname.slice(1).split("/");
|
||||||
|
|
||||||
|
if (parts.length > 2) {
|
||||||
|
type = parts[0];
|
||||||
|
domain = parts[1];
|
||||||
|
ns = [ parts[2] ];
|
||||||
|
} else if (parts.length > 1) {
|
||||||
|
type = parts[0];
|
||||||
|
domain = parts[1];
|
||||||
|
} else if (parts.length > 0) {
|
||||||
|
type = 'A';
|
||||||
|
domain = parts[0];
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, {'Content-Type': 'text/plain'});
|
||||||
|
res.end("Wrong request\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
|
|
||||||
|
c = dns.newChannel(ns);
|
||||||
|
c.query(domain, type, function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
res.end("Error resolving '" + type + "/" + domain + "': " + err.message + "\n");
|
||||||
|
} else {
|
||||||
|
res.end(domain + ":\n" + response.toString() + "\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).listen(4000);
|
Loading…
Reference in New Issue
Block a user