#include "dns_parser.h" #include #include #include #include #include #include #include #include #include #include #ifdef __OpenBSD__ # ifndef ns_t_a # include # endif #endif // __OpenBSD__ namespace ares { class Channel : public node::ObjectWrap { public: Channel(); virtual ~Channel(); static void Initialize(v8::Handle target); private: static v8::Persistent constructor_template; static v8::Handle New(const v8::Arguments& args); static v8::Handle Query(const v8::Arguments& args); static v8::Handle SetServers(const v8::Arguments& args); static v8::Handle Timeout(const v8::Arguments& args); static v8::Handle 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 Channel::constructor_template; static v8::Persistent callback_symbol; static void Initialize(v8::Handle 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 target) { v8::HandleScope scope; v8::Local t = v8::FunctionTemplate::New(Channel::New); constructor_template = v8::Persistent::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 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 options_o = v8::Local::Cast(args[0]); v8::Local 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 Channel::Query(const v8::Arguments& args) { v8::HandleScope scope; Channel *c = node::ObjectWrap::Unwrap(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 Channel::Timeout(const v8::Arguments& args) { v8::HandleScope scope; Channel *c = ObjectWrap::Unwrap(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 Channel::ProcessFD(const v8::Arguments& args) { v8::HandleScope scope; Channel *c = ObjectWrap::Unwrap(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 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 Channel::SetServers(const v8::Arguments& args) { int status; v8::HandleScope scope; Channel *c = node::ObjectWrap::Unwrap(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 a = v8::Local::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(data); v8::HandleScope scope; v8::Local callback_v = c->handle_->Get(callback_symbol); if (!callback_v->IsFunction()) return; v8::Local callback = v8::Local::Cast(callback_v); v8::Local 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 *cb = node::cb_unwrap(arg); v8::Local 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 argv[2] = { v8::Local::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 target) { v8::HandleScope scope; ares::Initialize(target); dns_parser::Initialize(target); }