
commit
9213b14663
7 changed files with 1147 additions and 0 deletions
-
2ares/.gitignore
-
369ares/ares.cc
-
586ares/dns_parser.cc
-
14ares/dns_parser.h
-
17ares/wscript
-
123dns_native.js
-
36main.js
@ -0,0 +1,2 @@ |
|||
build |
|||
.lock-wscript |
@ -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); |
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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_ |
@ -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' |
@ -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; |
@ -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); |
Write
Preview
Loading…
Cancel
Save
Reference in new issue