mirror of https://github.com/lukechilds/node.git
9 changed files with 1 additions and 1186 deletions
@ -1,324 +0,0 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|||
// copy of this software and associated documentation files (the
|
|||
// "Software"), to deal in the Software without restriction, including
|
|||
// without limitation the rights to use, copy, modify, merge, publish,
|
|||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|||
// persons to whom the Software is furnished to do so, subject to the
|
|||
// following conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be included
|
|||
// in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
var dns = process.binding('cares'); |
|||
var net = process.binding('net'); |
|||
var IOWatcher = process.binding('io_watcher').IOWatcher; |
|||
|
|||
|
|||
var watchers = {}; |
|||
var activeWatchers = {}; |
|||
var Timer = process.binding('timer').Timer; |
|||
|
|||
var timer = new Timer(); |
|||
|
|||
timer.ontimeout = function() { |
|||
var sockets = Object.keys(activeWatchers); |
|||
for (var i = 0, l = sockets.length; i < l; i++) { |
|||
var socket = sockets[i]; |
|||
var s = parseInt(socket, 10); |
|||
channel.processFD(watchers[socket].read ? s : dns.SOCKET_BAD, |
|||
watchers[socket].write ? s : dns.SOCKET_BAD); |
|||
} |
|||
updateTimer(); |
|||
}; |
|||
|
|||
|
|||
function updateTimer() { |
|||
timer.stop(); |
|||
|
|||
// Were just checking to see if activeWatchers is empty or not
|
|||
if (0 === Object.keys(activeWatchers).length) return; |
|||
var max = 20000; |
|||
var timeout = channel.timeout(max); |
|||
timer.start(timeout, 0); |
|||
} |
|||
|
|||
|
|||
var channel = new dns.Channel({SOCK_STATE_CB: function(socket, read, write) { |
|||
var watcher, fd; |
|||
|
|||
if (process.platform == 'win32') { |
|||
fd = process.binding('os').openOSHandle(socket); |
|||
} else { |
|||
fd = socket; |
|||
} |
|||
|
|||
if (socket in watchers) { |
|||
watcher = watchers[socket].watcher; |
|||
} else { |
|||
watcher = new IOWatcher(); |
|||
watchers[socket] = { read: read, |
|||
write: write, |
|||
watcher: watcher }; |
|||
|
|||
watcher.callback = function(read, write) { |
|||
channel.processFD(read ? socket : dns.SOCKET_BAD, |
|||
write ? socket : dns.SOCKET_BAD); |
|||
updateTimer(); |
|||
}; |
|||
} |
|||
|
|||
watcher.stop(); |
|||
|
|||
if (!(read || write)) { |
|||
delete activeWatchers[socket]; |
|||
return; |
|||
} else { |
|||
watcher.set(fd, read == 1, write == 1); |
|||
watcher.start(); |
|||
activeWatchers[socket] = watcher; |
|||
} |
|||
|
|||
updateTimer(); |
|||
}}); |
|||
|
|||
// c-ares invokes a callback either synchronously or asynchronously,
|
|||
// but the dns API should always invoke a callback asynchronously.
|
|||
//
|
|||
// This function makes sure that the callback is invoked asynchronously.
|
|||
// It returns a function that invokes the callback within nextTick().
|
|||
//
|
|||
// To avoid invoking unnecessary nextTick(), `immediately` property of
|
|||
// returned function should be set to true after c-ares returned.
|
|||
//
|
|||
// Usage:
|
|||
//
|
|||
// function someAPI(callback) {
|
|||
// callback = makeAsync(callback);
|
|||
// channel.someAPI(..., callback);
|
|||
// callback.immediately = true;
|
|||
// }
|
|||
function makeAsync(callback) { |
|||
if (typeof callback !== 'function') { |
|||
return callback; |
|||
} |
|||
return function asyncCallback() { |
|||
if (asyncCallback.immediately) { |
|||
// The API already returned, we can invoke the callback immediately.
|
|||
callback.apply(null, arguments); |
|||
} else { |
|||
var args = arguments; |
|||
process.nextTick(function() { |
|||
callback.apply(null, args); |
|||
}); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
|
|||
exports.resolve = function(domain, type_, callback_) { |
|||
var type, callback; |
|||
if (typeof(type_) == 'string') { |
|||
type = type_; |
|||
callback = callback_; |
|||
} else { |
|||
type = 'A'; |
|||
callback = arguments[1]; |
|||
} |
|||
|
|||
var resolveFunc = resolveMap[type]; |
|||
|
|||
if (typeof(resolveFunc) == 'function') { |
|||
resolveFunc(domain, callback); |
|||
} else { |
|||
throw new Error('Unknown type "' + type + '"'); |
|||
} |
|||
}; |
|||
|
|||
|
|||
function familyToSym(family) { |
|||
if (family !== dns.AF_INET && family !== dns.AF_INET6) { |
|||
family = (family === 6) ? dns.AF_INET6 : dns.AF_INET; |
|||
} |
|||
return family; |
|||
} |
|||
|
|||
|
|||
exports.getHostByName = function(domain, family/*=4*/, callback) { |
|||
if (typeof family === 'function') { callback = family; family = null; } |
|||
callback = makeAsync(callback); |
|||
channel.getHostByName(domain, familyToSym(family), callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.getHostByAddr = function(address, family/*=4*/, callback) { |
|||
if (typeof family === 'function') { callback = family; family = null; } |
|||
callback = makeAsync(callback); |
|||
channel.getHostByAddr(address, familyToSym(family), callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
// Easy DNS A/AAAA look up
|
|||
// lookup(domain, [family,] callback)
|
|||
exports.lookup = function(domain, family, callback) { |
|||
// parse arguments
|
|||
if (arguments.length === 2) { |
|||
callback = family; |
|||
family = undefined; |
|||
} else if (family && family !== 4 && family !== 6) { |
|||
family = parseInt(family, 10); |
|||
if (family === dns.AF_INET) { |
|||
family = 4; |
|||
} else if (family === dns.AF_INET6) { |
|||
family = 6; |
|||
} else if (family !== 4 && family !== 6) { |
|||
throw new Error('invalid argument: "family" must be 4 or 6'); |
|||
} |
|||
} |
|||
callback = makeAsync(callback); |
|||
|
|||
if (!domain) { |
|||
callback(null, null, family === 6 ? 6 : 4); |
|||
return; |
|||
} |
|||
|
|||
var matchedFamily = net.isIP(domain); |
|||
if (matchedFamily) { |
|||
callback(null, domain, matchedFamily); |
|||
return; |
|||
} |
|||
|
|||
if (/\w\.local\.?$/.test(domain)) { |
|||
// ANNOYING: In the case of mDNS domains use NSS in the thread pool.
|
|||
// I wish c-ares had better support.
|
|||
process.binding('net').getaddrinfo(domain, 4, function(err, domains4) { |
|||
callback(err, domains4[0], 4); |
|||
}); |
|||
callback.immediately = true; |
|||
return; |
|||
} |
|||
|
|||
if (family) { |
|||
// resolve names for explicit address family
|
|||
var af = familyToSym(family); |
|||
channel.getHostByName(domain, af, function(err, domains) { |
|||
if (!err && domains && domains.length) { |
|||
if (family !== net.isIP(domains[0])) { |
|||
callback(new Error('not found'), []); |
|||
} else { |
|||
callback(null, domains[0], family); |
|||
} |
|||
} else { |
|||
callback(err, []); |
|||
} |
|||
}); |
|||
callback.immediately = true; |
|||
return; |
|||
} |
|||
|
|||
// first resolve names for v4 and if that fails, try v6
|
|||
channel.getHostByName(domain, dns.AF_INET, function(err, domains4) { |
|||
if (domains4 && domains4.length) { |
|||
callback(null, domains4[0], 4); |
|||
} else { |
|||
channel.getHostByName(domain, dns.AF_INET6, function(err, domains6) { |
|||
if (domains6 && domains6.length) { |
|||
callback(null, domains6[0], 6); |
|||
} else { |
|||
callback(err, []); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolve4 = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.A, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolve6 = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.AAAA, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolveMx = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.MX, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolveTxt = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.TXT, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolveSrv = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.SRV, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.reverse = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.PTR, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolveNs = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.NS, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
|
|||
exports.resolveCname = function(domain, callback) { |
|||
callback = makeAsync(callback); |
|||
channel.query(domain, dns.CNAME, callback); |
|||
callback.immediately = true; |
|||
}; |
|||
|
|||
var resolveMap = { A: exports.resolve4, |
|||
AAAA: exports.resolve6, |
|||
MX: exports.resolveMx, |
|||
TXT: exports.resolveTxt, |
|||
SRV: exports.resolveSrv, |
|||
PTR: exports.reverse, |
|||
NS: exports.resolveNs, |
|||
CNAME: exports.resolveCname }; |
|||
|
|||
// ERROR CODES
|
|||
exports.NODATA = dns.NODATA; |
|||
exports.FORMERR = dns.FORMERR; |
|||
exports.BADRESP = dns.BADRESP; |
|||
exports.NOTFOUND = dns.NOTFOUND; |
|||
exports.BADNAME = dns.BADNAME; |
|||
exports.TIMEOUT = dns.TIMEOUT; |
|||
exports.CONNREFUSED = dns.CONNREFUSED; |
|||
exports.NOMEM = dns.NOMEM; |
|||
exports.DESTRUCTION = dns.DESTRUCTION; |
|||
exports.NOTIMP = dns.NOTIMP; |
|||
exports.EREFUSED = dns.EREFUSED; |
|||
exports.SERVFAIL = dns.SERVFAIL; |
@ -1,814 +0,0 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|||
// copy of this software and associated documentation files (the
|
|||
// "Software"), to deal in the Software without restriction, including
|
|||
// without limitation the rights to use, copy, modify, merge, publish,
|
|||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|||
// persons to whom the Software is furnished to do so, subject to the
|
|||
// following conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be included
|
|||
// in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
#include <node_cares.h> |
|||
|
|||
#include <node.h> |
|||
#include <v8.h> |
|||
#include <ares.h> |
|||
|
|||
#include <sys/types.h> |
|||
|
|||
#ifdef __POSIX__ |
|||
# include <sys/socket.h> |
|||
# include <netdb.h> |
|||
|
|||
# include <arpa/nameser.h> |
|||
# include <arpa/inet.h> |
|||
#endif |
|||
|
|||
#ifdef __MINGW32__ |
|||
# include <nameser.h> |
|||
#endif |
|||
|
|||
#ifdef __OpenBSD__ |
|||
# ifndef ns_t_a |
|||
# include <nameser.h> |
|||
# endif |
|||
#endif // __OpenBSD__
|
|||
|
|||
/*
|
|||
* HACK to use inet_pton/inet_ntop from c-ares because mingw32 doesn't have it |
|||
* This trick is used in node_net.cc as well |
|||
* TODO fixme |
|||
*/ |
|||
#ifdef __MINGW32__ |
|||
extern "C" { |
|||
# include <inet_net_pton.h> |
|||
# include <inet_ntop.h> |
|||
} |
|||
|
|||
# define inet_pton ares_inet_pton |
|||
# define inet_ntop ares_inet_ntop |
|||
#endif |
|||
|
|||
namespace node { |
|||
|
|||
using namespace v8; |
|||
|
|||
|
|||
class Channel : public ObjectWrap { |
|||
public: |
|||
static void Initialize(Handle<Object> target); |
|||
|
|||
private: |
|||
static Persistent<FunctionTemplate> constructor_template; |
|||
|
|||
static Handle<Value> New(const Arguments& args); |
|||
static Handle<Value> Query(const Arguments& args); |
|||
static Handle<Value> GetHostByName(const Arguments& args); |
|||
static Handle<Value> GetHostByAddr(const Arguments& args); |
|||
static Handle<Value> Timeout(const Arguments& args); |
|||
static Handle<Value> ProcessFD(const Arguments& args); |
|||
|
|||
ares_channel channel; |
|||
|
|||
static void SockStateCb(void *data, ares_socket_t sock, int read, int write); |
|||
static void QueryCb(void *arg, int status, int timeouts, unsigned char* abuf, int alen); |
|||
}; |
|||
|
|||
|
|||
// To be passed to the QueryCb callback when a Query is finished.
|
|||
// Holds a C callback to parse the response and the final JS callback
|
|||
struct QueryArg { |
|||
typedef void (*ParseAnswerCb)(QueryArg*, unsigned char*, int); |
|||
Persistent<Function> js_cb; |
|||
ParseAnswerCb parse_cb; |
|||
|
|||
QueryArg(const Local<Value> &js_cb, ParseAnswerCb parse_cb) |
|||
: js_cb(Persistent<Function>::New(Local<Function>::Cast(js_cb))) |
|||
, parse_cb(parse_cb) {} |
|||
|
|||
~QueryArg() { |
|||
js_cb.Dispose(); |
|||
} |
|||
}; |
|||
|
|||
|
|||
Persistent<FunctionTemplate> Channel::constructor_template; |
|||
static Persistent<String> priority_symbol; |
|||
static Persistent<String> weight_symbol; |
|||
static Persistent<String> port_symbol; |
|||
static Persistent<String> name_symbol; |
|||
static Persistent<String> callback_symbol; |
|||
static Persistent<String> exchange_symbol; |
|||
|
|||
|
|||
void Cares::Initialize(Handle<Object> target) { |
|||
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(String::NewSymbol("SOCKET_BAD"), Integer::New(ARES_SOCKET_BAD)); |
|||
|
|||
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->Set(String::NewSymbol("AF_INET"), Integer::New(AF_INET)); |
|||
target->Set(String::NewSymbol("AF_INET6"), Integer::New(AF_INET6)); |
|||
|
|||
target->Set(String::NewSymbol("A"), Integer::New(ns_t_a)); |
|||
target->Set(String::NewSymbol("AAAA"), Integer::New(ns_t_aaaa)); |
|||
target->Set(String::NewSymbol("MX"), Integer::New(ns_t_mx)); |
|||
target->Set(String::NewSymbol("NS"), Integer::New(ns_t_ns)); |
|||
target->Set(String::NewSymbol("PTR"), Integer::New(ns_t_ptr)); |
|||
target->Set(String::NewSymbol("TXT"), Integer::New(ns_t_txt)); |
|||
target->Set(String::NewSymbol("SRV"), Integer::New(ns_t_srv)); |
|||
target->Set(String::NewSymbol("CNAME"), Integer::New(ns_t_cname)); |
|||
|
|||
target->Set(String::NewSymbol("NODATA"), Integer::New(ARES_ENODATA)); |
|||
target->Set(String::NewSymbol("FORMERR"), Integer::New(ARES_EFORMERR)); |
|||
target->Set(String::NewSymbol("BADRESP"), Integer::New(ARES_EBADRESP)); |
|||
target->Set(String::NewSymbol("NOTFOUND"), Integer::New(ARES_ENOTFOUND)); |
|||
target->Set(String::NewSymbol("BADNAME"), Integer::New(ARES_EBADNAME)); |
|||
target->Set(String::NewSymbol("TIMEOUT"), Integer::New(ARES_ETIMEOUT)); |
|||
target->Set(String::NewSymbol("CONNREFUSED"), Integer::New(ARES_ECONNREFUSED)); |
|||
target->Set(String::NewSymbol("NOMEM"), Integer::New(ARES_ENOMEM)); |
|||
target->Set(String::NewSymbol("DESTRUCTION"), Integer::New(ARES_EDESTRUCTION)); |
|||
|
|||
// Only occur if the ARES_FLAG_NOCHECKRESP flag was specified
|
|||
target->Set(String::NewSymbol("NOTIMP"), Integer::New(ARES_ENOTIMP)); |
|||
target->Set(String::NewSymbol("EREFUSED"), Integer::New(ARES_EREFUSED)); |
|||
target->Set(String::NewSymbol("SERVFAIL"), Integer::New(ARES_ESERVFAIL)); |
|||
|
|||
Channel::Initialize(target); |
|||
} |
|||
|
|||
|
|||
static Local<Array> HostEntToAddresses(struct hostent* hostent) { |
|||
Local<Array> addresses = Array::New(); |
|||
|
|||
|
|||
char ip[INET6_ADDRSTRLEN]; |
|||
for (int i = 0; hostent->h_addr_list[i]; ++i) { |
|||
inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], ip, sizeof(ip)); |
|||
|
|||
Local<String> address = String::New(ip); |
|||
addresses->Set(Integer::New(i), address); |
|||
} |
|||
|
|||
return addresses; |
|||
} |
|||
|
|||
|
|||
static Local<Array> HostEntToNames(struct hostent* hostent) { |
|||
Local<Array> names = Array::New(); |
|||
|
|||
for (int i = 0; hostent->h_aliases[i]; ++i) { |
|||
Local<String> address = String::New(hostent->h_aliases[i]); |
|||
names->Set(Integer::New(i), address); |
|||
} |
|||
|
|||
return names; |
|||
} |
|||
|
|||
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)"; |
|||
} |
|||
} |
|||
|
|||
|
|||
static void ResolveError(Persistent<Function> &cb, int status) { |
|||
HandleScope scope; |
|||
|
|||
Local<String> code = String::NewSymbol(ares_errno_string(status)); |
|||
Local<String> message = String::NewSymbol(ares_strerror(status)); |
|||
|
|||
Local<String> cons1 = String::Concat(code, String::NewSymbol(", ")); |
|||
Local<String> cons2 = String::Concat(cons1, message); |
|||
|
|||
Local<Value> e = Exception::Error(cons2); |
|||
|
|||
Local<Object> obj = e->ToObject(); |
|||
obj->Set(String::NewSymbol("errno"), Integer::New(status)); |
|||
obj->Set(String::NewSymbol("code"), code); |
|||
|
|||
TryCatch try_catch; |
|||
|
|||
cb->Call(v8::Context::GetCurrent()->Global(), 1, &e); |
|||
|
|||
if (try_catch.HasCaught()) { |
|||
FatalException(try_catch); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void HostByNameCb(void *data, |
|||
int status, |
|||
int timeouts, |
|||
struct hostent *hostent) { |
|||
HandleScope scope; |
|||
|
|||
Persistent<Function> *cb = cb_unwrap(data); |
|||
|
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(*cb, status); |
|||
cb_destroy(cb); |
|||
return; |
|||
} |
|||
|
|||
TryCatch try_catch; |
|||
|
|||
Local<Array> addresses = HostEntToAddresses(hostent); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), addresses}; |
|||
|
|||
(*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv); |
|||
|
|||
if (try_catch.HasCaught()) { |
|||
FatalException(try_catch); |
|||
} |
|||
|
|||
cb_destroy(cb); |
|||
} |
|||
|
|||
|
|||
static void HostByAddrCb(void *data, |
|||
int status, |
|||
int timeouts, |
|||
struct hostent *hostent) { |
|||
HandleScope scope; |
|||
|
|||
Persistent<Function> *cb = cb_unwrap(data); |
|||
|
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(*cb, status); |
|||
cb_destroy(cb); |
|||
return; |
|||
} |
|||
|
|||
TryCatch try_catch; |
|||
|
|||
Local<Array> names = HostEntToNames(hostent); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), names }; |
|||
|
|||
(*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv); |
|||
|
|||
if (try_catch.HasCaught()) { |
|||
FatalException(try_catch); |
|||
} |
|||
|
|||
cb_destroy(cb); |
|||
} |
|||
|
|||
|
|||
static void cb_call(Persistent<Function> &cb, int argc, Local<Value> *argv) { |
|||
TryCatch try_catch; |
|||
|
|||
cb->Call(v8::Context::GetCurrent()->Global(), argc, argv); |
|||
|
|||
if (try_catch.HasCaught()) { |
|||
FatalException(try_catch); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerA(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
hostent* host; |
|||
|
|||
int status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
Local<Array> addresses = HostEntToAddresses(host); |
|||
ares_free_hostent(host); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), addresses }; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerAAAA(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
hostent* host; |
|||
|
|||
int status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
Local<Array> addresses = HostEntToAddresses(host); |
|||
ares_free_hostent(host); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), addresses}; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerCNAME(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
hostent* host; |
|||
|
|||
int status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
// a CNAME lookup always returns a single record but
|
|||
// it's probably best to follow the common API here
|
|||
Local<Array> addresses = Array::New(1); |
|||
addresses->Set(0, String::New(host->h_name)); |
|||
ares_free_hostent(host); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), addresses }; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerMX(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
struct ares_mx_reply *mx_out; |
|||
|
|||
int status = ares_parse_mx_reply(abuf, alen, &mx_out); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
Local<Array> mx_records = Array::New(); |
|||
|
|||
struct ares_mx_reply *current = mx_out; |
|||
for (int i = 0; current; ++i, current = current->next) { |
|||
Local<Object> mx = Object::New(); |
|||
|
|||
mx->Set(priority_symbol, Integer::New(current->priority)); |
|||
mx->Set(exchange_symbol, String::New(current->host)); |
|||
|
|||
mx_records->Set(Integer::New(i), mx); |
|||
} |
|||
ares_free_data(mx_out); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), mx_records }; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerNS(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
hostent* host; |
|||
|
|||
int status = ares_parse_ns_reply(abuf, alen, &host); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
Local<Array> names = HostEntToNames(host); |
|||
ares_free_hostent(host); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), names }; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerSRV(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
struct ares_srv_reply *srv_out; |
|||
|
|||
int status = ares_parse_srv_reply(abuf, alen, &srv_out); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
Local<Array> srv_records = Array::New(); |
|||
|
|||
struct ares_srv_reply *current = srv_out; |
|||
for (int i = 0; current; ++i, current = current->next) { |
|||
Local<Object> srv = Object::New(); |
|||
|
|||
srv->Set(priority_symbol, Integer::New(current->priority)); |
|||
srv->Set(weight_symbol, Integer::New(current->weight)); |
|||
srv->Set(port_symbol, Integer::New(current->port)); |
|||
srv->Set(name_symbol, String::New(current->host)); |
|||
|
|||
srv_records->Set(Integer::New(i), srv); |
|||
} |
|||
ares_free_data(srv_out); |
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), srv_records }; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
static void ParseAnswerTXT(QueryArg *arg, unsigned char* abuf, int alen) { |
|||
HandleScope scope; |
|||
|
|||
struct ares_txt_reply *txt_out; |
|||
|
|||
int status = ares_parse_txt_reply(abuf, alen, &txt_out); |
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(arg->js_cb, status); |
|||
return; |
|||
} |
|||
|
|||
Local<Array> txt_records = Array::New(); |
|||
|
|||
struct ares_txt_reply *current = txt_out; |
|||
for (int i = 0; current; ++i, current = current->next) { |
|||
Local<String> txt = String::New(reinterpret_cast<char*>(current->txt)); |
|||
txt_records->Set(Integer::New(i), txt); |
|||
} |
|||
ares_free_data(txt_out); |
|||
|
|||
|
|||
Local<Value> argv[2] = { Local<Value>::New(Null()), txt_records }; |
|||
cb_call(arg->js_cb, 2, argv); |
|||
} |
|||
|
|||
|
|||
void Channel::QueryCb(void *arg, |
|||
int status, |
|||
int timeouts, |
|||
unsigned char* abuf, |
|||
int alen) { |
|||
QueryArg *query_arg = static_cast<QueryArg*>(arg); |
|||
|
|||
HandleScope scope; |
|||
|
|||
if (status != ARES_SUCCESS) { |
|||
ResolveError(query_arg->js_cb, status); |
|||
delete query_arg; |
|||
return; |
|||
} |
|||
|
|||
query_arg->parse_cb(query_arg, abuf, alen); |
|||
|
|||
delete query_arg; |
|||
} |
|||
|
|||
|
|||
void Channel::Initialize(Handle<Object> target) { |
|||
HandleScope scope; |
|||
|
|||
Local<FunctionTemplate> t = FunctionTemplate::New(Channel::New); |
|||
constructor_template = Persistent<FunctionTemplate>::New(t); |
|||
constructor_template->InstanceTemplate()->SetInternalFieldCount(1); |
|||
constructor_template->SetClassName(String::NewSymbol("Channel")); |
|||
|
|||
NODE_SET_PROTOTYPE_METHOD(constructor_template, "getHostByName", Channel::GetHostByName); |
|||
NODE_SET_PROTOTYPE_METHOD(constructor_template, "getHostByAddr", Channel::GetHostByAddr); |
|||
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); |
|||
|
|||
target->Set(String::NewSymbol("Channel"), constructor_template->GetFunction()); |
|||
|
|||
callback_symbol = NODE_PSYMBOL("callback"); |
|||
} |
|||
|
|||
|
|||
Handle<Value> Channel::New(const Arguments& args) { |
|||
if (!args.IsConstructCall()) { |
|||
return FromConstructorTemplate(constructor_template, args); |
|||
} |
|||
|
|||
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 ThrowException(Exception::TypeError( |
|||
String::New("Bad Options Argument"))); |
|||
} |
|||
|
|||
Local<Object> options_o = Local<Object>::Cast(args[0]); |
|||
|
|||
Local<Value> cb = options_o->Get(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(); |
|||
} |
|||
|
|||
|
|||
Handle<Value> Channel::Query(const Arguments& args) { |
|||
HandleScope scope; |
|||
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder()); |
|||
assert(c); |
|||
|
|||
if (!args[0]->IsString()) { |
|||
return ThrowException(Exception::TypeError( |
|||
String::New("First argument must be a name"))); |
|||
} |
|||
|
|||
if (!args[1]->IsInt32()) { |
|||
return ThrowException(Exception::TypeError( |
|||
String::New("Second argument must be a query type"))); |
|||
} |
|||
|
|||
if (!args[2]->IsFunction()) { |
|||
return ThrowException(Exception::TypeError( |
|||
String::New("Third argument must be a callback"))); |
|||
} |
|||
|
|||
String::Utf8Value name(args[0]->ToString()); |
|||
int type = args[1]->Int32Value(); |
|||
QueryArg::ParseAnswerCb parse_cb; |
|||
|
|||
switch(type) { |
|||
case ns_t_a: |
|||
parse_cb = ParseAnswerA; |
|||
break; |
|||
|
|||
case ns_t_aaaa: |
|||
parse_cb = ParseAnswerAAAA; |
|||
break; |
|||
|
|||
case ns_t_mx: |
|||
parse_cb = ParseAnswerMX; |
|||
break; |
|||
|
|||
case ns_t_ns: |
|||
parse_cb = ParseAnswerNS; |
|||
break; |
|||
|
|||
case ns_t_txt: |
|||
parse_cb = ParseAnswerTXT; |
|||
break; |
|||
|
|||
case ns_t_srv: |
|||
parse_cb = ParseAnswerSRV; |
|||
break; |
|||
|
|||
case ns_t_cname: |
|||
parse_cb = ParseAnswerCNAME; |
|||
break; |
|||
|
|||
case ns_t_ptr: |
|||
|
|||
int length, family; |
|||
char address_b[sizeof(struct in6_addr)]; |
|||
|
|||
if (inet_pton(AF_INET, *name, &address_b) == 1) { |
|||
length = sizeof(struct in_addr); |
|||
family = AF_INET; |
|||
} else if (inet_pton(AF_INET6, *name, &address_b) == 1) { |
|||
length = sizeof(struct in6_addr); |
|||
family = AF_INET6; |
|||
} else { |
|||
return ThrowException(Exception::Error(String::New("Invalid IP address"))); |
|||
} |
|||
|
|||
ares_gethostbyaddr(c->channel, address_b, length, family, HostByAddrCb, cb_persist(args[2])); |
|||
|
|||
return Undefined(); |
|||
|
|||
default: |
|||
return ThrowException(Exception::Error( |
|||
String::New("Unsupported query type"))); |
|||
} |
|||
|
|||
QueryArg *query_arg = new QueryArg(args[2], parse_cb); |
|||
|
|||
ares_query(c->channel, *name, ns_c_in, type, QueryCb, query_arg); |
|||
|
|||
return Undefined(); |
|||
} |
|||
|
|||
|
|||
Handle<Value> Channel::GetHostByAddr(const Arguments& args) { |
|||
HandleScope scope; |
|||
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder()); |
|||
assert(c); |
|||
|
|||
if (!args[0]->IsString()) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("First argument must be a address"))); |
|||
} |
|||
|
|||
if (!args[1]->IsInt32()) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Second argument must be an address family"))); |
|||
} |
|||
|
|||
if (!args[2]->IsFunction()) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Third argument must be a callback"))); |
|||
} |
|||
|
|||
int family = args[1]->Int32Value(); |
|||
if (family != AF_INET6 && family != AF_INET) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Unsupported address family"))); |
|||
} |
|||
|
|||
String::Utf8Value address_s(args[0]->ToString()); |
|||
|
|||
char address_b[sizeof(struct in6_addr)]; |
|||
int r = inet_pton(family, *address_s, address_b); |
|||
if (r != 1) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Invalid network address"))); |
|||
} |
|||
|
|||
int length; |
|||
if (family == AF_INET6) |
|||
length = sizeof(struct in6_addr); |
|||
else |
|||
length = sizeof(struct in_addr); |
|||
|
|||
ares_gethostbyaddr(c->channel, address_b, length, family, HostByAddrCb, cb_persist(args[2])); |
|||
|
|||
return Undefined(); |
|||
} |
|||
|
|||
|
|||
|
|||
Handle<Value> Channel::GetHostByName(const Arguments& args) { |
|||
HandleScope scope; |
|||
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder()); |
|||
assert(c); |
|||
|
|||
if (!args[0]->IsString()) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("First argument must be a name"))); |
|||
} |
|||
|
|||
if (!args[1]->IsInt32()) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Second argument must be a family"))); |
|||
} |
|||
|
|||
if (!args[2]->IsFunction()) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Third argument must be a callback"))); |
|||
} |
|||
|
|||
int family = args[1]->Int32Value(); |
|||
if (family != AF_INET6 && family != AF_INET) { |
|||
return ThrowException(Exception::Error( |
|||
String::New("Unsupported address family"))); |
|||
} |
|||
|
|||
String::Utf8Value name(args[0]->ToString()); |
|||
|
|||
ares_gethostbyname(c->channel, *name, family, HostByNameCb, cb_persist(args[2])); |
|||
|
|||
return Undefined(); |
|||
} |
|||
|
|||
|
|||
Handle<Value> Channel::Timeout(const Arguments& args) { |
|||
HandleScope scope; |
|||
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder()); |
|||
assert(c); |
|||
|
|||
if (!args[0]->IsInt32()) { |
|||
return ThrowException(Exception::Error( |
|||
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, &maxtv, &tvbuf); |
|||
|
|||
return scope.Close(Integer::New(ret->tv_sec * 1000 + ret->tv_usec / 1000)); |
|||
} |
|||
|
|||
|
|||
Handle<Value> Channel::ProcessFD(const Arguments& args) { |
|||
HandleScope scope; |
|||
Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder()); |
|||
assert(c); |
|||
|
|||
int read_fd, write_fd; |
|||
|
|||
if (!args[0]->IsInt32()) { |
|||
return ThrowException(Exception::Error( |
|||
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 ThrowException(Exception::Error( |
|||
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 Undefined(); |
|||
} |
|||
|
|||
|
|||
void Channel::SockStateCb(void *data, ares_socket_t sock, int read, int write) { |
|||
Channel *c = static_cast<Channel*>(data); |
|||
HandleScope scope; |
|||
|
|||
Local<Value> callback_v = c->handle_->Get(callback_symbol); |
|||
if (!callback_v->IsFunction()) return; |
|||
Local<Function> callback = Local<Function>::Cast(callback_v); |
|||
|
|||
Local<Value> argv[3]; |
|||
|
|||
argv[0] = Integer::New(sock); |
|||
argv[1] = Integer::New(read); |
|||
argv[2] = Integer::New(write); |
|||
|
|||
TryCatch try_catch; |
|||
|
|||
callback->Call(c->handle_, 3, argv); |
|||
|
|||
if (try_catch.HasCaught()) { |
|||
FatalException(try_catch); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} // namespace node
|
|||
|
|||
NODE_MODULE(node_cares, node::Cares::Initialize); |
@ -1,37 +0,0 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|||
// copy of this software and associated documentation files (the
|
|||
// "Software"), to deal in the Software without restriction, including
|
|||
// without limitation the rights to use, copy, modify, merge, publish,
|
|||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|||
// persons to whom the Software is furnished to do so, subject to the
|
|||
// following conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be included
|
|||
// in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
#ifndef NODE_CARES_H_ |
|||
#define NODE_CARES_H_ |
|||
|
|||
#include <node.h> |
|||
#include <v8.h> |
|||
#include <ares.h> |
|||
|
|||
namespace node { |
|||
|
|||
class Cares { |
|||
public: |
|||
static void Initialize(v8::Handle<v8::Object> target); |
|||
}; |
|||
|
|||
} // namespace node
|
|||
#endif // NODE_CARES_H_
|
Loading…
Reference in new issue