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