mirror of https://github.com/nodejs/node.git
Add support for MX, TXT, and SRV records in DNS module.
This commit is contained in:
parent
c420c89dbd
commit
cd6397cc45
36
doc/api.txt
36
doc/api.txt
|
@ -1470,22 +1470,50 @@ resolution.addErrback(function (code, msg) {
|
|||
});
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
+dns.resolve(domain, rrtype = 'A')+::
|
||||
|
||||
+dns.resolve4(domain)+::
|
||||
|
||||
Resolves a domain (e.g. +"google.com"+) into an array of IPv4 addresses (e.g.
|
||||
+["74.125.79.104", "74.125.79.105", "74.125.79.106"]+).
|
||||
Resolves a domain (e.g. +"google.com"+) into an array of the record types
|
||||
specified by rrtype. Valid rrtypes are +A+ (IPV4 addresses), +AAAA+ (IPV6
|
||||
addresses), +MX+ (mail exchange records), +TXT+ (text records), +SRV+
|
||||
(SRV records), and +PTR+ (used for reverse IP lookups).
|
||||
This function returns a promise.
|
||||
- on success: returns +addresses, ttl, cname+. +ttl+ (time-to-live) is an integer
|
||||
specifying the number of seconds this result is valid for. +cname+ is the
|
||||
canonical name for the query.
|
||||
The type of each item in +addresses+ is determined by the record type, and
|
||||
described in the documentation for the corresponding lookup methods below.
|
||||
- on error: returns +code, msg+. +code+ is one of the error codes listed
|
||||
below and +msg+ is a string describing the error in English.
|
||||
|
||||
+dns.resolve4(domain)+::
|
||||
|
||||
The same as +dns.resolve()+, but only for IPv4 queries (+A+ records).
|
||||
+addresses+ is an array of IPv4 addresses (e.g. +["74.125.79.104",
|
||||
"74.125.79.105", "74.125.79.106"]+).
|
||||
|
||||
+dns.resolve6(domain)+::
|
||||
|
||||
The same as +dns.resolve4()+ except for IPv6 queries (an +AAAA+ query).
|
||||
|
||||
+dns.resolveMx(domain)+::
|
||||
|
||||
The same as +dns.resolve()+, but only for mail exchange queries (+MX+ records).
|
||||
+addresses+ is an array of MX records, each with a priority and an exchange
|
||||
attribute (e.g. +[{"priority": 10, "exchange": "mx.example.com"},...]+).
|
||||
|
||||
+dns.resolveTxt(domain)+::
|
||||
|
||||
The same as +dns.resolve()+, but only for text queries (+TXT+ records).
|
||||
+addresses+ is an array of the text records available for +domain+ (e.g.,
|
||||
+["v=spf1 ip4:0.0.0.0 ~all"]+).
|
||||
|
||||
+dns.resolveSrv(domain)+::
|
||||
|
||||
The same as +dns.resolve()+, but only for service records (+SRV+ records).
|
||||
+addresses+ is an array of the SRV records available for +domain+. Properties
|
||||
of SRV records are priority, weight, port, and name (e.g., +[{"priority": 10,
|
||||
{"weight": 5, "port": 21223, "name": "service.example.com"}, ...]+).
|
||||
|
||||
+dns.reverse(ip)+::
|
||||
|
||||
Reverse resolves an ip address to an array of domain names.
|
||||
|
|
39
lib/dns.js
39
lib/dns.js
|
@ -10,6 +10,18 @@ function callback (promise) {
|
|||
}
|
||||
}
|
||||
|
||||
exports.resolve = function (domain, type) {
|
||||
type = (type || 'a').toUpperCase();
|
||||
|
||||
var resolveFunc = resolveMap[type];
|
||||
|
||||
if (typeof(resolveFunc) == 'function') {
|
||||
return resolveFunc(domain);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
exports.resolve4 = function (domain) {
|
||||
var promise = new events.Promise();
|
||||
process.dns.resolve4(domain, callback(promise));
|
||||
|
@ -22,6 +34,24 @@ exports.resolve6 = function (domain) {
|
|||
return promise;
|
||||
};
|
||||
|
||||
exports.resolveMx = function (domain) {
|
||||
var promise = new process.Promise();
|
||||
process.dns.resolveMx(domain, callback(promise));
|
||||
return promise;
|
||||
};
|
||||
|
||||
exports.resolveTxt = function (domain) {
|
||||
var promise = new process.Promise();
|
||||
process.dns.resolveTxt(domain, callback(promise));
|
||||
return promise;
|
||||
};
|
||||
|
||||
exports.resolveSrv = function (domain) {
|
||||
var promise = new process.Promise();
|
||||
process.dns.resolveSrv(domain, callback(promise));
|
||||
return promise;
|
||||
}
|
||||
|
||||
exports.reverse = function (ip) {
|
||||
var promise = new events.Promise();
|
||||
process.dns.reverse(ip, callback(promise));
|
||||
|
@ -47,3 +77,12 @@ exports.NOMEM = process.dns.NOMEM;
|
|||
|
||||
// the query is malformed.
|
||||
exports.BADQUERY = process.dns.BADQUERY;
|
||||
|
||||
resolveMap = {
|
||||
'A': exports.resolve4,
|
||||
'AAAA': exports.resolve6,
|
||||
'MX': exports.resolveMx,
|
||||
'TXT': exports.resolveTxt,
|
||||
'SRV': exports.resolveSrv,
|
||||
'PTR': exports.reverse,
|
||||
};
|
||||
|
|
183
src/node_dns.cc
183
src/node_dns.cc
|
@ -20,6 +20,11 @@ static ev_io io_watcher;
|
|||
static ev_timer timer_watcher;
|
||||
|
||||
static Persistent<String> errno_symbol;
|
||||
static Persistent<String> exchange_symbol;
|
||||
static Persistent<String> priority_symbol;
|
||||
static Persistent<String> weight_symbol;
|
||||
static Persistent<String> port_symbol;
|
||||
static Persistent<String> name_symbol;
|
||||
|
||||
static inline Persistent<Function>* cb_persist(const Local<Value> &v) {
|
||||
Persistent<Function> *fn = new Persistent<Function>();
|
||||
|
@ -174,6 +179,145 @@ static void AfterResolveA6(struct dns_ctx *ctx,
|
|||
cb_destroy(cb);
|
||||
}
|
||||
|
||||
static void AfterResolveMX(struct dns_ctx *ctx,
|
||||
struct dns_rr_mx *result,
|
||||
void *data) {
|
||||
assert(ctx == &dns_defctx);
|
||||
|
||||
HandleScope scope;
|
||||
|
||||
Persistent<Function> *cb = cb_unwrap(data);
|
||||
|
||||
if (result == NULL) {
|
||||
ResolveError(cb);
|
||||
cb_destroy(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* canonical name */
|
||||
Local<String> cname = String::New(result->dnsmx_cname);
|
||||
|
||||
/* Time-To-Live (TTL) value */
|
||||
Local<Integer> ttl = Integer::New(result->dnsmx_ttl);
|
||||
|
||||
Local<Array> exchanges = Array::New(result->dnsmx_nrr);
|
||||
for (int i = 0; i < result->dnsmx_nrr; i++) {
|
||||
HandleScope loop_scope;
|
||||
|
||||
Local<Object> exchange = Object::New();
|
||||
|
||||
struct dns_mx *mx = &(result->dnsmx_mx[i]);
|
||||
exchange->Set(exchange_symbol, String::New(mx->name));
|
||||
exchange->Set(priority_symbol, Integer::New(mx->priority));
|
||||
|
||||
exchanges->Set(Integer::New(i), exchange);
|
||||
}
|
||||
|
||||
Local<Value> argv[3] = { exchanges, ttl, cname };
|
||||
|
||||
TryCatch try_catch;
|
||||
|
||||
(*cb)->Call(Context::GetCurrent()->Global(), 3, argv);
|
||||
|
||||
if (try_catch.HasCaught()) {
|
||||
FatalException(try_catch);
|
||||
}
|
||||
|
||||
cb_destroy(cb);
|
||||
}
|
||||
|
||||
static void AfterResolveTXT(struct dns_ctx *ctx,
|
||||
struct dns_rr_txt *result,
|
||||
void *data) {
|
||||
assert(ctx == &dns_defctx);
|
||||
|
||||
HandleScope scope;
|
||||
|
||||
Persistent<Function> *cb = cb_unwrap(data);
|
||||
|
||||
if (result == NULL) {
|
||||
ResolveError(cb);
|
||||
cb_destroy(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* canonical name */
|
||||
Local<String> cname = String::New(result->dnstxt_cname);
|
||||
|
||||
/* Time-To-Live (TTL) value */
|
||||
Local<Integer> ttl = Integer::New(result->dnstxt_ttl);
|
||||
|
||||
Local<Array> records = Array::New(result->dnstxt_nrr);
|
||||
for (int i = 0; i < result->dnstxt_nrr; i++) {
|
||||
HandleScope loop_scope;
|
||||
|
||||
struct dns_txt *record = &(result->dnstxt_txt[i]);
|
||||
const char *txt = (const char *)record->txt;
|
||||
records->Set(Integer::New(i), String::New(txt));
|
||||
}
|
||||
|
||||
Local<Value> argv[3] = { records, ttl, cname };
|
||||
|
||||
TryCatch try_catch;
|
||||
|
||||
(*cb)->Call(Context::GetCurrent()->Global(), 3, argv);
|
||||
|
||||
if (try_catch.HasCaught()) {
|
||||
FatalException(try_catch);
|
||||
}
|
||||
|
||||
cb_destroy(cb);
|
||||
}
|
||||
|
||||
static void AfterResolveSRV(struct dns_ctx *ctx,
|
||||
struct dns_rr_srv *result,
|
||||
void *data) {
|
||||
assert(ctx == &dns_defctx);
|
||||
|
||||
HandleScope scope;
|
||||
|
||||
Persistent<Function> *cb = cb_unwrap(data);
|
||||
|
||||
if (result == NULL) {
|
||||
ResolveError(cb);
|
||||
cb_destroy(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* canonical name */
|
||||
Local<String> cname = String::New(result->dnssrv_cname);
|
||||
|
||||
/* Time-To-Live (TTL) value */
|
||||
Local<Integer> ttl = Integer::New(result->dnssrv_ttl);
|
||||
|
||||
Local<Array> records = Array::New(result->dnssrv_nrr);
|
||||
for (int i = 0; i < result->dnssrv_nrr; i++) {
|
||||
HandleScope loop_scope;
|
||||
|
||||
Local<Object> record = Object::New();
|
||||
|
||||
struct dns_srv *srv = &(result->dnssrv_srv[i]);
|
||||
record->Set(priority_symbol, Integer::New(srv->priority));
|
||||
record->Set(weight_symbol, Integer::New(srv->weight));
|
||||
record->Set(port_symbol, Integer::New(srv->port));
|
||||
record->Set(name_symbol, String::New(srv->name));
|
||||
|
||||
records->Set(Integer::New(i), record);
|
||||
}
|
||||
|
||||
Local<Value> argv[3] = { records, ttl, cname };
|
||||
|
||||
TryCatch try_catch;
|
||||
|
||||
(*cb)->Call(Context::GetCurrent()->Global(), 3, argv);
|
||||
|
||||
if (try_catch.HasCaught()) {
|
||||
FatalException(try_catch);
|
||||
}
|
||||
|
||||
cb_destroy(cb);
|
||||
}
|
||||
|
||||
static Handle<Value> ResolveA(int type, const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
|
@ -194,6 +338,18 @@ static Handle<Value> ResolveA(int type, const Arguments& args) {
|
|||
query = dns_submit_a6(NULL, *name, 0, AfterResolveA6, cb_persist(args[1]));
|
||||
break;
|
||||
|
||||
case DNS_T_MX:
|
||||
query = dns_submit_mx(NULL, *name, 0, AfterResolveMX, cb_persist(args[1]));
|
||||
break;
|
||||
|
||||
case DNS_T_TXT:
|
||||
query = dns_submit_txt(NULL, *name, DNS_C_IN, 0, AfterResolveTXT, cb_persist(args[1]));
|
||||
break;
|
||||
|
||||
case DNS_T_SRV:
|
||||
query = dns_submit_srv(NULL, *name, NULL, NULL, 0, AfterResolveSRV, cb_persist(args[1]));
|
||||
break;
|
||||
|
||||
default:
|
||||
return ThrowException(Exception::Error(String::New("Unsupported type")));
|
||||
}
|
||||
|
@ -213,6 +369,18 @@ static Handle<Value> ResolveA6(const Arguments& args) {
|
|||
return ResolveA(DNS_T_AAAA, args);
|
||||
}
|
||||
|
||||
static Handle<Value> ResolveMX(const Arguments& args) {
|
||||
return ResolveA(DNS_T_MX, args);
|
||||
}
|
||||
|
||||
static Handle<Value> ResolveTXT(const Arguments& args) {
|
||||
return ResolveA(DNS_T_TXT, args);
|
||||
}
|
||||
|
||||
static Handle<Value> ResolveSRV(const Arguments& args) {
|
||||
return ResolveA(DNS_T_SRV, args);
|
||||
}
|
||||
|
||||
static void AfterReverse(struct dns_ctx *ctx,
|
||||
struct dns_rr_ptr *result,
|
||||
void *data) {
|
||||
|
@ -312,6 +480,12 @@ void DNS::Initialize(Handle<Object> target) {
|
|||
|
||||
errno_symbol = NODE_PSYMBOL("errno");
|
||||
|
||||
exchange_symbol = NODE_PSYMBOL("exchange");
|
||||
priority_symbol = NODE_PSYMBOL("priority");
|
||||
weight_symbol = NODE_PSYMBOL("weight");
|
||||
port_symbol = NODE_PSYMBOL("port");
|
||||
name_symbol = NODE_PSYMBOL("name");
|
||||
|
||||
target->Set(String::NewSymbol("TEMPFAIL"), Integer::New(DNS_E_TEMPFAIL));
|
||||
target->Set(String::NewSymbol("PROTOCOL"), Integer::New(DNS_E_PROTOCOL));
|
||||
target->Set(String::NewSymbol("NXDOMAIN"), Integer::New(DNS_E_NXDOMAIN));
|
||||
|
@ -325,6 +499,15 @@ void DNS::Initialize(Handle<Object> target) {
|
|||
Local<FunctionTemplate> resolve6 = FunctionTemplate::New(ResolveA6);
|
||||
target->Set(String::NewSymbol("resolve6"), resolve6->GetFunction());
|
||||
|
||||
Local<FunctionTemplate> resolveMx = FunctionTemplate::New(ResolveMX);
|
||||
target->Set(String::NewSymbol("resolveMx"), resolveMx->GetFunction());
|
||||
|
||||
Local<FunctionTemplate> resolveTxt = FunctionTemplate::New(ResolveTXT);
|
||||
target->Set(String::NewSymbol("resolveTxt"), resolveTxt->GetFunction());
|
||||
|
||||
Local<FunctionTemplate> resolveSrv = FunctionTemplate::New(ResolveSRV);
|
||||
target->Set(String::NewSymbol("resolveSrv"), resolveSrv->GetFunction());
|
||||
|
||||
Local<FunctionTemplate> reverse = FunctionTemplate::New(Reverse);
|
||||
target->Set(String::NewSymbol("reverse"), reverse->GetFunction());
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
process.mixin(require("../common"));
|
||||
var dns = require("dns");
|
||||
|
||||
for (var i = 2; i < process.ARGV.length; i++) {
|
||||
var name = process.ARGV[i]
|
||||
puts("looking up " + name);
|
||||
var resolution = dns.resolve4(name);
|
||||
|
||||
resolution.addCallback(function (addresses, ttl, cname) {
|
||||
puts("addresses: " + JSON.stringify(addresses));
|
||||
puts("ttl: " + JSON.stringify(ttl));
|
||||
puts("cname: " + JSON.stringify(cname));
|
||||
|
||||
for (var i = 0; i < addresses.length; i++) {
|
||||
var a = addresses[i];
|
||||
var reversing = dns.reverse(a);
|
||||
reversing.addCallback( function (domains, ttl, cname) {
|
||||
puts("reverse for " + a + ": " + JSON.stringify(domains));
|
||||
});
|
||||
reversing.addErrback( function (code, msg) {
|
||||
puts("reverse for " + a + " failed: " + msg);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
resolution.addErrback(function (code, msg) {
|
||||
puts("error: " + msg);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
process.mixin(require("./common"));
|
||||
|
||||
var dns = require("dns"),
|
||||
sys = require("sys");
|
||||
|
||||
var hosts = ['example.com', 'example.org',
|
||||
'ietf.org', // AAAA
|
||||
'google.com', // MX, multiple A records
|
||||
'_xmpp-client._tcp.google.com', // SRV
|
||||
'oakalynhall.co.uk']; // Multiple PTR replies
|
||||
|
||||
var records = ['A', 'AAAA', 'MX', 'TXT', 'SRV'];
|
||||
|
||||
var i = hosts.length;
|
||||
while (i--) {
|
||||
|
||||
var j = records.length;
|
||||
while (j--) {
|
||||
var hostCmd = "dig -t " + records[j] + " " + hosts[i] +
|
||||
"| grep '^" + hosts[i] + "\\.\\W.*IN.*" + records[j] + "'" +
|
||||
"| sed -E 's/[[:space:]]+/ /g' | cut -d ' ' -f 5- " +
|
||||
"| sed -e 's/\\.$//'";
|
||||
|
||||
sys.exec(hostCmd).addCallback(checkDnsRecord(hosts[i], records[j]));
|
||||
}
|
||||
}
|
||||
|
||||
function checkDnsRecord(host, record) {
|
||||
var myHost = host,
|
||||
myRecord = record;
|
||||
return function(stdout) {
|
||||
var expected = stdout.substr(0, stdout.length - 1).split("\n");
|
||||
|
||||
var resolution = dns.resolve(myHost, myRecord);
|
||||
|
||||
switch (myRecord) {
|
||||
case "A":
|
||||
case "AAAA":
|
||||
resolution.addCallback(function (result, ttl, cname) {
|
||||
cmpResults(expected, result, ttl, cname);
|
||||
|
||||
// do reverse lookup check
|
||||
var ll = result.length;
|
||||
while (ll--) {
|
||||
var ip = result[ll];
|
||||
|
||||
var reverseCmd = "host " + ip +
|
||||
"| cut -d \" \" -f 5-" +
|
||||
"| sed -e 's/\\.$//'";
|
||||
|
||||
sys.exec(reverseCmd).addCallback(checkReverse(ip));
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "MX":
|
||||
resolution.addCallback(function (result, ttl, cname) {
|
||||
var strResult = [];
|
||||
var ll = result.length;
|
||||
while (ll--) {
|
||||
strResult.push(result[ll].priority + " " + result[ll].exchange);
|
||||
}
|
||||
|
||||
cmpResults(expected, strResult, ttl, cname);
|
||||
});
|
||||
break;
|
||||
case "TXT":
|
||||
resolution.addCallback(function (result, ttl, cname) {
|
||||
var strResult = [];
|
||||
var ll = result.length;
|
||||
while (ll--) {
|
||||
strResult.push('"' + result[ll] + '"');
|
||||
}
|
||||
cmpResults(expected, strResult, ttl, cname);
|
||||
});
|
||||
break;
|
||||
case "SRV":
|
||||
resolution.addCallback(function (result, ttl, cname) {
|
||||
var strResult = [];
|
||||
var ll = result.length;
|
||||
while (ll--) {
|
||||
strResult.push(result[ll].priority + " " +
|
||||
result[ll].weight + " " +
|
||||
result[ll].port + " " +
|
||||
result[ll].name);
|
||||
}
|
||||
cmpResults(expected, strResult, ttl, cname);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkReverse(ip) {
|
||||
var myIp = ip;
|
||||
|
||||
return function (stdout) {
|
||||
var expected = stdout.substr(0, stdout.length - 1).split("\n");
|
||||
|
||||
var reversing = dns.reverse(myIp);
|
||||
|
||||
reversing.addCallback(
|
||||
function (domains, ttl, cname) {
|
||||
cmpResults(expected, domains, ttl, cname);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function cmpResults(expected, result, ttl, cname) {
|
||||
assert.equal(expected.length, result.length);
|
||||
|
||||
expected.sort();
|
||||
result.sort();
|
||||
|
||||
ll = expected.length;
|
||||
while (ll--) {
|
||||
assert.equal(result[ll], expected[ll]);
|
||||
// puts("Result " + result[ll] + " was equal to expected " + expected[ll]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue