dns: add resolveAny support
`dns.resolveAny` and `dns.resolve` with `"ANY"` has the similar behavior like `$ dig <domain> any` and returns an array with several types of records. `dns.resolveAny` parses the result packet by several rules in turn. Supported types: * A * AAAA * CNAME * MX * NAPTR * NS * PTR * SOA * SRV * TXT Fixes: https://github.com/nodejs/node/issues/2848 PR-URL: https://github.com/nodejs/node/pull/13137 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Roman Reiss <me@silverwind.io>
This commit is contained in:
parent
c4a61b3ee5
commit
27de36926b
@ -197,6 +197,7 @@ records. The type and structure of individual results varies based on `rrtype`:
|
||||
| `'SOA'` | start of authority records | {Object} | [`dns.resolveSoa()`][] |
|
||||
| `'SRV'` | service records | {Object} | [`dns.resolveSrv()`][] |
|
||||
| `'TXT'` | text records | {string} | [`dns.resolveTxt()`][] |
|
||||
| `'ANY'` | any records | {Object} | [`dns.resolveAny()`][] |
|
||||
|
||||
On error, `err` is an [`Error`][] object, where `err.code` is one of the
|
||||
[DNS error codes](#dns_error_codes).
|
||||
@ -417,6 +418,51 @@ is a two-dimensional array of the text records available for `hostname` (e.g.,
|
||||
one record. Depending on the use case, these could be either joined together or
|
||||
treated separately.
|
||||
|
||||
## dns.resolveAny(hostname, callback)
|
||||
|
||||
- `hostname` {string}
|
||||
- `callback` {Function}
|
||||
- `err` {Error}
|
||||
- `ret` {Object[][]}
|
||||
|
||||
Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query).
|
||||
The `ret` argument passed to the `callback` function will be an array containing
|
||||
various types of records. Each object has a property `type` that indicates the
|
||||
type of the current record. And depending on the `type`, additional properties
|
||||
will be present on the object:
|
||||
|
||||
| Type | Properties |
|
||||
|------|------------|
|
||||
| `"A"` | `address` / `ttl` |
|
||||
| `"AAAA"` | `address` / `ttl` |
|
||||
| `"CNAME"` | `value` |
|
||||
| `"MX"` | Refer to [`dns.resolveMx()`][] |
|
||||
| `"NAPTR"` | Refer to [`dns.resolveNaptr()`][] |
|
||||
| `"NS"` | `value` |
|
||||
| `"PTR"` | `value` |
|
||||
| `"SOA"` | Refer to [`dns.resolveSoa()`][] |
|
||||
| `"SRV"` | Refer to [`dns.resolveSrv()`][] |
|
||||
| `"TXT"` | This type of record contains an array property called `entries` which refers to [`dns.resolveTxt()`][], eg. `{ entries: ['...'], type: 'TXT' }` |
|
||||
|
||||
Here is a example of the `ret` object passed to the callback:
|
||||
|
||||
<!-- eslint-disable -->
|
||||
```js
|
||||
[ { type: 'A', address: '127.0.0.1', ttl: 299 },
|
||||
{ type: 'CNAME', value: 'example.com' },
|
||||
{ type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
|
||||
{ type: 'NS', value: 'ns1.example.com', type: 'NS' },
|
||||
{ type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
|
||||
{ type: 'SOA',
|
||||
nsname: 'ns1.example.com',
|
||||
hostmaster: 'admin.example.com',
|
||||
serial: 156696742,
|
||||
refresh: 900,
|
||||
retry: 900,
|
||||
expire: 1800,
|
||||
minttl: 60 } ]
|
||||
```
|
||||
|
||||
## dns.reverse(ip, callback)
|
||||
<!-- YAML
|
||||
added: v0.1.16
|
||||
@ -531,6 +577,7 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
|
||||
[`dns.resolveSoa()`]: #dns_dns_resolvesoa_hostname_callback
|
||||
[`dns.resolveSrv()`]: #dns_dns_resolvesrv_hostname_callback
|
||||
[`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback
|
||||
[`dns.resolveAny()`]: #dns_dns_resolveany_hostname_callback
|
||||
[DNS error codes]: #dns_error_codes
|
||||
[Implementation considerations section]: #dns_implementation_considerations
|
||||
[supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags
|
||||
|
@ -268,6 +268,7 @@ function resolver(bindingName) {
|
||||
|
||||
|
||||
var resolveMap = Object.create(null);
|
||||
resolveMap.ANY = resolver('queryAny');
|
||||
resolveMap.A = resolver('queryA');
|
||||
resolveMap.AAAA = resolver('queryAaaa');
|
||||
resolveMap.CNAME = resolver('queryCname');
|
||||
@ -349,6 +350,7 @@ module.exports = {
|
||||
getServers,
|
||||
setServers,
|
||||
resolve,
|
||||
resolveAny: resolveMap.ANY,
|
||||
resolve4: resolveMap.A,
|
||||
resolve6: resolveMap.AAAA,
|
||||
resolveCname: resolveMap.CNAME,
|
||||
|
File diff suppressed because it is too large
Load Diff
13
src/env.h
13
src/env.h
@ -105,6 +105,16 @@ namespace node {
|
||||
V(dest_string, "dest") \
|
||||
V(detached_string, "detached") \
|
||||
V(disposed_string, "_disposed") \
|
||||
V(dns_a_string, "A") \
|
||||
V(dns_aaaa_string, "AAAA") \
|
||||
V(dns_cname_string, "CNAME") \
|
||||
V(dns_mx_string, "MX") \
|
||||
V(dns_naptr_string, "NAPTR") \
|
||||
V(dns_ns_string, "NS") \
|
||||
V(dns_ptr_string, "PTR") \
|
||||
V(dns_soa_string, "SOA") \
|
||||
V(dns_srv_string, "SRV") \
|
||||
V(dns_txt_string, "TXT") \
|
||||
V(domain_string, "domain") \
|
||||
V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \
|
||||
V(exchange_string, "exchange") \
|
||||
@ -113,6 +123,7 @@ namespace node {
|
||||
V(irq_string, "irq") \
|
||||
V(encoding_string, "encoding") \
|
||||
V(enter_string, "enter") \
|
||||
V(entries_string, "entries") \
|
||||
V(env_pairs_string, "envPairs") \
|
||||
V(errno_string, "errno") \
|
||||
V(error_string, "error") \
|
||||
@ -151,6 +162,7 @@ namespace node {
|
||||
V(issuer_string, "issuer") \
|
||||
V(issuercert_string, "issuerCertificate") \
|
||||
V(kill_signal_string, "killSignal") \
|
||||
V(length_string, "length") \
|
||||
V(mac_string, "mac") \
|
||||
V(max_buffer_string, "maxBuffer") \
|
||||
V(message_string, "message") \
|
||||
@ -231,6 +243,7 @@ namespace node {
|
||||
V(timeout_string, "timeout") \
|
||||
V(times_string, "times") \
|
||||
V(tls_ticket_string, "tlsTicket") \
|
||||
V(ttl_string, "ttl") \
|
||||
V(type_string, "type") \
|
||||
V(uid_string, "uid") \
|
||||
V(unknown_string, "<unknown>") \
|
||||
|
198
test/internet/test-dns-any.js
Normal file
198
test/internet/test-dns-any.js
Normal file
@ -0,0 +1,198 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const dns = require('dns');
|
||||
const net = require('net');
|
||||
|
||||
let running = false;
|
||||
const queue = [];
|
||||
|
||||
const isIPv4 = net.isIPv4;
|
||||
const isIPv6 = net.isIPv6;
|
||||
|
||||
dns.setServers([ '8.8.8.8', '8.8.4.4' ]);
|
||||
|
||||
function checkWrap(req) {
|
||||
assert.ok(typeof req === 'object');
|
||||
}
|
||||
|
||||
const checkers = {
|
||||
checkA(r) {
|
||||
assert.ok(isIPv4(r.address));
|
||||
assert.strictEqual(typeof r.ttl, 'number');
|
||||
assert.strictEqual(r.type, 'A');
|
||||
},
|
||||
checkAAAA(r) {
|
||||
assert.ok(isIPv6(r.address));
|
||||
assert.strictEqual(typeof r.ttl, 'number');
|
||||
assert.strictEqual(r.type, 'AAAA');
|
||||
},
|
||||
checkCNAME(r) {
|
||||
assert.ok(r.value);
|
||||
assert.strictEqual(typeof r.value, 'string');
|
||||
assert.strictEqual(r.type, 'CNAME');
|
||||
},
|
||||
checkMX(r) {
|
||||
assert.strictEqual(typeof r.exchange, 'string');
|
||||
assert.strictEqual(typeof r.priority, 'number');
|
||||
assert.strictEqual(r.type, 'MX');
|
||||
},
|
||||
checkNAPTR(r) {
|
||||
assert.strictEqual(typeof r.flags, 'string');
|
||||
assert.strictEqual(typeof r.service, 'string');
|
||||
assert.strictEqual(typeof r.regexp, 'string');
|
||||
assert.strictEqual(typeof r.replacement, 'string');
|
||||
assert.strictEqual(typeof r.order, 'number');
|
||||
assert.strictEqual(typeof r.preference, 'number');
|
||||
assert.strictEqual(r.type, 'NAPTR');
|
||||
},
|
||||
checkNS(r) {
|
||||
assert.strictEqual(typeof r.value, 'string');
|
||||
assert.strictEqual(r.type, 'NS');
|
||||
},
|
||||
checkPTR(r) {
|
||||
assert.strictEqual(typeof r.value, 'string');
|
||||
assert.strictEqual(r.type, 'PTR');
|
||||
},
|
||||
checkTXT(r) {
|
||||
assert.ok(Array.isArray(r.entries));
|
||||
assert.ok(r.entries.length > 0);
|
||||
r.entries.forEach((txt) => {
|
||||
assert.strictEqual(txt.indexOf('v=spf1'), 0);
|
||||
});
|
||||
assert.strictEqual(r.type, 'TXT');
|
||||
},
|
||||
checkSOA(r) {
|
||||
assert.strictEqual(typeof r.nsname, 'string');
|
||||
assert.strictEqual(typeof r.hostmaster, 'string');
|
||||
assert.strictEqual(typeof r.serial, 'number');
|
||||
assert.strictEqual(typeof r.refresh, 'number');
|
||||
assert.strictEqual(typeof r.retry, 'number');
|
||||
assert.strictEqual(typeof r.expire, 'number');
|
||||
assert.strictEqual(typeof r.minttl, 'number');
|
||||
assert.strictEqual(r.type, 'SOA');
|
||||
},
|
||||
checkSRV(r) {
|
||||
assert.strictEqual(typeof r.name, 'string');
|
||||
assert.strictEqual(typeof r.port, 'number');
|
||||
assert.strictEqual(typeof r.priority, 'number');
|
||||
assert.strictEqual(typeof r.weight, 'number');
|
||||
assert.strictEqual(r.type, 'SRV');
|
||||
}
|
||||
};
|
||||
|
||||
function TEST(f) {
|
||||
function next() {
|
||||
const f = queue.shift();
|
||||
if (f) {
|
||||
running = true;
|
||||
f(done);
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
running = false;
|
||||
process.nextTick(next);
|
||||
}
|
||||
|
||||
queue.push(f);
|
||||
|
||||
if (!running) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(function test_google(done) {
|
||||
const req = dns.resolve(
|
||||
'google.com',
|
||||
'ANY',
|
||||
common.mustCall(function(err, ret) {
|
||||
assert.ifError(err);
|
||||
assert.ok(Array.isArray(ret));
|
||||
assert.ok(ret.length > 0);
|
||||
|
||||
/* current google.com has A / AAAA / MX / NS / TXT and SOA records */
|
||||
const types = {};
|
||||
ret.forEach((obj) => {
|
||||
types[obj.type] = true;
|
||||
checkers[`check${obj.type}`](obj);
|
||||
});
|
||||
assert.ok(
|
||||
types.A && types.AAAA && types.MX &&
|
||||
types.NS && types.TXT && types.SOA);
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(function test_sip2sip_for_naptr(done) {
|
||||
const req = dns.resolve(
|
||||
'sip2sip.info',
|
||||
'ANY',
|
||||
common.mustCall(function(err, ret) {
|
||||
assert.ifError(err);
|
||||
assert.ok(Array.isArray(ret));
|
||||
assert.ok(ret.length > 0);
|
||||
|
||||
/* current sip2sip.info has A / NS / NAPTR and SOA records */
|
||||
const types = {};
|
||||
ret.forEach((obj) => {
|
||||
types[obj.type] = true;
|
||||
checkers[`check${obj.type}`](obj);
|
||||
});
|
||||
assert.ok(types.A && types.NS && types.NAPTR && types.SOA);
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(function test_google_for_cname_and_srv(done) {
|
||||
const req = dns.resolve(
|
||||
'_jabber._tcp.google.com',
|
||||
'ANY',
|
||||
common.mustCall(function(err, ret) {
|
||||
assert.ifError(err);
|
||||
assert.ok(Array.isArray(ret));
|
||||
assert.ok(ret.length > 0);
|
||||
|
||||
const types = {};
|
||||
ret.forEach((obj) => {
|
||||
types[obj.type] = true;
|
||||
checkers[`check${obj.type}`](obj);
|
||||
});
|
||||
assert.ok(types.SRV);
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(function test_ptr(done) {
|
||||
const req = dns.resolve(
|
||||
'8.8.8.8.in-addr.arpa',
|
||||
'ANY',
|
||||
common.mustCall(function(err, ret) {
|
||||
assert.ifError(err);
|
||||
assert.ok(Array.isArray(ret));
|
||||
assert.ok(ret.length > 0);
|
||||
|
||||
/* current 8.8.8.8.in-addr.arpa has PTR record */
|
||||
const types = {};
|
||||
ret.forEach((obj) => {
|
||||
types[obj.type] = true;
|
||||
checkers[`check${obj.type}`](obj);
|
||||
});
|
||||
assert.ok(types.PTR);
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user