Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dns: enable managing independent channels in JS #14518

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions doc/api/dns.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,55 @@ dns.resolve4('archive.org', (err, addresses) => {
There are subtle consequences in choosing one over the other, please consult
the [Implementation considerations section][] for more information.

## Class dns.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()`][`dns.setServers()`] does not affect
other resolver:

```js
const { Resolver } = require('dns');
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', (err, addresses) => {
// ...
});
```

The following methods from the `dns` module are available:

* [`resolver.getServers()`][`dns.getServers()`]
* [`resolver.setServers()`][`dns.setServers()`]
* [`resolver.resolve()`][`dns.resolve()`]
* [`resolver.resolve4()`][`dns.resolve4()`]
* [`resolver.resolve6()`][`dns.resolve6()`]
* [`resolver.resolveAny()`][`dns.resolveAny()`]
* [`resolver.resolveCname()`][`dns.resolveCname()`]
* [`resolver.resolveMx()`][`dns.resolveMx()`]
* [`resolver.resolveNaptr()`][`dns.resolveNaptr()`]
* [`resolver.resolveNs()`][`dns.resolveNs()`]
* [`resolver.resolvePtr()`][`dns.resolvePtr()`]
* [`resolver.resolveSoa()`][`dns.resolveSoa()`]
* [`resolver.resolveSrv()`][`dns.resolveSrv()`]
* [`resolver.resolveTxt()`][`dns.resolveTxt()`]
* [`resolver.reverse()`][`dns.reverse()`]

### resolver.cancel()
<!-- YAML
added: REPLACEME
-->

Cancel all outstanding DNS queries made by this resolver. The corresponding
callbacks will be called with an error with code `ECANCELLED`.

## dns.getServers()
<!-- YAML
added: v0.11.3
Expand Down Expand Up @@ -590,6 +639,7 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.

[`Error`]: errors.html#errors_class_error
[`dns.lookup()`]: #dns_dns_lookup_hostname_options_callback
[`dns.resolve()`]: #dns_dns_resolve_hostname_rrtype_callback
[`dns.resolve4()`]: #dns_dns_resolve4_hostname_options_callback
[`dns.resolve6()`]: #dns_dns_resolve6_hostname_options_callback
[`dns.resolveCname()`]: #dns_dns_resolvecname_hostname_callback
Expand All @@ -601,6 +651,9 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
[`dns.resolveSrv()`]: #dns_dns_resolvesrv_hostname_callback
[`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback
[`dns.resolveAny()`]: #dns_dns_resolveany_hostname_callback
[`dns.getServers()`]: #dns_dns_getservers
[`dns.setServers()`]: #dns_dns_setservers_servers
[`dns.reverse()`]: #dns_dns_reverse_ip_callback
[DNS error codes]: #dns_error_codes
[Implementation considerations section]: #dns_implementation_considerations
[supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags
Expand Down
104 changes: 62 additions & 42 deletions lib/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ const internalNet = require('internal/net');
const { customPromisifyArgs } = require('internal/util');
const errors = require('internal/errors');

const GetAddrInfoReqWrap = cares.GetAddrInfoReqWrap;
const GetNameInfoReqWrap = cares.GetNameInfoReqWrap;
const QueryReqWrap = cares.QueryReqWrap;
const {
GetAddrInfoReqWrap,
GetNameInfoReqWrap,
QueryReqWrap,
ChannelWrap,
isIP
} = cares;

const isIP = cares.isIP;
const isLegalPort = internalNet.isLegalPort;


Expand Down Expand Up @@ -239,11 +242,19 @@ 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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about hide _handle via Object.defineProperty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a plain _handle property is kind of the standard in Node core. If we were to hide something, I’d suggest thinking of a meaningful custom util.inspect overload for resolvers.

}

function resolver(bindingName) {
var binding = cares[bindingName];
cancel() {
this._handle.cancel();
}
}

return function query(name, /* options, */ callback) {
function resolver(bindingName) {
function query(name, /* options, */ callback) {
var options;
if (arguments.length > 2) {
options = callback;
Expand All @@ -263,26 +274,29 @@ function resolver(bindingName) {
req.hostname = name;
req.oncomplete = onresolve;
req.ttl = !!(options && options.ttl);
var err = binding(req, name);
var err = this._handle[bindingName](req, name);
if (err) throw errnoException(err, bindingName);
return req;
};
}
Object.defineProperty(query, 'name', { value: bindingName });
return query;
}


var resolveMap = Object.create(null);
resolveMap.ANY = resolver('queryAny');
resolveMap.A = resolver('queryA');
resolveMap.AAAA = resolver('queryAaaa');
resolveMap.CNAME = resolver('queryCname');
resolveMap.MX = resolver('queryMx');
resolveMap.NS = resolver('queryNs');
resolveMap.TXT = resolver('queryTxt');
resolveMap.SRV = resolver('querySrv');
resolveMap.PTR = resolver('queryPtr');
resolveMap.NAPTR = resolver('queryNaptr');
resolveMap.SOA = resolver('querySoa');

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 = resolve;

function resolve(hostname, rrtype, callback) {
var resolver;
Expand All @@ -297,15 +311,16 @@ function resolve(hostname, rrtype, callback) {
}

if (typeof resolver === 'function') {
return resolver(hostname, callback);
return resolver.call(this, hostname, callback);
} else {
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'rrtype', rrtype);
}
}


Resolver.prototype.getServers = getServers;
function getServers() {
const ret = cares.getServers();
const ret = this._handle.getServers();
return ret.map((val) => {
if (!val[1] || val[1] === IANA_DNS_PORT) return val[0];

Expand All @@ -315,10 +330,11 @@ function getServers() {
}


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 = cares.getServers();
const orig = this._handle.getServers();
const newSet = [];
const IPv6RE = /\[(.*)\]/;
const addrSplitRE = /(^.+?)(?::(\d+))?$/;
Expand Down Expand Up @@ -350,35 +366,39 @@ function setServers(servers) {
throw new errors.Error('ERR_INVALID_IP_ADDRESS', serv);
});

const errorNumber = cares.setServers(newSet);
const errorNumber = this._handle.setServers(newSet);

if (errorNumber !== 0) {
// reset the servers to the old servers, because ares probably unset them
cares.setServers(orig.join(','));
this._handle.setServers(orig.join(','));

var err = cares.strerror(errorNumber);
throw new errors.Error('ERR_DNS_SET_SERVERS_FAILED', err, servers);
}
}

const defaultResolver = new Resolver();

module.exports = {
lookup,
lookupService,
getServers,
setServers,
resolve,
resolveAny: resolveMap.ANY,
resolve4: resolveMap.A,
resolve6: resolveMap.AAAA,
resolveCname: resolveMap.CNAME,
resolveMx: resolveMap.MX,
resolveNs: resolveMap.NS,
resolveTxt: resolveMap.TXT,
resolveSrv: resolveMap.SRV,
resolvePtr: resolveMap.PTR,
resolveNaptr: resolveMap.NAPTR,
resolveSoa: resolveMap.SOA,
reverse: resolver('getHostByAddr'),

Resolver,
getServers: defaultResolver.getServers.bind(defaultResolver),
setServers: defaultResolver.setServers.bind(defaultResolver),
resolve: defaultResolver.resolve.bind(defaultResolver),
resolveAny: defaultResolver.resolveAny.bind(defaultResolver),
resolve4: defaultResolver.resolve4.bind(defaultResolver),
resolve6: defaultResolver.resolve6.bind(defaultResolver),
resolveCname: defaultResolver.resolveCname.bind(defaultResolver),
resolveMx: defaultResolver.resolveMx.bind(defaultResolver),
resolveNs: defaultResolver.resolveNs.bind(defaultResolver),
resolveTxt: defaultResolver.resolveTxt.bind(defaultResolver),
resolveSrv: defaultResolver.resolveSrv.bind(defaultResolver),
resolvePtr: defaultResolver.resolvePtr.bind(defaultResolver),
resolveNaptr: defaultResolver.resolveNaptr.bind(defaultResolver),
resolveSoa: defaultResolver.resolveSoa.bind(defaultResolver),
reverse: defaultResolver.reverse.bind(defaultResolver),

// uv_getaddrinfo flags
ADDRCONFIG: cares.AI_ADDRCONFIG,
Expand Down
1 change: 1 addition & 0 deletions src/async-wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace node {

#define NODE_ASYNC_NON_CRYPTO_PROVIDER_TYPES(V) \
V(NONE) \
V(DNSCHANNEL) \
V(FSEVENTWRAP) \
V(FSREQWRAP) \
V(GETADDRINFOREQWRAP) \
Expand Down
Loading