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()`][] |
|
| `'SOA'` | start of authority records | {Object} | [`dns.resolveSoa()`][] |
|
||||||
| `'SRV'` | service records | {Object} | [`dns.resolveSrv()`][] |
|
| `'SRV'` | service records | {Object} | [`dns.resolveSrv()`][] |
|
||||||
| `'TXT'` | text records | {string} | [`dns.resolveTxt()`][] |
|
| `'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
|
On error, `err` is an [`Error`][] object, where `err.code` is one of the
|
||||||
[DNS error codes](#dns_error_codes).
|
[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
|
one record. Depending on the use case, these could be either joined together or
|
||||||
treated separately.
|
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)
|
## dns.reverse(ip, callback)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.16
|
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.resolveSoa()`]: #dns_dns_resolvesoa_hostname_callback
|
||||||
[`dns.resolveSrv()`]: #dns_dns_resolvesrv_hostname_callback
|
[`dns.resolveSrv()`]: #dns_dns_resolvesrv_hostname_callback
|
||||||
[`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback
|
[`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback
|
||||||
|
[`dns.resolveAny()`]: #dns_dns_resolveany_hostname_callback
|
||||||
[DNS error codes]: #dns_error_codes
|
[DNS error codes]: #dns_error_codes
|
||||||
[Implementation considerations section]: #dns_implementation_considerations
|
[Implementation considerations section]: #dns_implementation_considerations
|
||||||
[supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags
|
[supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags
|
||||||
|
@ -268,6 +268,7 @@ function resolver(bindingName) {
|
|||||||
|
|
||||||
|
|
||||||
var resolveMap = Object.create(null);
|
var resolveMap = Object.create(null);
|
||||||
|
resolveMap.ANY = resolver('queryAny');
|
||||||
resolveMap.A = resolver('queryA');
|
resolveMap.A = resolver('queryA');
|
||||||
resolveMap.AAAA = resolver('queryAaaa');
|
resolveMap.AAAA = resolver('queryAaaa');
|
||||||
resolveMap.CNAME = resolver('queryCname');
|
resolveMap.CNAME = resolver('queryCname');
|
||||||
@ -349,6 +350,7 @@ module.exports = {
|
|||||||
getServers,
|
getServers,
|
||||||
setServers,
|
setServers,
|
||||||
resolve,
|
resolve,
|
||||||
|
resolveAny: resolveMap.ANY,
|
||||||
resolve4: resolveMap.A,
|
resolve4: resolveMap.A,
|
||||||
resolve6: resolveMap.AAAA,
|
resolve6: resolveMap.AAAA,
|
||||||
resolveCname: resolveMap.CNAME,
|
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(dest_string, "dest") \
|
||||||
V(detached_string, "detached") \
|
V(detached_string, "detached") \
|
||||||
V(disposed_string, "_disposed") \
|
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(domain_string, "domain") \
|
||||||
V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \
|
V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \
|
||||||
V(exchange_string, "exchange") \
|
V(exchange_string, "exchange") \
|
||||||
@ -113,6 +123,7 @@ namespace node {
|
|||||||
V(irq_string, "irq") \
|
V(irq_string, "irq") \
|
||||||
V(encoding_string, "encoding") \
|
V(encoding_string, "encoding") \
|
||||||
V(enter_string, "enter") \
|
V(enter_string, "enter") \
|
||||||
|
V(entries_string, "entries") \
|
||||||
V(env_pairs_string, "envPairs") \
|
V(env_pairs_string, "envPairs") \
|
||||||
V(errno_string, "errno") \
|
V(errno_string, "errno") \
|
||||||
V(error_string, "error") \
|
V(error_string, "error") \
|
||||||
@ -151,6 +162,7 @@ namespace node {
|
|||||||
V(issuer_string, "issuer") \
|
V(issuer_string, "issuer") \
|
||||||
V(issuercert_string, "issuerCertificate") \
|
V(issuercert_string, "issuerCertificate") \
|
||||||
V(kill_signal_string, "killSignal") \
|
V(kill_signal_string, "killSignal") \
|
||||||
|
V(length_string, "length") \
|
||||||
V(mac_string, "mac") \
|
V(mac_string, "mac") \
|
||||||
V(max_buffer_string, "maxBuffer") \
|
V(max_buffer_string, "maxBuffer") \
|
||||||
V(message_string, "message") \
|
V(message_string, "message") \
|
||||||
@ -231,6 +243,7 @@ namespace node {
|
|||||||
V(timeout_string, "timeout") \
|
V(timeout_string, "timeout") \
|
||||||
V(times_string, "times") \
|
V(times_string, "times") \
|
||||||
V(tls_ticket_string, "tlsTicket") \
|
V(tls_ticket_string, "tlsTicket") \
|
||||||
|
V(ttl_string, "ttl") \
|
||||||
V(type_string, "type") \
|
V(type_string, "type") \
|
||||||
V(uid_string, "uid") \
|
V(uid_string, "uid") \
|
||||||
V(unknown_string, "<unknown>") \
|
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