dns: add promisified dns module

PR-URL: https://github.com/nodejs/node/pull/21264
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
cjihrig 2018-06-11 14:56:33 -04:00
parent fea3595c2f
commit 7486c4d710
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
16 changed files with 1553 additions and 408 deletions

View File

@ -568,6 +568,456 @@ An error will be thrown if an invalid address is provided.
The `dns.setServers()` method must not be called while a DNS query is in
progress.
## DNS Promises API
> Stability: 1 - Experimental
The `dns.promises` API provides an alternative set of asynchronous DNS methods
that return `Promise` objects rather than using callbacks. The API is accessible
via `require('dns').promises`.
### Class: dnsPromises.Resolver
<!-- YAML
added: REPLACEME
-->
An independent resolver for DNS requests.
Note that creating a new resolver uses the default server settings. Setting
the servers used for a resolver using
[`resolver.setServers()`][`dnsPromises.setServers()`] does not affect
other resolvers:
```js
const { Resolver } = require('dns').promises;
const resolver = new Resolver();
resolver.setServers(['4.4.4.4']);
// This request will use the server at 4.4.4.4, independent of global settings.
resolver.resolve4('example.org').then((addresses) => {
// ...
});
// Alternatively, the same code can be written using async-await style.
(async function() {
const addresses = await resolver.resolve4('example.org');
})();
```
The following methods from the `dnsPromises` API are available:
* [`resolver.getServers()`][`dnsPromises.getServers()`]
* [`resolver.setServers()`][`dnsPromises.setServers()`]
* [`resolver.resolve()`][`dnsPromises.resolve()`]
* [`resolver.resolve4()`][`dnsPromises.resolve4()`]
* [`resolver.resolve6()`][`dnsPromises.resolve6()`]
* [`resolver.resolveAny()`][`dnsPromises.resolveAny()`]
* [`resolver.resolveCname()`][`dnsPromises.resolveCname()`]
* [`resolver.resolveMx()`][`dnsPromises.resolveMx()`]
* [`resolver.resolveNaptr()`][`dnsPromises.resolveNaptr()`]
* [`resolver.resolveNs()`][`dnsPromises.resolveNs()`]
* [`resolver.resolvePtr()`][`dnsPromises.resolvePtr()`]
* [`resolver.resolveSoa()`][`dnsPromises.resolveSoa()`]
* [`resolver.resolveSrv()`][`dnsPromises.resolveSrv()`]
* [`resolver.resolveTxt()`][`dnsPromises.resolveTxt()`]
* [`resolver.reverse()`][`dnsPromises.reverse()`]
#### resolver.cancel()
<!-- YAML
added: REPLACEME
-->
Cancel all outstanding DNS queries made by this resolver. The corresponding
`Promise`s will be rejected with an error with code `ECANCELLED`.
### dnsPromises.getServers()
<!-- YAML
added: REPLACEME
-->
* Returns: {string[]}
Returns an array of IP address strings, formatted according to [rfc5952][],
that are currently configured for DNS resolution. A string will include a port
section if a custom port is used.
<!-- eslint-disable semi-->
```js
[
'4.4.4.4',
'2001:4860:4860::8888',
'4.4.4.4:1053',
'[2001:4860:4860::8888]:1053'
]
```
### dnsPromises.lookup(hostname[, options])
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
- `options` {integer | Object}
- `family` {integer} The record family. Must be `4` or `6`. IPv4
and IPv6 addresses are both returned by default.
- `hints` {number} One or more [supported `getaddrinfo` flags][]. Multiple
flags may be passed by bitwise `OR`ing their values.
- `all` {boolean} When `true`, the `Promise` is resolved with all addresses in
an array. Otherwise, returns a single address. **Default:** `false`.
- `verbatim` {boolean} When `true`, the `Promise` is resolved with IPv4 and
IPv6 addresses in the order the DNS resolver returned them. When `false`,
IPv4 addresses are placed before IPv6 addresses.
**Default:** currently `false` (addresses are reordered) but this is
expected to change in the not too distant future.
New code should use `{ verbatim: true }`.
Resolves a hostname (e.g. `'nodejs.org'`) into the first found A (IPv4) or
AAAA (IPv6) record. All `option` properties are optional. If `options` is an
integer, then it must be `4` or `6` if `options` is not provided, then IPv4
and IPv6 addresses are both returned if found.
With the `all` option set to `true`, the `Promise` is resolved with `addresses`
being an array of objects with the properties `address` and `family`.
On error, the `Promise` is rejected with an [`Error`][] object, where `err.code`
is the error code.
Keep in mind that `err.code` will be set to `'ENOENT'` not only when
the hostname does not exist but also when the lookup fails in other ways
such as no available file descriptors.
[`dnsPromises.lookup()`][] does not necessarily have anything to do with the DNS
protocol. The implementation uses an operating system facility that can
associate names with addresses, and vice versa. This implementation can have
subtle but important consequences on the behavior of any Node.js program. Please
take some time to consult the [Implementation considerations section][] before
using `dnsPromises.lookup()`.
Example usage:
```js
const dns = require('dns');
const dnsPromises = dns.promises;
const options = {
family: 6,
hints: dns.ADDRCONFIG | dns.V4MAPPED,
};
dnsPromises.lookup('example.com', options).then((result) => {
console.log('address: %j family: IPv%s', result.address, result.family);
// address: "2606:2800:220:1:248:1893:25c8:1946" family: IPv6
});
// When options.all is true, the result will be an Array.
options.all = true;
dnsPromises.lookup('example.com', options).then((result) => {
console.log('addresses: %j', result);
// addresses: [{"address":"2606:2800:220:1:248:1893:25c8:1946","family":6}]
});
```
### dnsPromises.lookupService(address, port)
<!-- YAML
added: REPLACEME
-->
- `address` {string}
- `port` {number}
Resolves the given `address` and `port` into a hostname and service using
the operating system's underlying `getnameinfo` implementation.
If `address` is not a valid IP address, a `TypeError` will be thrown.
The `port` will be coerced to a number. If it is not a legal port, a `TypeError`
will be thrown.
On error, the `Promise` is rejected with an [`Error`][] object, where `err.code`
is the error code.
```js
const dnsPromises = require('dns').promises;
dnsPromises.lookupService('127.0.0.1', 22).then((result) => {
console.log(result.hostname, result.service);
// Prints: localhost ssh
});
```
### dnsPromises.resolve(hostname[, rrtype])
<!-- YAML
added: REPLACEME
-->
- `hostname` {string} Hostname to resolve.
- `rrtype` {string} Resource record type. **Default:** `'A'`.
Uses the DNS protocol to resolve a hostname (e.g. `'nodejs.org'`) into an array
of the resource records. When successful, the `Promise` is resolved with an
array of resource records. The type and structure of individual results vary
based on `rrtype`:
| `rrtype` | `records` contains | Result type | Shorthand method |
|-----------|--------------------------------|-------------|--------------------------|
| `'A'` | IPv4 addresses (default) | {string} | [`dnsPromises.resolve4()`][] |
| `'AAAA'` | IPv6 addresses | {string} | [`dnsPromises.resolve6()`][] |
| `'CNAME'` | canonical name records | {string} | [`dnsPromises.resolveCname()`][] |
| `'MX'` | mail exchange records | {Object} | [`dnsPromises.resolveMx()`][] |
| `'NAPTR'` | name authority pointer records | {Object} | [`dnsPromises.resolveNaptr()`][] |
| `'NS'` | name server records | {string} | [`dnsPromises.resolveNs()`][] |
| `'PTR'` | pointer records | {string} | [`dnsPromises.resolvePtr()`][] |
| `'SOA'` | start of authority records | {Object} | [`dnsPromises.resolveSoa()`][] |
| `'SRV'` | service records | {Object} | [`dnsPromises.resolveSrv()`][] |
| `'TXT'` | text records | {string[]} | [`dnsPromises.resolveTxt()`][] |
| `'ANY'` | any records | {Object} | [`dnsPromises.resolveAny()`][] |
On error, the `Promise` is rejected with an [`Error`][] object, where `err.code`
is one of the [DNS error codes](#dns_error_codes).
### dnsPromises.resolve4(hostname[, options])
<!-- YAML
added: REPLACEME
-->
- `hostname` {string} Hostname to resolve.
- `options` {Object}
- `ttl` {boolean} Retrieve the Time-To-Live value (TTL) of each record.
When `true`, the `Promise` is resolved with an array of
`{ address: '1.2.3.4', ttl: 60 }` objects rather than an array of strings,
with the TTL expressed in seconds.
Uses the DNS protocol to resolve IPv4 addresses (`A` records) for the
`hostname`. On success, the `Promise` is resolved with an array of IPv4
addresses (e.g. `['74.125.79.104', '74.125.79.105', '74.125.79.106']`).
### dnsPromises.resolve6(hostname[, options])
<!-- YAML
added: REPLACEME
-->
- `hostname` {string} Hostname to resolve.
- `options` {Object}
- `ttl` {boolean} Retrieve the Time-To-Live value (TTL) of each record.
When `true`, the `Promise` is resolved with an array of
`{ address: '0:1:2:3:4:5:6:7', ttl: 60 }` objects rather than an array of
strings, with the TTL expressed in seconds.
Uses the DNS protocol to resolve IPv6 addresses (`AAAA` records) for the
`hostname`. On success, the `Promise` is resolved with an array of IPv6
addresses.
### dnsPromises.resolveCname(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve `CNAME` records for the `hostname`. On success,
the `Promise` is resolved with an array of canonical name records available for
the `hostname` (e.g. `['bar.example.com']`).
### dnsPromises.resolveMx(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve mail exchange records (`MX` records) for the
`hostname`. On success, the `Promise` is resolved with an array of objects
containing both a `priority` and `exchange` property (e.g.
`[{priority: 10, exchange: 'mx.example.com'}, ...]`).
### dnsPromises.resolveNaptr(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve regular expression based records (`NAPTR`
records) for the `hostname`. On success, the `Promise` is resolved with an array
of objects with the following properties:
* `flags`
* `service`
* `regexp`
* `replacement`
* `order`
* `preference`
<!-- eslint-skip -->
```js
{
flags: 's',
service: 'SIP+D2U',
regexp: '',
replacement: '_sip._udp.example.com',
order: 30,
preference: 100
}
```
### dnsPromises.resolveNs(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve name server records (`NS` records) for the
`hostname`. On success, the `Promise` is resolved with an array of name server
records available for `hostname` (e.g.
`['ns1.example.com', 'ns2.example.com']`).
### dnsPromises.resolvePtr(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve pointer records (`PTR` records) for the
`hostname`. On success, the `Promise` is resolved with an array of strings
containing the reply records.
### dnsPromises.resolveSoa(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve a start of authority record (`SOA` record) for
the `hostname`. On success, the `Promise` is resolved with an object with the
following properties:
* `nsname`
* `hostmaster`
* `serial`
* `refresh`
* `retry`
* `expire`
* `minttl`
<!-- eslint-skip -->
```js
{
nsname: 'ns.example.com',
hostmaster: 'root.example.com',
serial: 2013101809,
refresh: 10000,
retry: 2400,
expire: 604800,
minttl: 3600
}
```
### dnsPromises.resolveSrv(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve service records (`SRV` records) for the
`hostname`. On success, the `Promise` is resolved with an array of objects with
the following properties:
* `priority`
* `weight`
* `port`
* `name`
<!-- eslint-skip -->
```js
{
priority: 10,
weight: 5,
port: 21223,
name: 'service.example.com'
}
```
### dnsPromises.resolveTxt(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve text queries (`TXT` records) for the
`hostname`. On success, the `Promise` is resolved with a two-dimensional array
of the text records available for `hostname` (e.g.
`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
one record. Depending on the use case, these could be either joined together or
treated separately.
### dnsPromises.resolveAny(hostname)
<!-- YAML
added: REPLACEME
-->
- `hostname` {string}
Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query).
On success, the `Promise` is resolved with 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 [`dnsPromises.resolveMx()`][] |
| `'NAPTR'` | Refer to [`dnsPromises.resolveNaptr()`][] |
| `'NS'` | `value` |
| `'PTR'` | `value` |
| `'SOA'` | Refer to [`dnsPromises.resolveSoa()`][] |
| `'SRV'` | Refer to [`dnsPromises.resolveSrv()`][] |
| `'TXT'` | This type of record contains an array property called `entries` which refers to [`dnsPromises.resolveTxt()`][], e.g. `{ entries: ['...'], type: 'TXT' }` |
Here is an example of the result object:
<!-- eslint-disable semi -->
```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: '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 } ]
```
### dnsPromises.reverse(ip)
<!-- YAML
added: REPLACEME
-->
- `ip` {string}
Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an
array of hostnames.
On error, the `Promise` is rejected with an [`Error`][] object, where `err.code`
is one of the [DNS error codes](#dns_error_codes).
### dnsPromises.setServers(servers)
<!-- YAML
added: REPLACEME
-->
- `servers` {string[]} array of [rfc5952][] formatted addresses
Sets the IP address and port of servers to be used when performing DNS
resolution. The `servers` argument is an array of [rfc5952][] formatted
addresses. If the port is the IANA default DNS port (53) it can be omitted.
```js
dnsPromises.setServers([
'4.4.4.4',
'[2001:4860:4860::8888]',
'4.4.4.4:1053',
'[2001:4860:4860::8888]:1053'
]);
```
An error will be thrown if an invalid address is provided.
The `dnsPromises.setServers()` method must not be called while a DNS query is in
progress.
## Error codes
Each DNS query can return one of the following error codes:
@ -659,6 +1109,22 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
[`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback
[`dns.reverse()`]: #dns_dns_reverse_ip_callback
[`dns.setServers()`]: #dns_dns_setservers_servers
[`dnsPromises.getServers()`]: #dns_dnspromises_getservers
[`dnsPromises.lookup()`]: #dns_dnspromises_lookup_hostname_options
[`dnsPromises.resolve()`]: #dns_dnspromises_resolve_hostname_rrtype
[`dnsPromises.resolve4()`]: #dns_dnspromises_resolve4_hostname_options
[`dnsPromises.resolve6()`]: #dns_dnspromises_resolve6_hostname_options
[`dnsPromises.resolveAny()`]: #dns_dnspromises_resolveany_hostname
[`dnsPromises.resolveCname()`]: #dns_dnspromises_resolvecname_hostname
[`dnsPromises.resolveMx()`]: #dns_dnspromises_resolvemx_hostname
[`dnsPromises.resolveNaptr()`]: #dns_dnspromises_resolvenaptr_hostname
[`dnsPromises.resolveNs()`]: #dns_dnspromises_resolvens_hostname
[`dnsPromises.resolvePtr()`]: #dns_dnspromises_resolveptr_hostname
[`dnsPromises.resolveSoa()`]: #dns_dnspromises_resolvesoa_hostname
[`dnsPromises.resolveSrv()`]: #dns_dnspromises_resolvesrv_hostname
[`dnsPromises.resolveTxt()`]: #dns_dnspromises_resolvetxt_hostname
[`dnsPromises.reverse()`]: #dns_dnspromises_reverse_ip
[`dnsPromises.setServers()`]: #dns_dnspromises_setservers_servers
[`socket.connect()`]: net.html#net_socket_connect_options_connectlistener
[`util.promisify()`]: util.html#util_util_promisify_original
[DNS error codes]: #dns_error_codes

View File

@ -26,10 +26,15 @@ const { isIP, isIPv4, isLegalPort } = require('internal/net');
const { customPromisifyArgs } = require('internal/util');
const errors = require('internal/errors');
const {
ERR_DNS_SET_SERVERS_FAILED,
bindDefaultResolver,
getDefaultResolver,
setDefaultResolver,
Resolver,
validateHints
} = require('internal/dns/utils');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK,
ERR_INVALID_IP_ADDRESS,
ERR_INVALID_OPT_VALUE,
ERR_MISSING_ARGS,
ERR_SOCKET_BAD_PORT
@ -39,12 +44,13 @@ const {
GetAddrInfoReqWrap,
GetNameInfoReqWrap,
QueryReqWrap,
ChannelWrap,
} = cares;
const IANA_DNS_PORT = 53;
const dnsException = errors.dnsException;
let promisesWarn = true;
let promises; // Lazy loaded
function onlookup(err, addresses) {
if (err) {
return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
@ -97,12 +103,7 @@ function lookup(hostname, options, callback) {
all = options.all === true;
verbatim = options.verbatim === true;
if (hints !== 0 &&
hints !== cares.AI_ADDRCONFIG &&
hints !== cares.AI_V4MAPPED &&
hints !== (cares.AI_ADDRCONFIG | cares.AI_V4MAPPED)) {
throw new ERR_INVALID_OPT_VALUE('hints', hints);
}
validateHints(hints);
} else {
family = options >>> 0;
}
@ -197,17 +198,6 @@ function onresolve(err, result, ttls) {
this.callback(null, result);
}
// Resolver instances correspond 1:1 to c-ares channels.
class Resolver {
constructor() {
this._handle = new ChannelWrap();
}
cancel() {
this._handle.cancel();
}
}
function resolver(bindingName) {
function query(name, /* options, */ callback) {
var options;
@ -270,101 +260,15 @@ function resolve(hostname, rrtype, callback) {
}
}
Resolver.prototype.getServers = getServers;
function getServers() {
const ret = this._handle.getServers();
return ret.map((val) => {
if (!val[1] || val[1] === IANA_DNS_PORT) return val[0];
const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0];
return `${host}:${val[1]}`;
});
}
Resolver.prototype.setServers = setServers;
function setServers(servers) {
// cache the original servers because in the event of an error setting the
// servers cares won't have any servers available for resolution
const orig = this._handle.getServers();
const newSet = [];
const IPv6RE = /^\[([^[\]]*)\]/;
const addrSplitRE = /(^.+?)(?::(\d+))?$/;
servers.forEach((serv) => {
var ipVersion = isIP(serv);
if (ipVersion !== 0)
return newSet.push([ipVersion, serv, IANA_DNS_PORT]);
const match = serv.match(IPv6RE);
// we have an IPv6 in brackets
if (match) {
ipVersion = isIP(match[1]);
if (ipVersion !== 0) {
const port =
parseInt(serv.replace(addrSplitRE, '$2')) ||
IANA_DNS_PORT;
return newSet.push([ipVersion, match[1], port]);
}
}
// addr::port
const addrSplitMatch = serv.match(addrSplitRE);
if (addrSplitMatch) {
const hostIP = addrSplitMatch[1];
const port = addrSplitMatch[2] || IANA_DNS_PORT;
ipVersion = isIP(hostIP);
if (ipVersion !== 0) {
return newSet.push([ipVersion, hostIP, parseInt(port)]);
}
}
throw new ERR_INVALID_IP_ADDRESS(serv);
});
const errorNumber = this._handle.setServers(newSet);
if (errorNumber !== 0) {
// reset the servers to the old servers, because ares probably unset them
this._handle.setServers(orig.join(','));
var err = cares.strerror(errorNumber);
throw new ERR_DNS_SET_SERVERS_FAILED(err, servers);
}
}
let defaultResolver = new Resolver();
const resolverKeys = [
'getServers',
'resolve',
'resolveAny',
'resolve4',
'resolve6',
'resolveCname',
'resolveMx',
'resolveNs',
'resolveTxt',
'resolveSrv',
'resolvePtr',
'resolveNaptr',
'resolveSoa',
'reverse'
];
function setExportsFunctions() {
resolverKeys.forEach((key) => {
module.exports[key] = defaultResolver[key].bind(defaultResolver);
});
}
function defaultResolverSetServers(servers) {
const resolver = new Resolver();
resolver.setServers(servers);
defaultResolver = resolver;
setExportsFunctions();
setDefaultResolver(resolver);
bindDefaultResolver(module.exports, Resolver.prototype);
if (promises !== undefined)
bindDefaultResolver(promises, promises.Resolver.prototype);
}
module.exports = {
@ -405,4 +309,21 @@ module.exports = {
CANCELLED: 'ECANCELLED'
};
setExportsFunctions();
bindDefaultResolver(module.exports, getDefaultResolver());
Object.defineProperties(module.exports, {
promises: {
configurable: true,
enumerable: false,
get() {
if (promisesWarn) {
promises = require('internal/dns/promises');
promises.setServers = defaultResolverSetServers;
promisesWarn = false;
process.emitWarning('The dns.promises API is experimental',
'ExperimentalWarning');
}
return promises;
}
}
});

View File

@ -0,0 +1,249 @@
'use strict';
const {
bindDefaultResolver,
Resolver: CallbackResolver,
validateHints
} = require('internal/dns/utils');
const { codes, dnsException } = require('internal/errors');
const { isIP, isIPv4, isLegalPort } = require('internal/net');
const {
getaddrinfo,
getnameinfo,
ChannelWrap,
GetAddrInfoReqWrap,
GetNameInfoReqWrap,
QueryReqWrap
} = process.binding('cares_wrap');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_OPT_VALUE,
ERR_MISSING_ARGS,
ERR_SOCKET_BAD_PORT
} = codes;
function onlookup(err, addresses) {
if (err) {
this.reject(dnsException(err, 'getaddrinfo', this.hostname));
return;
}
const family = this.family ? this.family : isIPv4(addresses[0]) ? 4 : 6;
this.resolve({ address: addresses[0], family });
}
function onlookupall(err, addresses) {
if (err) {
this.reject(dnsException(err, 'getaddrinfo', this.hostname));
return;
}
const family = this.family;
for (var i = 0; i < addresses.length; i++) {
const address = addresses[i];
addresses[i] = {
address,
family: family ? family : isIPv4(addresses[i]) ? 4 : 6
};
}
this.resolve(addresses);
}
function createLookupPromise(family, hostname, all, hints, verbatim) {
return new Promise((resolve, reject) => {
if (!hostname) {
if (all)
resolve([]);
else
resolve({ address: null, family: family === 6 ? 6 : 4 });
return;
}
const matchedFamily = isIP(hostname);
if (matchedFamily !== 0) {
const result = { address: hostname, family: matchedFamily };
if (all)
resolve([result]);
else
resolve(result);
return;
}
const req = new GetAddrInfoReqWrap();
req.family = family;
req.hostname = hostname;
req.oncomplete = all ? onlookupall : onlookup;
req.resolve = resolve;
req.reject = reject;
const err = getaddrinfo(req, hostname, family, hints, verbatim);
if (err) {
reject(dnsException(err, 'getaddrinfo', hostname));
}
});
}
function lookup(hostname, options) {
var hints = 0;
var family = -1;
var all = false;
var verbatim = false;
// Parse arguments
if (hostname && typeof hostname !== 'string') {
throw new ERR_INVALID_ARG_TYPE('hostname', ['string', 'falsy'], hostname);
} else if (options !== null && typeof options === 'object') {
hints = options.hints >>> 0;
family = options.family >>> 0;
all = options.all === true;
verbatim = options.verbatim === true;
validateHints(hints);
} else {
family = options >>> 0;
}
if (family !== 0 && family !== 4 && family !== 6)
throw new ERR_INVALID_OPT_VALUE('family', family);
return createLookupPromise(family, hostname, all, hints, verbatim);
}
function onlookupservice(err, hostname, service) {
if (err) {
this.reject(dnsException(err, 'getnameinfo', this.host));
return;
}
this.resolve({ hostname, service });
}
function createLookupServicePromise(host, port) {
return new Promise((resolve, reject) => {
const req = new GetNameInfoReqWrap();
req.host = host;
req.port = port;
req.oncomplete = onlookupservice;
req.resolve = resolve;
req.reject = reject;
const err = getnameinfo(req, host, port);
if (err)
reject(dnsException(err, 'getnameinfo', host));
});
}
function lookupService(host, port) {
if (arguments.length !== 2)
throw new ERR_MISSING_ARGS('host', 'port');
if (isIP(host) === 0)
throw new ERR_INVALID_OPT_VALUE('host', host);
if (!isLegalPort(port))
throw new ERR_SOCKET_BAD_PORT(port);
return createLookupServicePromise(host, +port);
}
function onresolve(err, result, ttls) {
if (err) {
this.reject(dnsException(err, this.bindingName, this.hostname));
return;
}
if (ttls && this.ttl)
result = result.map((address, index) => ({ address, ttl: ttls[index] }));
this.resolve(result);
}
function createResolverPromise(resolver, bindingName, hostname, ttl) {
return new Promise((resolve, reject) => {
const req = new QueryReqWrap();
req.bindingName = bindingName;
req.hostname = hostname;
req.oncomplete = onresolve;
req.resolve = resolve;
req.reject = reject;
req.ttl = ttl;
const err = resolver._handle[bindingName](req, hostname);
if (err)
reject(dnsException(err, bindingName, hostname));
});
}
function resolver(bindingName) {
function query(name, options) {
if (typeof name !== 'string') {
throw new ERR_INVALID_ARG_TYPE('name', 'string', name);
}
const ttl = !!(options && options.ttl);
return createResolverPromise(this, bindingName, name, ttl);
}
Object.defineProperty(query, 'name', { value: bindingName });
return query;
}
const resolveMap = Object.create(null);
// Resolver instances correspond 1:1 to c-ares channels.
class Resolver {
constructor() {
this._handle = new ChannelWrap();
}
}
Resolver.prototype.cancel = CallbackResolver.prototype.cancel;
Resolver.prototype.getServers = CallbackResolver.prototype.getServers;
Resolver.prototype.setServers = CallbackResolver.prototype.setServers;
Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny');
Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA');
Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname');
Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx');
Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs');
Resolver.prototype.resolveTxt = resolveMap.TXT = resolver('queryTxt');
Resolver.prototype.resolveSrv = resolveMap.SRV = resolver('querySrv');
Resolver.prototype.resolvePtr = resolveMap.PTR = resolver('queryPtr');
Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr');
Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa');
Resolver.prototype.reverse = resolver('getHostByAddr');
Resolver.prototype.resolve = function resolve(hostname, rrtype) {
var resolver;
if (typeof rrtype === 'string') {
resolver = resolveMap[rrtype];
if (typeof resolver !== 'function')
throw new ERR_INVALID_OPT_VALUE('rrtype', rrtype);
} else if (rrtype === undefined) {
resolver = resolveMap.A;
} else {
throw new ERR_INVALID_ARG_TYPE('rrtype', 'string', rrtype);
}
return resolver.call(this, hostname);
};
module.exports = { lookup, lookupService, Resolver };
bindDefaultResolver(module.exports, Resolver.prototype);

141
lib/internal/dns/utils.js Normal file
View File

@ -0,0 +1,141 @@
'use strict';
const errors = require('internal/errors');
const { isIP } = require('internal/net');
const {
ChannelWrap,
strerror,
AI_ADDRCONFIG,
AI_V4MAPPED
} = process.binding('cares_wrap');
const IANA_DNS_PORT = 53;
const IPv6RE = /^\[([^[\]]*)\]/;
const addrSplitRE = /(^.+?)(?::(\d+))?$/;
const {
ERR_DNS_SET_SERVERS_FAILED,
ERR_INVALID_IP_ADDRESS,
ERR_INVALID_OPT_VALUE
} = errors.codes;
// Resolver instances correspond 1:1 to c-ares channels.
class Resolver {
constructor() {
this._handle = new ChannelWrap();
}
cancel() {
this._handle.cancel();
}
getServers() {
return this._handle.getServers().map((val) => {
if (!val[1] || val[1] === IANA_DNS_PORT)
return val[0];
const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0];
return `${host}:${val[1]}`;
});
}
setServers(servers) {
// Cache the original servers because in the event of an error while
// setting the servers, c-ares won't have any servers available for
// resolution.
const orig = this._handle.getServers();
const newSet = [];
servers.forEach((serv) => {
var ipVersion = isIP(serv);
if (ipVersion !== 0)
return newSet.push([ipVersion, serv, IANA_DNS_PORT]);
const match = serv.match(IPv6RE);
// Check for an IPv6 in brackets.
if (match) {
ipVersion = isIP(match[1]);
if (ipVersion !== 0) {
const port =
parseInt(serv.replace(addrSplitRE, '$2')) ||
IANA_DNS_PORT;
return newSet.push([ipVersion, match[1], port]);
}
}
// addr::port
const addrSplitMatch = serv.match(addrSplitRE);
if (addrSplitMatch) {
const hostIP = addrSplitMatch[1];
const port = addrSplitMatch[2] || IANA_DNS_PORT;
ipVersion = isIP(hostIP);
if (ipVersion !== 0) {
return newSet.push([ipVersion, hostIP, parseInt(port)]);
}
}
throw new ERR_INVALID_IP_ADDRESS(serv);
});
const errorNumber = this._handle.setServers(newSet);
if (errorNumber !== 0) {
// Reset the servers to the old servers, because ares probably unset them.
this._handle.setServers(orig.join(','));
const err = strerror(errorNumber);
throw new ERR_DNS_SET_SERVERS_FAILED(err, servers);
}
}
}
let defaultResolver = new Resolver();
const resolverKeys = [
'getServers',
'resolve',
'resolveAny',
'resolve4',
'resolve6',
'resolveCname',
'resolveMx',
'resolveNs',
'resolveTxt',
'resolveSrv',
'resolvePtr',
'resolveNaptr',
'resolveSoa',
'reverse'
];
function getDefaultResolver() {
return defaultResolver;
}
function setDefaultResolver(resolver) {
defaultResolver = resolver;
}
function bindDefaultResolver(target, source) {
resolverKeys.forEach((key) => {
target[key] = source[key].bind(defaultResolver);
});
}
function validateHints(hints) {
if (hints !== 0 &&
hints !== AI_ADDRCONFIG &&
hints !== AI_V4MAPPED &&
hints !== (AI_ADDRCONFIG | AI_V4MAPPED)) {
throw new ERR_INVALID_OPT_VALUE('hints', hints);
}
}
module.exports = {
bindDefaultResolver,
getDefaultResolver,
setDefaultResolver,
validateHints,
Resolver
};

View File

@ -101,6 +101,8 @@
'lib/internal/crypto/sig.js',
'lib/internal/crypto/util.js',
'lib/internal/constants.js',
'lib/internal/dns/promises.js',
'lib/internal/dns/utils.js',
'lib/internal/encoding.js',
'lib/internal/errors.js',
'lib/internal/error-serdes.js',

View File

@ -9,6 +9,9 @@ const net = require('net');
let running = false;
const queue = [];
common.crashOnUnhandledRejection();
const dnsPromises = dns.promises;
const isIPv4 = net.isIPv4;
const isIPv6 = net.isIPv6;
@ -101,93 +104,95 @@ function TEST(f) {
}
}
TEST(function test_google(done) {
function processResult(res) {
assert.ok(Array.isArray(res));
assert.ok(res.length > 0);
const types = {};
res.forEach((obj) => {
types[obj.type] = true;
checkers[`check${obj.type}`](obj);
});
return types;
}
TEST(async function test_google(done) {
function validateResult(res) {
const types = processResult(res);
assert.ok(
types.A && types.AAAA && types.MX &&
types.NS && types.TXT && types.SOA);
}
validateResult(await dnsPromises.resolve('google.com', 'ANY'));
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);
validateResult(ret);
done();
}));
checkWrap(req);
});
TEST(function test_sip2sip_for_naptr(done) {
TEST(async function test_sip2sip_for_naptr(done) {
function validateResult(res) {
const types = processResult(res);
assert.ok(types.A && types.NS && types.NAPTR && types.SOA);
}
validateResult(await dnsPromises.resolve('sip2sip.info', 'ANY'));
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);
validateResult(ret);
done();
}));
checkWrap(req);
});
TEST(function test_google_for_cname_and_srv(done) {
TEST(async function test_google_for_cname_and_srv(done) {
function validateResult(res) {
const types = processResult(res);
assert.ok(types.SRV);
}
validateResult(await dnsPromises.resolve('_jabber._tcp.google.com', 'ANY'));
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);
validateResult(ret);
done();
}));
checkWrap(req);
});
TEST(function test_ptr(done) {
TEST(async function test_ptr(done) {
function validateResult(res) {
const types = processResult(res);
assert.ok(types.PTR);
}
validateResult(await dnsPromises.resolve('8.8.8.8.in-addr.arpa', 'ANY'));
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);
validateResult(ret);
done();
}));

View File

@ -9,6 +9,7 @@ const isIPv4 = net.isIPv4;
common.crashOnUnhandledRejection();
const dnsPromises = dns.promises;
let running = false;
const queue = [];
@ -38,139 +39,187 @@ function checkWrap(req) {
assert.ok(typeof req === 'object');
}
TEST(function test_resolve4(done) {
TEST(async function test_resolve4(done) {
function validateResult(res) {
assert.ok(res.length > 0);
for (let i = 0; i < res.length; i++) {
assert.ok(isIPv4(res[i]));
}
}
validateResult(await dnsPromises.resolve4(addresses.INET4_HOST));
const req = dns.resolve4(
addresses.INET4_HOST,
common.mustCall((err, ips) => {
assert.ifError(err);
assert.ok(ips.length > 0);
for (let i = 0; i < ips.length; i++) {
assert.ok(isIPv4(ips[i]));
}
validateResult(ips);
done();
}));
checkWrap(req);
});
TEST(function test_reverse_ipv4(done) {
TEST(async function test_reverse_ipv4(done) {
function validateResult(res) {
assert.ok(res.length > 0);
for (let i = 0; i < res.length; i++) {
assert.ok(res[i]);
assert.ok(typeof res[i] === 'string');
}
}
validateResult(await dnsPromises.reverse(addresses.INET4_IP));
const req = dns.reverse(
addresses.INET4_IP,
common.mustCall((err, domains) => {
assert.ifError(err);
assert.ok(domains.length > 0);
for (let i = 0; i < domains.length; i++) {
assert.ok(domains[i]);
assert.ok(typeof domains[i] === 'string');
}
validateResult(domains);
done();
}));
checkWrap(req);
});
TEST(function test_lookup_ipv4_explicit(done) {
TEST(async function test_lookup_ipv4_explicit(done) {
function validateResult(res) {
assert.ok(net.isIPv4(res.address));
assert.strictEqual(res.family, 4);
}
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, 4));
const req = dns.lookup(
addresses.INET4_HOST, 4,
common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(net.isIPv4(ip));
assert.strictEqual(family, 4);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_ipv4_implicit(done) {
TEST(async function test_lookup_ipv4_implicit(done) {
function validateResult(res) {
assert.ok(net.isIPv4(res.address));
assert.strictEqual(res.family, 4);
}
validateResult(await dnsPromises.lookup(addresses.INET4_HOST));
const req = dns.lookup(
addresses.INET4_HOST,
common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(net.isIPv4(ip));
assert.strictEqual(family, 4);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_ipv4_explicit_object(done) {
TEST(async function test_lookup_ipv4_explicit_object(done) {
function validateResult(res) {
assert.ok(net.isIPv4(res.address));
assert.strictEqual(res.family, 4);
}
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, { family: 4 }));
const req = dns.lookup(addresses.INET4_HOST, {
family: 4
}, common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(net.isIPv4(ip));
assert.strictEqual(family, 4);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_ipv4_hint_addrconfig(done) {
TEST(async function test_lookup_ipv4_hint_addrconfig(done) {
function validateResult(res) {
assert.ok(net.isIPv4(res.address));
assert.strictEqual(res.family, 4);
}
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, {
hints: dns.ADDRCONFIG
}));
const req = dns.lookup(addresses.INET4_HOST, {
hints: dns.ADDRCONFIG
}, common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(net.isIPv4(ip));
assert.strictEqual(family, 4);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_ip_ipv4(done) {
TEST(async function test_lookup_ip_ipv4(done) {
function validateResult(res) {
assert.strictEqual(res.address, '127.0.0.1');
assert.strictEqual(res.family, 4);
}
validateResult(await dnsPromises.lookup('127.0.0.1'));
const req = dns.lookup('127.0.0.1',
common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.strictEqual(ip, '127.0.0.1');
assert.strictEqual(family, 4);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_localhost_ipv4(done) {
TEST(async function test_lookup_localhost_ipv4(done) {
function validateResult(res) {
assert.strictEqual(res.address, '127.0.0.1');
assert.strictEqual(res.family, 4);
}
validateResult(await dnsPromises.lookup('localhost', 4));
const req = dns.lookup('localhost', 4,
common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.strictEqual(ip, '127.0.0.1');
assert.strictEqual(family, 4);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_all_ipv4(done) {
TEST(async function test_lookup_all_ipv4(done) {
function validateResult(res) {
assert.ok(Array.isArray(res));
assert.ok(res.length > 0);
res.forEach((ip) => {
assert.ok(isIPv4(ip.address));
assert.strictEqual(ip.family, 4);
});
}
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, {
all: true,
family: 4
}));
const req = dns.lookup(
addresses.INET4_HOST,
{ all: true, family: 4 },
common.mustCall((err, ips) => {
assert.ifError(err);
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);
ips.forEach((ip) => {
assert.ok(isIPv4(ip.address));
assert.strictEqual(ip.family, 4);
});
validateResult(ips);
done();
})
);
@ -178,14 +227,20 @@ TEST(function test_lookup_all_ipv4(done) {
checkWrap(req);
});
TEST(function test_lookupservice_ip_ipv4(done) {
TEST(async function test_lookupservice_ip_ipv4(done) {
function validateResult(res) {
assert.strictEqual(typeof res.hostname, 'string');
assert(res.hostname);
assert(['http', 'www', '80'].includes(res.service));
}
validateResult(await dnsPromises.lookupService('127.0.0.1', 80));
const req = dns.lookupService(
'127.0.0.1', 80,
common.mustCall((err, host, service) => {
common.mustCall((err, hostname, service) => {
assert.ifError(err);
assert.strictEqual(typeof host, 'string');
assert(host);
assert(['http', 'www', '80'].includes(service));
validateResult({ hostname, service });
done();
})
);

View File

@ -4,9 +4,12 @@ const { addresses } = require('../common/internet');
if (!common.hasIPv6)
common.skip('this test, no IPv6 support');
common.crashOnUnhandledRejection();
const assert = require('assert');
const dns = require('dns');
const net = require('net');
const dnsPromises = dns.promises;
const isIPv6 = net.isIPv6;
let running = false;
@ -38,49 +41,64 @@ function checkWrap(req) {
assert.ok(typeof req === 'object');
}
TEST(function test_resolve6(done) {
TEST(async function test_resolve6(done) {
function validateResult(res) {
assert.ok(res.length > 0);
for (let i = 0; i < res.length; i++) {
assert.ok(isIPv6(res[i]));
}
}
validateResult(await dnsPromises.resolve6(addresses.INET6_HOST));
const req = dns.resolve6(
addresses.INET6_HOST,
common.mustCall((err, ips) => {
assert.ifError(err);
assert.ok(ips.length > 0);
for (let i = 0; i < ips.length; i++)
assert.ok(isIPv6(ips[i]));
validateResult(ips);
done();
}));
checkWrap(req);
});
TEST(function test_reverse_ipv6(done) {
TEST(async function test_reverse_ipv6(done) {
function validateResult(res) {
assert.ok(res.length > 0);
for (let i = 0; i < res.length; i++) {
assert.ok(typeof res[i] === 'string');
}
}
validateResult(await dnsPromises.reverse(addresses.INET6_IP));
const req = dns.reverse(
addresses.INET6_IP,
common.mustCall((err, domains) => {
assert.ifError(err);
assert.ok(domains.length > 0);
for (let i = 0; i < domains.length; i++)
assert.ok(typeof domains[i] === 'string');
validateResult(domains);
done();
}));
checkWrap(req);
});
TEST(function test_lookup_ipv6_explicit(done) {
TEST(async function test_lookup_ipv6_explicit(done) {
function validateResult(res) {
assert.ok(isIPv6(res.address));
assert.strictEqual(res.family, 6);
}
validateResult(await dnsPromises.lookup(addresses.INET6_HOST, 6));
const req = dns.lookup(
addresses.INET6_HOST,
6,
common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(isIPv6(ip));
assert.strictEqual(family, 6);
validateResult({ address: ip, family });
done();
}));
@ -101,14 +119,19 @@ TEST(function test_lookup_ipv6_implicit(done) {
});
*/
TEST(function test_lookup_ipv6_explicit_object(done) {
TEST(async function test_lookup_ipv6_explicit_object(done) {
function validateResult(res) {
assert.ok(isIPv6(res.address));
assert.strictEqual(res.family, 6);
}
validateResult(await dnsPromises.lookup(addresses.INET6_HOST, { family: 6 }));
const req = dns.lookup(addresses.INET6_HOST, {
family: 6
}, common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(isIPv6(ip));
assert.strictEqual(family, 6);
validateResult({ address: ip, family });
done();
}));
@ -143,35 +166,48 @@ TEST(function test_lookup_ipv6_hint(done) {
checkWrap(req);
});
TEST(function test_lookup_ip_ipv6(done) {
TEST(async function test_lookup_ip_ipv6(done) {
function validateResult(res) {
assert.ok(isIPv6(res.address));
assert.strictEqual(res.family, 6);
}
validateResult(await dnsPromises.lookup('::1'));
const req = dns.lookup(
'::1',
common.mustCall((err, ip, family) => {
assert.ifError(err);
assert.ok(isIPv6(ip));
assert.strictEqual(family, 6);
validateResult({ address: ip, family });
done();
}));
checkWrap(req);
});
TEST(function test_lookup_all_ipv6(done) {
TEST(async function test_lookup_all_ipv6(done) {
function validateResult(res) {
assert.ok(Array.isArray(res));
assert.ok(res.length > 0);
res.forEach((ip) => {
assert.ok(isIPv6(ip.address),
`Invalid IPv6: ${ip.address.toString()}`);
assert.strictEqual(ip.family, 6);
});
}
validateResult(await dnsPromises.lookup(addresses.INET6_HOST, {
all: true,
family: 6
}));
const req = dns.lookup(
addresses.INET6_HOST,
{ all: true, family: 6 },
common.mustCall((err, ips) => {
assert.ifError(err);
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);
ips.forEach((ip) => {
assert.ok(isIPv6(ip.address),
`Invalid IPv6: ${ip.address.toString()}`);
assert.strictEqual(ip.family, 6);
});
validateResult(ips);
done();
})
);

View File

@ -1,7 +1,15 @@
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const dns = require('dns');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
(async function() {
const result = await dnsPromises.resolveTxt('www.microsoft.com');
assert.strictEqual(result.length, 0);
})();
dns.resolveTxt('www.microsoft.com', function(err, records) {
assert.strictEqual(err, null);

View File

@ -28,6 +28,7 @@ const net = require('net');
const isIPv4 = net.isIPv4;
const isIPv6 = net.isIPv6;
const util = require('util');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
@ -68,17 +69,18 @@ function checkWrap(req) {
TEST(function test_reverse_bogus(done) {
dnsPromises.reverse('bogus ip')
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'EINVAL' }));
assert.throws(() => {
dns.reverse('bogus ip', common.mustNotCall());
}, /^Error: getHostByAddr EINVAL bogus ip$/);
done();
});
TEST(function test_resolve4_ttl(done) {
const req = dns.resolve4(addresses.INET4_HOST, {
ttl: true
}, function(err, result) {
assert.ifError(err);
TEST(async function test_resolve4_ttl(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
@ -90,18 +92,25 @@ TEST(function test_resolve4_ttl(done) {
assert.ok(item.ttl > 0);
assert.ok(isIPv4(item.address));
}
}
validateResult(await dnsPromises.resolve4(addresses.INET4_HOST, {
ttl: true
}));
const req = dns.resolve4(addresses.INET4_HOST, {
ttl: true
}, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
checkWrap(req);
});
TEST(function test_resolve6_ttl(done) {
const req = dns.resolve6(addresses.INET6_HOST, {
ttl: true
}, function(err, result) {
assert.ifError(err);
TEST(async function test_resolve6_ttl(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
@ -113,29 +122,42 @@ TEST(function test_resolve6_ttl(done) {
assert.ok(item.ttl > 0);
assert.ok(isIPv6(item.address));
}
}
validateResult(await dnsPromises.resolve6(addresses.INET6_HOST, {
ttl: true
}));
const req = dns.resolve6(addresses.INET6_HOST, {
ttl: true
}, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
checkWrap(req);
});
TEST(function test_resolveMx(done) {
const req = dns.resolveMx(addresses.MX_HOST, function(err, result) {
assert.ifError(err);
TEST(async function test_resolveMx(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
const item = result[i];
assert.ok(item);
assert.strictEqual(typeof item, 'object');
assert.ok(item.exchange);
assert.strictEqual(typeof item.exchange, 'string');
assert.strictEqual(typeof item.priority, 'number');
}
}
validateResult(await dnsPromises.resolveMx(addresses.MX_HOST));
const req = dns.resolveMx(addresses.MX_HOST, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
@ -143,6 +165,10 @@ TEST(function test_resolveMx(done) {
});
TEST(function test_resolveMx_failure(done) {
dnsPromises.resolveMx(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveMx(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -155,17 +181,23 @@ TEST(function test_resolveMx_failure(done) {
checkWrap(req);
});
TEST(function test_resolveNs(done) {
TEST(async function test_resolveNs(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
const item = result[i];
assert.ok(item);
assert.strictEqual(typeof item, 'string');
}
}
validateResult(await dnsPromises.resolveNs(addresses.NS_HOST));
const req = dns.resolveNs(addresses.NS_HOST, function(err, names) {
assert.ifError(err);
assert.ok(names.length > 0);
for (let i = 0; i < names.length; i++) {
const name = names[i];
assert.ok(name);
assert.strictEqual(typeof name, 'string');
}
validateResult(names);
done();
});
@ -173,6 +205,10 @@ TEST(function test_resolveNs(done) {
});
TEST(function test_resolveNs_failure(done) {
dnsPromises.resolveNs(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveNs(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -185,9 +221,8 @@ TEST(function test_resolveNs_failure(done) {
checkWrap(req);
});
TEST(function test_resolveSrv(done) {
const req = dns.resolveSrv(addresses.SRV_HOST, function(err, result) {
assert.ifError(err);
TEST(async function test_resolveSrv(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
@ -202,7 +237,13 @@ TEST(function test_resolveSrv(done) {
assert.strictEqual(typeof item.priority, 'number');
assert.strictEqual(typeof item.weight, 'number');
}
}
validateResult(await dnsPromises.resolveSrv(addresses.SRV_HOST));
const req = dns.resolveSrv(addresses.SRV_HOST, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
@ -210,6 +251,10 @@ TEST(function test_resolveSrv(done) {
});
TEST(function test_resolveSrv_failure(done) {
dnsPromises.resolveSrv(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveSrv(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -222,9 +267,8 @@ TEST(function test_resolveSrv_failure(done) {
checkWrap(req);
});
TEST(function test_resolvePtr(done) {
const req = dns.resolvePtr(addresses.PTR_HOST, function(err, result) {
assert.ifError(err);
TEST(async function test_resolvePtr(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
@ -232,7 +276,13 @@ TEST(function test_resolvePtr(done) {
assert.ok(item);
assert.strictEqual(typeof item, 'string');
}
}
validateResult(await dnsPromises.resolvePtr(addresses.PTR_HOST));
const req = dns.resolvePtr(addresses.PTR_HOST, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
@ -240,6 +290,10 @@ TEST(function test_resolvePtr(done) {
});
TEST(function test_resolvePtr_failure(done) {
dnsPromises.resolvePtr(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolvePtr(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -252,16 +306,14 @@ TEST(function test_resolvePtr_failure(done) {
checkWrap(req);
});
TEST(function test_resolveNaptr(done) {
const req = dns.resolveNaptr(addresses.NAPTR_HOST, function(err, result) {
assert.ifError(err);
TEST(async function test_resolveNaptr(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < result.length; i++) {
const item = result[i];
assert.ok(item);
assert.strictEqual(typeof item, 'object');
assert.strictEqual(typeof item.flags, 'string');
assert.strictEqual(typeof item.service, 'string');
assert.strictEqual(typeof item.regexp, 'string');
@ -269,7 +321,13 @@ TEST(function test_resolveNaptr(done) {
assert.strictEqual(typeof item.order, 'number');
assert.strictEqual(typeof item.preference, 'number');
}
}
validateResult(await dnsPromises.resolveNaptr(addresses.NAPTR_HOST));
const req = dns.resolveNaptr(addresses.NAPTR_HOST, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
@ -277,6 +335,10 @@ TEST(function test_resolveNaptr(done) {
});
TEST(function test_resolveNaptr_failure(done) {
dnsPromises.resolveNaptr(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveNaptr(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -289,33 +351,31 @@ TEST(function test_resolveNaptr_failure(done) {
checkWrap(req);
});
TEST(function test_resolveSoa(done) {
const req = dns.resolveSoa(addresses.SOA_HOST, function(err, result) {
assert.ifError(err);
TEST(async function test_resolveSoa(done) {
function validateResult(result) {
assert.ok(result);
assert.strictEqual(typeof result, 'object');
assert.strictEqual(typeof result.nsname, 'string');
assert.ok(result.nsname.length > 0);
assert.strictEqual(typeof result.hostmaster, 'string');
assert.ok(result.hostmaster.length > 0);
assert.strictEqual(typeof result.serial, 'number');
assert.ok((result.serial > 0) && (result.serial < 4294967295));
assert.strictEqual(typeof result.refresh, 'number');
assert.ok((result.refresh > 0) && (result.refresh < 2147483647));
assert.strictEqual(typeof result.retry, 'number');
assert.ok((result.retry > 0) && (result.retry < 2147483647));
assert.strictEqual(typeof result.expire, 'number');
assert.ok((result.expire > 0) && (result.expire < 2147483647));
assert.strictEqual(typeof result.minttl, 'number');
assert.ok((result.minttl >= 0) && (result.minttl < 2147483647));
}
validateResult(await dnsPromises.resolveSoa(addresses.SOA_HOST));
const req = dns.resolveSoa(addresses.SOA_HOST, function(err, result) {
assert.ifError(err);
validateResult(result);
done();
});
@ -323,6 +383,10 @@ TEST(function test_resolveSoa(done) {
});
TEST(function test_resolveSoa_failure(done) {
dnsPromises.resolveSoa(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveSoa(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -335,17 +399,22 @@ TEST(function test_resolveSoa_failure(done) {
checkWrap(req);
});
TEST(function test_resolveCname(done) {
const req = dns.resolveCname(addresses.CNAME_HOST, function(err, names) {
assert.ifError(err);
assert.ok(names.length > 0);
TEST(async function test_resolveCname(done) {
function validateResult(result) {
assert.ok(result.length > 0);
for (let i = 0; i < names.length; i++) {
const name = names[i];
for (let i = 0; i < result.length; i++) {
const name = result[i];
assert.ok(name);
assert.strictEqual(typeof name, 'string');
}
}
validateResult(await dnsPromises.resolveCname(addresses.CNAME_HOST));
const req = dns.resolveCname(addresses.CNAME_HOST, function(err, names) {
assert.ifError(err);
validateResult(names);
done();
});
@ -353,6 +422,10 @@ TEST(function test_resolveCname(done) {
});
TEST(function test_resolveCname_failure(done) {
dnsPromises.resolveCname(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveCname(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -366,12 +439,18 @@ TEST(function test_resolveCname_failure(done) {
});
TEST(function test_resolveTxt(done) {
TEST(async function test_resolveTxt(done) {
function validateResult(result) {
assert.ok(Array.isArray(result[0]));
assert.strictEqual(result.length, 1);
assert(result[0][0].startsWith('v=spf1'));
}
validateResult(await dnsPromises.resolveTxt(addresses.TXT_HOST));
const req = dns.resolveTxt(addresses.TXT_HOST, function(err, records) {
assert.ifError(err);
assert.strictEqual(records.length, 1);
assert.ok(util.isArray(records[0]));
assert(records[0][0].startsWith('v=spf1'));
validateResult(records);
done();
});
@ -379,6 +458,10 @@ TEST(function test_resolveTxt(done) {
});
TEST(function test_resolveTxt_failure(done) {
dnsPromises.resolveTxt(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: 'ENOTFOUND' }));
const req = dns.resolveTxt(addresses.INVALID_HOST, function(err, result) {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, 'ENOTFOUND');
@ -393,6 +476,10 @@ TEST(function test_resolveTxt_failure(done) {
TEST(function test_lookup_failure(done) {
dnsPromises.lookup(addresses.INVALID_HOST, 4)
.then(common.mustNotCall())
.catch(common.expectsError({ errno: dns.NOTFOUND }));
const req = dns.lookup(addresses.INVALID_HOST, 4, (err, ip, family) => {
assert.ok(err instanceof Error);
assert.strictEqual(err.errno, dns.NOTFOUND);
@ -407,17 +494,23 @@ TEST(function test_lookup_failure(done) {
});
TEST(function test_lookup_ip_all(done) {
TEST(async function test_lookup_ip_all(done) {
function validateResult(result) {
assert.ok(Array.isArray(result));
assert.ok(result.length > 0);
assert.strictEqual(result[0].address, '127.0.0.1');
assert.strictEqual(result[0].family, 4);
}
validateResult(await dnsPromises.lookup('127.0.0.1', { all: true }));
const req = dns.lookup(
'127.0.0.1',
{ all: true },
function(err, ips, family) {
assert.ifError(err);
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);
assert.strictEqual(ips[0].address, '127.0.0.1');
assert.strictEqual(ips[0].family, 4);
assert.strictEqual(family, undefined);
validateResult(ips);
done();
}
);
@ -452,7 +545,9 @@ TEST(function test_lookup_ip_promise(done) {
});
TEST(function test_lookup_null_all(done) {
TEST(async function test_lookup_null_all(done) {
assert.deepStrictEqual(await dnsPromises.lookup(null, { all: true }), []);
const req = dns.lookup(null, { all: true }, function(err, ips, family) {
assert.ifError(err);
assert.ok(Array.isArray(ips));
@ -465,15 +560,12 @@ TEST(function test_lookup_null_all(done) {
});
TEST(function test_lookup_all_mixed(done) {
const req = dns.lookup(addresses.INET_HOST, {
all: true
}, function(err, ips) {
assert.ifError(err);
assert.ok(Array.isArray(ips));
assert.ok(ips.length > 0);
TEST(async function test_lookup_all_mixed(done) {
function validateResult(result) {
assert.ok(Array.isArray(result));
assert.ok(result.length > 0);
ips.forEach(function(ip) {
result.forEach(function(ip) {
if (isIPv4(ip.address))
assert.strictEqual(ip.family, 4);
else if (isIPv6(ip.address))
@ -481,7 +573,15 @@ TEST(function test_lookup_all_mixed(done) {
else
assert.fail('unexpected IP address');
});
}
validateResult(await dnsPromises.lookup(addresses.INET_HOST, { all: true }));
const req = dns.lookup(addresses.INET_HOST, {
all: true
}, function(err, ips) {
assert.ifError(err);
validateResult(ips);
done();
});
@ -490,6 +590,10 @@ TEST(function test_lookup_all_mixed(done) {
TEST(function test_lookupservice_invalid(done) {
dnsPromises.lookupService('1.2.3.4', 80)
.then(common.mustNotCall())
.catch(common.expectsError({ code: 'ENOTFOUND' }));
const req = dns.lookupService('1.2.3.4', 80, function(err, host, service) {
assert(err instanceof Error);
assert.strictEqual(err.code, 'ENOTFOUND');
@ -503,6 +607,13 @@ TEST(function test_lookupservice_invalid(done) {
TEST(function test_reverse_failure(done) {
dnsPromises.reverse('203.0.113.0')
.then(common.mustNotCall())
.catch(common.expectsError({
code: 'ENOTFOUND',
hostname: '203.0.113.0'
}));
// 203.0.113.0/24 are addresses reserved for (RFC) documentation use only
const req = dns.reverse('203.0.113.0', function(err) {
assert(err instanceof Error);
@ -518,6 +629,13 @@ TEST(function test_reverse_failure(done) {
TEST(function test_lookup_failure(done) {
dnsPromises.lookup(addresses.INVALID_HOST)
.then(common.mustNotCall())
.catch(common.expectsError({
code: 'ENOTFOUND',
hostname: addresses.INVALID_HOST
}));
const req = dns.lookup(addresses.INVALID_HOST, (err) => {
assert(err instanceof Error);
assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code...
@ -584,3 +702,7 @@ dns.lookup(addresses.INET6_HOST, 6, common.mustCall());
dns.lookup(addresses.INET_HOST, {}, common.mustCall());
dns.lookupService('0.0.0.0', '0', common.mustCall());
dns.lookupService('0.0.0.0', 0, common.mustCall());
(async function() {
await dnsPromises.lookup(addresses.INET6_HOST, 6);
await dnsPromises.lookup(addresses.INET_HOST, {});
})();

View File

@ -23,8 +23,26 @@
const common = require('../common');
const assert = require('assert');
const dns = require('dns');
common.crashOnUnhandledRejection();
const dns = require('dns');
const dnsPromises = dns.promises;
(async function() {
let res;
res = await dnsPromises.lookup(null);
assert.strictEqual(res.address, null);
assert.strictEqual(res.family, 4);
res = await dnsPromises.lookup('127.0.0.1');
assert.strictEqual(res.address, '127.0.0.1');
assert.strictEqual(res.family, 4);
res = await dnsPromises.lookup('::1');
assert.strictEqual(res.address, '::1');
assert.strictEqual(res.family, 6);
})();
// Try resolution without callback
@ -52,14 +70,18 @@ dns.lookup('::1', common.mustCall((error, result, addressType) => {
// Try calling resolve with an unsupported type that's an object key
'toString'
].forEach((val) => {
const err = {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: `The value "${val}" is invalid for option "rrtype"`
};
common.expectsError(
() => dns.resolve('www.google.com', val),
{
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: `The value "${val}" is invalid for option "rrtype"`
}
err
);
common.expectsError(() => dnsPromises.resolve('www.google.com', val), err);
});
// Windows doesn't usually have an entry for localhost 127.0.0.1 in
@ -70,4 +92,8 @@ if (!common.isWindows) {
assert.ifError(error);
assert.ok(Array.isArray(domains));
}));
(async function() {
assert.ok(Array.isArray(await dnsPromises.reverse('127.0.0.1')));
})();
}

View File

@ -3,17 +3,23 @@ const common = require('../common');
const assert = require('assert');
const cares = process.binding('cares_wrap');
const dns = require('dns');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
// Stub `getaddrinfo` to *always* error.
cares.getaddrinfo = () => process.binding('uv').UV_ENOENT;
common.expectsError(() => {
dns.lookup(1, {});
}, {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: /^The "hostname" argument must be one of type string or falsy/
});
{
const err = {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: /^The "hostname" argument must be one of type string or falsy/
};
common.expectsError(() => dns.lookup(1, {}), err);
common.expectsError(() => dnsPromises.lookup(1, {}), err);
}
common.expectsError(() => {
dns.lookup(false, 'cb');
@ -29,29 +35,66 @@ common.expectsError(() => {
type: TypeError
});
common.expectsError(() => {
dns.lookup(false, {
{
const err = {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: 'The value "100" is invalid for option "hints"'
};
const options = {
hints: 100,
family: 0,
all: false
}, common.mustNotCall());
}, {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: 'The value "100" is invalid for option "hints"'
});
};
common.expectsError(() => {
dns.lookup(false, {
common.expectsError(() => { dnsPromises.lookup(false, options); }, err);
common.expectsError(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
}
{
const err = {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: 'The value "20" is invalid for option "family"'
};
const options = {
hints: 0,
family: 20,
all: false
}, common.mustNotCall());
}, {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: 'The value "20" is invalid for option "family"'
});
};
common.expectsError(() => { dnsPromises.lookup(false, options); }, err);
common.expectsError(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
}
(async function() {
let res;
res = await dnsPromises.lookup(false, {
hints: 0,
family: 0,
all: true
});
assert.deepStrictEqual(res, []);
res = await dnsPromises.lookup('127.0.0.1', {
hints: 0,
family: 4,
all: true
});
assert.deepStrictEqual(res, [{ address: '127.0.0.1', family: 4 }]);
res = await dnsPromises.lookup('127.0.0.1', {
hints: 0,
family: 4,
all: false
});
assert.deepStrictEqual(res, { address: '127.0.0.1', family: 4 });
})();
dns.lookup(false, {
hints: 0,

View File

@ -4,6 +4,9 @@ const dnstools = require('../common/dns');
const dns = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
const server = dgram.createSocket('udp4');
@ -20,12 +23,20 @@ server.on('message', common.mustCall((msg, { address, port }) => {
// Overwrite the # of answers with 2, which is incorrect.
buf.writeUInt16LE(2, 6);
server.send(buf, port, address);
}));
}, 2));
server.bind(0, common.mustCall(() => {
server.bind(0, common.mustCall(async () => {
const address = server.address();
dns.setServers([`127.0.0.1:${address.port}`]);
dnsPromises.resolveAny('example.org')
.then(common.mustNotCall())
.catch(common.expectsError({
code: 'EBADRESP',
syscall: 'queryAny',
hostname: 'example.org'
}));
dns.resolveAny('example.org', common.mustCall((err) => {
assert.strictEqual(err.code, 'EBADRESP');
assert.strictEqual(err.syscall, 'queryAny');

View File

@ -4,6 +4,9 @@ const dnstools = require('../common/dns');
const dns = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
const answers = [
{ type: 'A', address: '1.2.3.4', ttl: 123 },
@ -36,18 +39,24 @@ server.on('message', common.mustCall((msg, { address, port }) => {
questions: parsed.questions,
answers: answers.map((answer) => Object.assign({ domain }, answer)),
}), port, address);
}));
}, 2));
server.bind(0, common.mustCall(() => {
server.bind(0, common.mustCall(async () => {
const address = server.address();
dns.setServers([`127.0.0.1:${address.port}`]);
validateResults(await dnsPromises.resolveAny('example.org'));
dns.resolveAny('example.org', common.mustCall((err, res) => {
assert.ifError(err);
// Compare copies with ttl removed, c-ares fiddles with that value.
assert.deepStrictEqual(
res.map((r) => Object.assign({}, r, { ttl: null })),
answers.map((r) => Object.assign({}, r, { ttl: null })));
validateResults(res);
server.close();
}));
}));
function validateResults(res) {
// Compare copies with ttl removed, c-ares fiddles with that value.
assert.deepStrictEqual(
res.map((r) => Object.assign({}, r, { ttl: null })),
answers.map((r) => Object.assign({}, r, { ttl: null })));
}

View File

@ -27,7 +27,18 @@ const common = require('../common');
// Issue https://github.com/nodejs/node-v0.x-archive/issues/7070
const dns = require('dns');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
common.expectsError(
() => dnsPromises.resolveNs([]), // bad name
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: /^The "name" argument must be of type string/
}
);
common.expectsError(
() => dns.resolveNs([]), // bad name
{

View File

@ -24,6 +24,9 @@ const common = require('../common');
const assert = require('assert');
const dns = require('dns');
const dnsPromises = dns.promises;
common.crashOnUnhandledRejection();
const existing = dns.getServers();
assert(existing.length > 0);
@ -149,7 +152,7 @@ common.expectsError(() => {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: /^The "hostname" argument must be one of type string or falsy/
}, 5);
}, 10);
assert.throws(() => dns.lookup({}, common.mustNotCall()), errorReg);
@ -161,6 +164,12 @@ common.expectsError(() => {
assert.throws(() => dns.lookup(common.mustNotCall(), common.mustNotCall()),
errorReg);
assert.throws(() => dnsPromises.lookup({}), errorReg);
assert.throws(() => dnsPromises.lookup([]), errorReg);
assert.throws(() => dnsPromises.lookup(true), errorReg);
assert.throws(() => dnsPromises.lookup(1), errorReg);
assert.throws(() => dnsPromises.lookup(common.mustNotCall()), errorReg);
}
// dns.lookup should accept falsey values
@ -171,30 +180,37 @@ common.expectsError(() => {
assert.strictEqual(family, 4);
};
dns.lookup('', common.mustCall(checkCallback));
dns.lookup(null, common.mustCall(checkCallback));
dns.lookup(undefined, common.mustCall(checkCallback));
dns.lookup(0, common.mustCall(checkCallback));
dns.lookup(NaN, common.mustCall(checkCallback));
['', null, undefined, 0, NaN].forEach(async (value) => {
const res = await dnsPromises.lookup(value);
assert.deepStrictEqual(res, { address: null, family: 4 });
dns.lookup(value, common.mustCall(checkCallback));
});
}
/*
* Make sure that dns.lookup throws if hints does not represent a valid flag.
* (dns.V4MAPPED | dns.ADDRCONFIG) + 1 is invalid because:
* - it's different from dns.V4MAPPED and dns.ADDRCONFIG.
* - it's different from them bitwise ored.
* - it's different from 0.
* - it's an odd number different than 1, and thus is invalid, because
* flags are either === 1 or even.
*/
common.expectsError(() => {
dns.lookup('nodejs.org', { hints: (dns.V4MAPPED | dns.ADDRCONFIG) + 1 },
common.mustNotCall());
}, {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: /The value "\d+" is invalid for option "hints"/
});
{
/*
* Make sure that dns.lookup throws if hints does not represent a valid flag.
* (dns.V4MAPPED | dns.ADDRCONFIG) + 1 is invalid because:
* - it's different from dns.V4MAPPED and dns.ADDRCONFIG.
* - it's different from them bitwise ored.
* - it's different from 0.
* - it's an odd number different than 1, and thus is invalid, because
* flags are either === 1 or even.
*/
const hints = (dns.V4MAPPED | dns.ADDRCONFIG) + 1;
const err = {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: /The value "\d+" is invalid for option "hints"/
};
common.expectsError(() => {
dnsPromises.lookup('nodejs.org', { hints });
}, err);
common.expectsError(() => {
dns.lookup('nodejs.org', { hints }, common.mustNotCall());
}, err);
}
common.expectsError(() => dns.lookup('nodejs.org'), {
code: 'ERR_INVALID_CALLBACK',
@ -219,33 +235,57 @@ dns.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED
}, common.mustCall());
common.expectsError(() => dns.lookupService('0.0.0.0'), {
code: 'ERR_MISSING_ARGS',
type: TypeError,
message: 'The "host", "port", and "callback" arguments must be specified'
});
(async function() {
await dnsPromises.lookup('', { family: 4, hints: 0 });
await dnsPromises.lookup('', { family: 6, hints: dns.ADDRCONFIG });
await dnsPromises.lookup('', { hints: dns.V4MAPPED });
await dnsPromises.lookup('', { hints: dns.ADDRCONFIG | dns.V4MAPPED });
})();
const invalidHost = 'fasdfdsaf';
common.expectsError(() => {
dns.lookupService(invalidHost, 0, common.mustNotCall());
}, {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: `The value "${invalidHost}" is invalid for option "host"`
});
{
const err = {
code: 'ERR_MISSING_ARGS',
type: TypeError,
message: 'The "host", "port", and "callback" arguments must be specified'
};
common.expectsError(() => dns.lookupService('0.0.0.0'), err);
err.message = 'The "host" and "port" arguments must be specified';
common.expectsError(() => dnsPromises.lookupService('0.0.0.0'), err);
}
{
const invalidHost = 'fasdfdsaf';
const err = {
code: 'ERR_INVALID_OPT_VALUE',
type: TypeError,
message: `The value "${invalidHost}" is invalid for option "host"`
};
common.expectsError(() => {
dnsPromises.lookupService(invalidHost, 0);
}, err);
common.expectsError(() => {
dns.lookupService(invalidHost, 0, common.mustNotCall());
}, err);
}
const portErr = (port) => {
common.expectsError(
() => {
dns.lookupService('0.0.0.0', port, common.mustNotCall());
},
{
code: 'ERR_SOCKET_BAD_PORT',
message:
`Port should be > 0 and < 65536. Received ${port}.`,
type: RangeError
}
);
const err = {
code: 'ERR_SOCKET_BAD_PORT',
message:
`Port should be > 0 and < 65536. Received ${port}.`,
type: RangeError
};
common.expectsError(() => {
dnsPromises.lookupService('0.0.0.0', port);
}, err);
common.expectsError(() => {
dns.lookupService('0.0.0.0', port, common.mustNotCall());
}, err);
};
portErr(null);
portErr(undefined);