Browse Source

Initial commit

master
Stefan Bühler 9 years ago
commit
9213b14663
7 changed files with 1147 additions and 0 deletions
  1. +2
    -0
      ares/.gitignore
  2. +369
    -0
      ares/ares.cc
  3. +586
    -0
      ares/dns_parser.cc
  4. +14
    -0
      ares/dns_parser.h
  5. +17
    -0
      ares/wscript
  6. +123
    -0
      dns_native.js
  7. +36
    -0
      main.js

+ 2
- 0
ares/.gitignore View File

@@ -0,0 +1,2 @@
build
.lock-wscript

+ 369
- 0
ares/ares.cc View 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
- 0
ares/dns_parser.cc View 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
- 0
ares/dns_parser.h View 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
- 0
ares/wscript View 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
- 0
dns_native.js View 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
- 0
main.js View 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…
Cancel
Save