Bindings for libuv-integrated c-ares
This commit is contained in:
parent
c953ecfb9e
commit
858f23094e
173
lib/dns_uv.js
Normal file
173
lib/dns_uv.js
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
// 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 cares = process.binding('cares_wrap'),
|
||||||
|
net = require('net'),
|
||||||
|
isIp = net.isIP;
|
||||||
|
|
||||||
|
|
||||||
|
function errnoException(errorno, syscall) {
|
||||||
|
// TODO make this more compatible with ErrnoException from src/node.cc
|
||||||
|
// Once all of Node is using this function the ErrnoException from
|
||||||
|
// src/node.cc should be removed.
|
||||||
|
var e = new Error(syscall + ' ' + errorno);
|
||||||
|
e.errno = errorno;
|
||||||
|
e.syscall = syscall;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function familyToSym(family) {
|
||||||
|
switch (family) {
|
||||||
|
case 4: return cares.AF_INET;
|
||||||
|
case 6: return cares.AF_INET6;
|
||||||
|
default: return cares.AF_UNSPEC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function symToFamily(family) {
|
||||||
|
switch (family) {
|
||||||
|
case cares.AF_INET: return 4;
|
||||||
|
case cares.AF_INET6: return 6;
|
||||||
|
default: return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 = 0;
|
||||||
|
} else if (!family) {
|
||||||
|
family = 0;
|
||||||
|
} else {
|
||||||
|
family = +family;
|
||||||
|
if (family !== 4 && family !== 6) {
|
||||||
|
throw new Error('invalid argument: `family` must be 4 or 6');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!domain) {
|
||||||
|
callback(null, null, family === 6 ? 6 : 4);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchedFamily = net.isIP(domain);
|
||||||
|
if (matchedFamily) {
|
||||||
|
callback(null, domain, matchedFamily);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
return {};
|
||||||
|
} */
|
||||||
|
|
||||||
|
function onanswer(status, addresses, familySym) {
|
||||||
|
if (!status) {
|
||||||
|
callback(null, addresses[0], symToFamily(familySym));
|
||||||
|
} else {
|
||||||
|
callback(errnoException(errno, 'getHostByName'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrap = cares.getHostByName(domain, familyToSym(family), onanswer);
|
||||||
|
if (!wrap) {
|
||||||
|
throw errnoException(errno, 'getHostByName');
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrap;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function resolver(bindingName) {
|
||||||
|
var binding = cares[bindingName];
|
||||||
|
|
||||||
|
return function query(name, callback) {
|
||||||
|
function onanswer(status, result) {
|
||||||
|
if (!status) {
|
||||||
|
callback(null, result);
|
||||||
|
} else {
|
||||||
|
callback(errnoException(errno, bindingName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrap = binding(name, onanswer);
|
||||||
|
if (!wrap) {
|
||||||
|
throw errnoException(errno, bindingName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var resolveMap = {};
|
||||||
|
exports.resolve4 = resolveMap.A = resolver('queryA');
|
||||||
|
exports.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
|
||||||
|
exports.resolveCname = resolveMap.CNAME = resolver('queryCname');
|
||||||
|
exports.resolveMx = resolveMap.MX = resolver('queryMx');
|
||||||
|
exports.resolveNs = resolveMap.NS = resolver('queryNs');
|
||||||
|
exports.resolveTxt = resolveMap.TXT = resolver('queryTxt');
|
||||||
|
exports.resolveSrv = resolveMap.SRV = resolver('querySrv');
|
||||||
|
exports.reverse = resolveMap.PTR = resolver('getHostByAddr');
|
||||||
|
|
||||||
|
|
||||||
|
exports.resolve = function(domain, type_, callback_) {
|
||||||
|
var resolver, callback;
|
||||||
|
if (typeof type_ == 'string') {
|
||||||
|
resolver = resolveMap[type_];
|
||||||
|
callback = callback_;
|
||||||
|
} else {
|
||||||
|
resolver = exports.resolve4;
|
||||||
|
callback = type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof resolver === 'function') {
|
||||||
|
return resolver(domain, callback);
|
||||||
|
} else {
|
||||||
|
throw new Error('Unknown type "' + type + '"');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ERROR CODES
|
||||||
|
exports.BADNAME = 'EBADNAME';
|
||||||
|
exports.BADRESP = 'EBADRESP';
|
||||||
|
exports.CONNREFUSED = 'ECONNREFUSED';
|
||||||
|
exports.DESTRUCTION = 'EDESTRUCTION';
|
||||||
|
exports.REFUSED = 'EREFUSED';
|
||||||
|
exports.FORMERR = 'EFORMERR';
|
||||||
|
exports.NODATA = 'ENODATA';
|
||||||
|
exports.NOMEM = 'ENOMEM';
|
||||||
|
exports.NOTFOUND = 'ENOTFOUND';
|
||||||
|
exports.NOTIMP = 'ENOTIMP';
|
||||||
|
exports.SERVFAIL = 'ESERVFAIL';
|
||||||
|
exports.TIMEOUT = 'ETIMEOUT';
|
601
src/cares_wrap.cc
Normal file
601
src/cares_wrap.cc
Normal file
@ -0,0 +1,601 @@
|
|||||||
|
// 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 <assert.h>
|
||||||
|
#include <node.h>
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__) || defined(__MINGW32__)
|
||||||
|
# include <nameser.h>
|
||||||
|
#else
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Temporary hack: libuv should provide uv_inet_pton and uv_inet_ntop.
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
extern "C" {
|
||||||
|
# include <inet_net_pton.h>
|
||||||
|
# include <inet_ntop.h>
|
||||||
|
}
|
||||||
|
# define uv_inet_pton ares_inet_pton
|
||||||
|
# define uv_inet_ntop ares_inet_ntop
|
||||||
|
|
||||||
|
#else // __POSIX__
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# define uv_inet_pton inet_pton
|
||||||
|
# define uv_inet_ntop inet_ntop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
|
||||||
|
namespace cares_wrap {
|
||||||
|
|
||||||
|
using v8::Arguments;
|
||||||
|
using v8::Array;
|
||||||
|
using v8::Context;
|
||||||
|
using v8::Function;
|
||||||
|
using v8::Handle;
|
||||||
|
using v8::HandleScope;
|
||||||
|
using v8::Integer;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Null;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Persistent;
|
||||||
|
using v8::String;
|
||||||
|
using v8::Value;
|
||||||
|
|
||||||
|
static Persistent<String> onanswer_sym;
|
||||||
|
|
||||||
|
static ares_channel ares_channel;
|
||||||
|
|
||||||
|
|
||||||
|
static Local<Array> HostentToAddresses(struct hostent* host) {
|
||||||
|
HandleScope scope;
|
||||||
|
Local<Array> addresses = Array::New();
|
||||||
|
|
||||||
|
char ip[INET6_ADDRSTRLEN];
|
||||||
|
for (int i = 0; host->h_addr_list[i]; ++i) {
|
||||||
|
uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip));
|
||||||
|
|
||||||
|
Local<String> address = String::New(ip);
|
||||||
|
addresses->Set(Integer::New(i), address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope.Close(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Local<Array> HostentToNames(struct hostent* host) {
|
||||||
|
HandleScope scope;
|
||||||
|
Local<Array> names = Array::New();
|
||||||
|
|
||||||
|
for (int i = 0; host->h_aliases[i]; ++i) {
|
||||||
|
Local<String> address = String::New(host->h_aliases[i]);
|
||||||
|
names->Set(Integer::New(i), address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope.Close(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char* AresErrnoString(int errorno) {
|
||||||
|
switch (errorno) {
|
||||||
|
#define ERRNO_CASE(e) case ARES_##e: return #e;
|
||||||
|
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)
|
||||||
|
#undef ERRNO_CASE
|
||||||
|
default:
|
||||||
|
assert(0 && "Unhandled c-ares error");
|
||||||
|
return "(UNKNOWN)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void SetAresErrno(int errorno) {
|
||||||
|
HandleScope scope;
|
||||||
|
Handle<Value> key = String::NewSymbol("errno");
|
||||||
|
Handle<Value> value = String::NewSymbol(AresErrnoString(errorno));
|
||||||
|
Context::GetCurrent()->Global()->Set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class QueryWrap {
|
||||||
|
public:
|
||||||
|
QueryWrap() {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
object_ = Persistent<Object>::New(Object::New());
|
||||||
|
}
|
||||||
|
|
||||||
|
~QueryWrap() {
|
||||||
|
assert(!object_.IsEmpty());
|
||||||
|
|
||||||
|
object_->DeleteHiddenValue(onanswer_sym);
|
||||||
|
|
||||||
|
object_.Dispose();
|
||||||
|
object_.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Object> GetObject() {
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOnAnswer(Handle<Value> onanswer) {
|
||||||
|
assert(onanswer->IsFunction());
|
||||||
|
object_->SetHiddenValue(onanswer_sym, onanswer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subclasses should implement the appropriate Send method.
|
||||||
|
virtual int Send(const char* name) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int Send(const char* name, int family) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void* GetQueryArg() {
|
||||||
|
return static_cast<void*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Callback(void *arg, int status, int timeouts,
|
||||||
|
unsigned char* answer_buf, int answer_len) {
|
||||||
|
QueryWrap* wrap = reinterpret_cast<QueryWrap*>(arg);
|
||||||
|
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
wrap->ParseError(status);
|
||||||
|
} else {
|
||||||
|
wrap->Parse(answer_buf, answer_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Callback(void *arg, int status, int timeouts,
|
||||||
|
struct hostent* host) {
|
||||||
|
QueryWrap* wrap = reinterpret_cast<QueryWrap*>(arg);
|
||||||
|
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
wrap->ParseError(status);
|
||||||
|
} else {
|
||||||
|
wrap->Parse(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Function> GetOnAnswer() {
|
||||||
|
HandleScope scope;
|
||||||
|
assert(!object_.IsEmpty());
|
||||||
|
Handle<Value> onanswer = object_->GetHiddenValue(onanswer_sym);
|
||||||
|
assert(onanswer->IsFunction());
|
||||||
|
return scope.Close(Handle<Function>::Cast(onanswer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallOnAnswer(Local<Value> answer) {
|
||||||
|
HandleScope scope;
|
||||||
|
Local<Value> argv[2] = { Integer::New(0), answer };
|
||||||
|
GetOnAnswer()->Call(this->object_, 2, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallOnAnswer(Local<Value> answer, Local<Value> family) {
|
||||||
|
HandleScope scope;
|
||||||
|
Local<Value> argv[3] = { Integer::New(0), answer, family };
|
||||||
|
GetOnAnswer()->Call(this->object_, 3, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseError(int status) {
|
||||||
|
assert(status != ARES_SUCCESS);
|
||||||
|
SetAresErrno(status);
|
||||||
|
|
||||||
|
HandleScope scope;
|
||||||
|
Local<Value> argv[1] = { Integer::New(-1) };
|
||||||
|
GetOnAnswer()->Call(this->object_, 1, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subclasses should implement the appropriate Parse method.
|
||||||
|
virtual void Parse(unsigned char* buf, int len) {
|
||||||
|
assert(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void Parse(struct hostent* host) {
|
||||||
|
assert(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Persistent<Object> object_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QueryAWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
ares_query(ares_channel, name, ns_c_in, ns_t_a, Callback, GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(unsigned char* buf, int len) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
struct hostent* host;
|
||||||
|
|
||||||
|
int status = ares_parse_a_reply(buf, len, &host, NULL, NULL);
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
this->ParseError(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Array> addresses = HostentToAddresses(host);
|
||||||
|
ares_free_hostent(host);
|
||||||
|
|
||||||
|
this->CallOnAnswer(addresses);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QueryAaaaWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
ares_query(ares_channel,
|
||||||
|
name,
|
||||||
|
ns_c_in,
|
||||||
|
ns_t_aaaa,
|
||||||
|
Callback,
|
||||||
|
GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(unsigned char* buf, int len) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
struct hostent* host;
|
||||||
|
|
||||||
|
int status = ares_parse_aaaa_reply(buf, len, &host, NULL, NULL);
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
this->ParseError(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Array> addresses = HostentToAddresses(host);
|
||||||
|
ares_free_hostent(host);
|
||||||
|
|
||||||
|
this->CallOnAnswer(addresses);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QueryCnameWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
ares_query(ares_channel,
|
||||||
|
name,
|
||||||
|
ns_c_in,
|
||||||
|
ns_t_cname,
|
||||||
|
Callback,
|
||||||
|
GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(unsigned char* buf, int len) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
struct hostent* host;
|
||||||
|
|
||||||
|
int status = ares_parse_a_reply(buf, len, &host, NULL, NULL);
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
this->ParseError(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A cname lookup always returns a single record but we follow the
|
||||||
|
// common API here.
|
||||||
|
Local<Array> result = Array::New(1);
|
||||||
|
result->Set(0, String::New(host->h_name));
|
||||||
|
ares_free_hostent(host);
|
||||||
|
|
||||||
|
this->CallOnAnswer(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QueryMxWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
ares_query(ares_channel, name, ns_c_in, ns_t_mx, Callback, GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(unsigned char* buf, int len) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
struct ares_mx_reply* mx_start;
|
||||||
|
int status = ares_parse_mx_reply(buf, len, &mx_start);
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
this->ParseError(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Array> mx_records = Array::New();
|
||||||
|
Local<String> exchange_symbol = String::NewSymbol("exchange");
|
||||||
|
Local<String> priority_symbol = String::NewSymbol("priority");
|
||||||
|
int i = 0;
|
||||||
|
for (struct ares_mx_reply* mx_current = mx_start;
|
||||||
|
mx_current;
|
||||||
|
mx_current = mx_current->next) {
|
||||||
|
Local<Object> mx_record = Object::New();
|
||||||
|
mx_record->Set(exchange_symbol, String::New(mx_current->host));
|
||||||
|
mx_record->Set(priority_symbol, Integer::New(mx_current->priority));
|
||||||
|
mx_records->Set(Integer::New(i++), mx_record);
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_free_data(mx_start);
|
||||||
|
|
||||||
|
this->CallOnAnswer(mx_records);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QueryNsWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
ares_query(ares_channel, name, ns_c_in, ns_t_ns, Callback, GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(unsigned char* buf, int len) {
|
||||||
|
struct hostent* host;
|
||||||
|
|
||||||
|
int status = ares_parse_ns_reply(buf, len, &host);
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
this->ParseError(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Array> names = HostentToNames(host);
|
||||||
|
ares_free_hostent(host);
|
||||||
|
|
||||||
|
this->CallOnAnswer(names);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QuerySrvWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
ares_query(ares_channel,
|
||||||
|
name,
|
||||||
|
ns_c_in,
|
||||||
|
ns_t_srv,
|
||||||
|
Callback,
|
||||||
|
GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(unsigned char* buf, int len) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
struct ares_srv_reply* srv_start;
|
||||||
|
int status = ares_parse_srv_reply(buf, len, &srv_start);
|
||||||
|
if (status != ARES_SUCCESS) {
|
||||||
|
this->ParseError(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Array> srv_records = Array::New();
|
||||||
|
Local<String> host_symbol = String::NewSymbol("host");
|
||||||
|
Local<String> port_symbol = String::NewSymbol("port");
|
||||||
|
Local<String> priority_symbol = String::NewSymbol("priority");
|
||||||
|
Local<String> weight_symbol = String::NewSymbol("weight");
|
||||||
|
int i = 0;
|
||||||
|
for (struct ares_srv_reply* srv_current = srv_start;
|
||||||
|
srv_current;
|
||||||
|
srv_current = srv_current->next) {
|
||||||
|
Local<Object> srv_record = Object::New();
|
||||||
|
srv_record->Set(host_symbol, String::New(srv_current->host));
|
||||||
|
srv_record->Set(port_symbol, Integer::New(srv_current->port));
|
||||||
|
srv_record->Set(priority_symbol, Integer::New(srv_current->priority));
|
||||||
|
srv_record->Set(weight_symbol, Integer::New(srv_current->weight));
|
||||||
|
srv_records->Set(Integer::New(i++), srv_record);
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_free_data(srv_start);
|
||||||
|
|
||||||
|
this->CallOnAnswer(srv_records);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class GetHostByAddrWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name) {
|
||||||
|
int length, family;
|
||||||
|
char address_buffer[sizeof(struct in6_addr)];
|
||||||
|
|
||||||
|
if (uv_inet_pton(AF_INET, name, &address_buffer) == 1) {
|
||||||
|
length = sizeof(struct in_addr);
|
||||||
|
family = AF_INET;
|
||||||
|
} else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 1) {
|
||||||
|
length = sizeof(struct in6_addr);
|
||||||
|
family = AF_INET6;
|
||||||
|
} else {
|
||||||
|
return ARES_ENOTIMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ares_gethostbyaddr(ares_channel,
|
||||||
|
address_buffer,
|
||||||
|
length,
|
||||||
|
family,
|
||||||
|
Callback,
|
||||||
|
GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(struct hostent* host) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
this->CallOnAnswer(HostentToNames(host));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class GetHostByNameWrap: public QueryWrap {
|
||||||
|
public:
|
||||||
|
int Send(const char* name, int family) {
|
||||||
|
ares_gethostbyname(ares_channel, name, family, Callback, GetQueryArg());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Parse(struct hostent* host) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Local<Array> addresses = HostentToAddresses(host);
|
||||||
|
Local<Integer> family = Integer::New(host->h_addrtype);
|
||||||
|
|
||||||
|
this->CallOnAnswer(addresses, family);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <class Wrap>
|
||||||
|
static Handle<Value> Query(const Arguments& args) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
assert(!args.IsConstructCall());
|
||||||
|
assert(args.Length() >= 2);
|
||||||
|
assert(args[1]->IsFunction());
|
||||||
|
|
||||||
|
Wrap* wrap = new Wrap();
|
||||||
|
wrap->SetOnAnswer(args[1]);
|
||||||
|
|
||||||
|
// We must cache the wrap's js object here, because cares might make the
|
||||||
|
// callback from the wrap->Send stack. This will destroy the wrap's internal
|
||||||
|
// object reference, causing wrap->GetObject() to return undefined.
|
||||||
|
Local<Object> object = Local<Object>::New(wrap->GetObject());
|
||||||
|
|
||||||
|
String::Utf8Value name(args[0]->ToString());
|
||||||
|
|
||||||
|
int r = wrap->Send(*name);
|
||||||
|
if (r) {
|
||||||
|
SetAresErrno(r);
|
||||||
|
delete wrap;
|
||||||
|
return scope.Close(v8::Null());
|
||||||
|
} else {
|
||||||
|
return scope.Close(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class Wrap>
|
||||||
|
static Handle<Value> QueryWithFamily(const Arguments& args) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
assert(!args.IsConstructCall());
|
||||||
|
assert(args.Length() >= 3);
|
||||||
|
assert(args[2]->IsFunction());
|
||||||
|
|
||||||
|
Wrap* wrap = new Wrap();
|
||||||
|
wrap->SetOnAnswer(args[2]);
|
||||||
|
|
||||||
|
// We must cache the wrap's js object here, because cares might make the
|
||||||
|
// callback from the wrap->Send stack. This will destroy the wrap's internal
|
||||||
|
// object reference, causing wrap->GetObject() to return undefined.
|
||||||
|
Local<Object> object = Local<Object>::New(wrap->GetObject());
|
||||||
|
|
||||||
|
String::Utf8Value name(args[0]->ToString());
|
||||||
|
int family = args[1]->Int32Value();
|
||||||
|
|
||||||
|
int r = wrap->Send(*name, family);
|
||||||
|
if (r) {
|
||||||
|
SetAresErrno(r);
|
||||||
|
delete wrap;
|
||||||
|
return scope.Close(v8::Null());
|
||||||
|
} else {
|
||||||
|
return scope.Close(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Initialize(Handle<Object> target) {
|
||||||
|
HandleScope scope;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = ares_library_init(ARES_LIB_INIT_ALL);
|
||||||
|
assert(r == ARES_SUCCESS);
|
||||||
|
|
||||||
|
struct ares_options options;
|
||||||
|
uv_ares_init_options(&ares_channel, &options, 0);
|
||||||
|
assert(r == 0);
|
||||||
|
|
||||||
|
NODE_SET_METHOD(target, "queryA", Query<QueryAWrap>);
|
||||||
|
NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>);
|
||||||
|
NODE_SET_METHOD(target, "queryCname", Query<QueryCnameWrap>);
|
||||||
|
NODE_SET_METHOD(target, "queryMx", Query<QueryMxWrap>);
|
||||||
|
NODE_SET_METHOD(target, "queryNs", Query<QueryNsWrap>);
|
||||||
|
NODE_SET_METHOD(target, "querySrv", Query<QuerySrvWrap>);
|
||||||
|
NODE_SET_METHOD(target, "getHostByAddr", Query<GetHostByAddrWrap>);
|
||||||
|
NODE_SET_METHOD(target, "getHostByName", QueryWithFamily<GetHostByNameWrap>);
|
||||||
|
|
||||||
|
target->Set(String::NewSymbol("AF_INET"), Integer::New(AF_INET));
|
||||||
|
target->Set(String::NewSymbol("AF_INET6"), Integer::New(AF_INET6));
|
||||||
|
target->Set(String::NewSymbol("AF_UNSPEC"), Integer::New(AF_UNSPEC));
|
||||||
|
|
||||||
|
onanswer_sym = Persistent<String>::New(String::NewSymbol("onanswer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace cares_wrap
|
||||||
|
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
NODE_MODULE(node_cares_wrap, node::cares_wrap::Initialize);
|
@ -390,6 +390,9 @@
|
|||||||
case 'timers':
|
case 'timers':
|
||||||
return process.useUV ? 'timers_uv' : 'timers_legacy';
|
return process.useUV ? 'timers_uv' : 'timers_legacy';
|
||||||
|
|
||||||
|
case 'dns':
|
||||||
|
return process.useUV ? 'dns_uv' : 'dns_legacy';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ NODE_EXT_LIST_ITEM(node_os)
|
|||||||
// libuv rewrite
|
// libuv rewrite
|
||||||
NODE_EXT_LIST_ITEM(node_timer_wrap)
|
NODE_EXT_LIST_ITEM(node_timer_wrap)
|
||||||
NODE_EXT_LIST_ITEM(node_tcp_wrap)
|
NODE_EXT_LIST_ITEM(node_tcp_wrap)
|
||||||
|
NODE_EXT_LIST_ITEM(node_cares_wrap)
|
||||||
|
|
||||||
NODE_EXT_LIST_END
|
NODE_EXT_LIST_END
|
||||||
|
|
||||||
|
370
test/internet/test-dns.js
Normal file
370
test/internet/test-dns.js
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
// 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 assert = require('assert');
|
||||||
|
dns = require('dns'),
|
||||||
|
net = require('net_uv');
|
||||||
|
isIP = net.isIP,
|
||||||
|
isIPv4 = net.isIPv4,
|
||||||
|
isIPv6 = net.isIPv6;
|
||||||
|
|
||||||
|
var expected = 0,
|
||||||
|
completed = 0,
|
||||||
|
running = false,
|
||||||
|
queue = [];
|
||||||
|
|
||||||
|
|
||||||
|
function TEST(f) {
|
||||||
|
function next() {
|
||||||
|
var f = queue.shift();
|
||||||
|
if (f) {
|
||||||
|
running = true;
|
||||||
|
console.log(f.name);
|
||||||
|
f(done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
running = false;
|
||||||
|
completed++;
|
||||||
|
process.nextTick(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
expected++;
|
||||||
|
queue.push(f);
|
||||||
|
|
||||||
|
if (!running) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
process.on('exit', function() {
|
||||||
|
console.log(completed + " tests completed");
|
||||||
|
assert.equal(running, false);
|
||||||
|
assert.strictEqual(expected, completed);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_resolve4(done) {
|
||||||
|
var req = dns.resolve4('www.google.com', function(err, ips) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(ips.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < ips.length; i++) {
|
||||||
|
assert.ok(isIPv4(ips[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_resolve6(done) {
|
||||||
|
var req = dns.resolve6('ipv6.google.com', function(err, ips) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(ips.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < ips.length; i++) {
|
||||||
|
assert.ok(isIPv6(ips[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_reverse_ipv4(done) {
|
||||||
|
var req = dns.reverse('8.8.8.8', function(err, domains) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(domains.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < domains.length; i++) {
|
||||||
|
assert.ok(domains[i]);
|
||||||
|
assert.ok(typeof domains[i] === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_reverse_ipv6(done) {
|
||||||
|
var req = dns.reverse('2001:4860:4860::8888', function(err, domains) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(domains.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < domains.length; i++) {
|
||||||
|
assert.ok(domains[i]);
|
||||||
|
assert.ok(typeof domains[i] === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_reverse_bogus(done) {
|
||||||
|
var error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var req = dns.reverse('bogus ip', function() {
|
||||||
|
assert.ok(false);
|
||||||
|
});
|
||||||
|
} catch(e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.ok(error instanceof Error);
|
||||||
|
assert.strictEqual(error.errno, "ENOTIMP");
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_resolveMx(done) {
|
||||||
|
var req = dns.resolveMx('gmail.com', function(err, result) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(result.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < result.length; i++) {
|
||||||
|
var item = result[i];
|
||||||
|
assert.ok(item);
|
||||||
|
assert.ok(typeof item === 'object');
|
||||||
|
|
||||||
|
assert.ok(item.exchange);
|
||||||
|
assert.ok(typeof item.exchange === 'string');
|
||||||
|
|
||||||
|
assert.ok(typeof item.priority === 'number');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_resolveNs(done) {
|
||||||
|
var req = dns.resolveNs('rackspace.com', function(err, names) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(names.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < names.length; i++) {
|
||||||
|
var name = names[i];
|
||||||
|
assert.ok(name);
|
||||||
|
assert.ok(typeof name === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_resolveSrv(done) {
|
||||||
|
var req = dns.resolveSrv('_jabber._tcp.google.com', function(err, result) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(result.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < result.length; i++) {
|
||||||
|
var item = result[i];
|
||||||
|
assert.ok(item);
|
||||||
|
assert.ok(typeof item === 'object');
|
||||||
|
|
||||||
|
assert.ok(item.host);
|
||||||
|
assert.ok(typeof item.host === 'string');
|
||||||
|
|
||||||
|
assert.ok(typeof item.port === 'number');
|
||||||
|
assert.ok(typeof item.priority === 'number');
|
||||||
|
assert.ok(typeof item.weight === 'number');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_resolveCname(done) {
|
||||||
|
var req = dns.resolveCname('www.google.com', function(err, names) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
assert.ok(names.length > 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < names.length; i++) {
|
||||||
|
var name = names[i];
|
||||||
|
assert.ok(name);
|
||||||
|
assert.ok(typeof name === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_ipv4_explicit(done) {
|
||||||
|
var req = dns.lookup('www.google.com', 4, function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.ok(net.isIPv4(ip));
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_ipv4_implicit(done) {
|
||||||
|
var req = dns.lookup('www.google.com', function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.ok(net.isIPv4(ip));
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_ipv6_explicit(done) {
|
||||||
|
var req = dns.lookup('ipv6.google.com', 6, function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.ok(net.isIPv6(ip));
|
||||||
|
assert.strictEqual(family, 6);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_ipv6_implicit(done) {
|
||||||
|
var req = dns.lookup('ipv6.google.com', function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.ok(net.isIPv6(ip));
|
||||||
|
assert.strictEqual(family, 6);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_failure(done) {
|
||||||
|
var req = dns.lookup('does.not.exist', 4, function(err, ip, family) {
|
||||||
|
assert.ok(err instanceof Error);
|
||||||
|
assert.strictEqual(err.errno, 'ENOTFOUND');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_null(done) {
|
||||||
|
var req = dns.lookup(null, function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(ip, null);
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_ip_ipv4(done) {
|
||||||
|
var req = dns.lookup("127.0.0.1", function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(ip, "127.0.0.1");
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_ip_ipv6(done) {
|
||||||
|
var req = dns.lookup("::1", function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.ok(net.isIPv6(ip));
|
||||||
|
assert.strictEqual(family, 6);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TEST(function test_lookup_localhost_ipv4(done) {
|
||||||
|
var req = dns.lookup("localhost", 4, function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(ip, "127.0.0.1");
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/* Disabled because it appears to be not working on linux. */
|
||||||
|
/* TEST(function test_lookup_localhost_ipv6(done) {
|
||||||
|
var req = dns.lookup("localhost", 6, function(err, ip, family) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.ok(net.isIPv6(ip));
|
||||||
|
assert.strictEqual(family, 6);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(typeof req === 'object');
|
||||||
|
}); */
|
@ -27,16 +27,6 @@ var dns = require('dns');
|
|||||||
|
|
||||||
// Try resolution without callback
|
// Try resolution without callback
|
||||||
|
|
||||||
dns.getHostByName('localhost', function(error, result) {
|
|
||||||
console.dir(result);
|
|
||||||
assert.deepEqual(['127.0.0.1'], result);
|
|
||||||
});
|
|
||||||
|
|
||||||
dns.getHostByName('127.0.0.1', function(error, result) {
|
|
||||||
console.dir(result);
|
|
||||||
assert.deepEqual(['127.0.0.1'], result);
|
|
||||||
});
|
|
||||||
|
|
||||||
dns.lookup(null, function(error, result, addressType) {
|
dns.lookup(null, function(error, result, addressType) {
|
||||||
assert.equal(null, result);
|
assert.equal(null, result);
|
||||||
assert.equal(4, addressType);
|
assert.equal(4, addressType);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user