| 'use strict'; |
| |
| const { |
| ArrayPrototypeForEach, |
| ArrayPrototypeJoin, |
| ArrayPrototypeMap, |
| ArrayPrototypePush, |
| FunctionPrototypeBind, |
| NumberParseInt, |
| RegExpPrototypeExec, |
| RegExpPrototypeSymbolReplace, |
| } = primordials; |
| |
| const errors = require('internal/errors'); |
| const { isIP } = require('internal/net'); |
| const { getOptionValue } = require('internal/options'); |
| const { |
| validateArray, |
| validateInt32, |
| validateOneOf, |
| validateString, |
| } = require('internal/validators'); |
| const { |
| ChannelWrap, |
| strerror, |
| AI_ADDRCONFIG, |
| AI_ALL, |
| AI_V4MAPPED, |
| } = internalBinding('cares_wrap'); |
| const IANA_DNS_PORT = 53; |
| const IPv6RE = /^\[([^[\]]*)\]/; |
| const addrSplitRE = /(^.+?)(?::(\d+))?$/; |
| const { |
| ERR_DNS_SET_SERVERS_FAILED, |
| ERR_INVALID_ARG_VALUE, |
| ERR_INVALID_IP_ADDRESS, |
| } = errors.codes; |
| |
| function validateTimeout(options) { |
| const { timeout = -1 } = { ...options }; |
| validateInt32(timeout, 'options.timeout', -1); |
| return timeout; |
| } |
| |
| function validateTries(options) { |
| const { tries = 4 } = { ...options }; |
| validateInt32(tries, 'options.tries', 1); |
| return tries; |
| } |
| |
| // Resolver instances correspond 1:1 to c-ares channels. |
| class Resolver { |
| constructor(options = undefined) { |
| const timeout = validateTimeout(options); |
| const tries = validateTries(options); |
| this._handle = new ChannelWrap(timeout, tries); |
| } |
| |
| cancel() { |
| this._handle.cancel(); |
| } |
| |
| getServers() { |
| return ArrayPrototypeMap(this._handle.getServers() || [], (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) { |
| validateArray(servers, '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 = []; |
| |
| ArrayPrototypeForEach(servers, (serv, index) => { |
| validateString(serv, `servers[${index}]`); |
| let ipVersion = isIP(serv); |
| |
| if (ipVersion !== 0) |
| return ArrayPrototypePush(newSet, [ipVersion, serv, IANA_DNS_PORT]); |
| |
| const match = RegExpPrototypeExec(IPv6RE, serv); |
| |
| // Check for an IPv6 in brackets. |
| if (match) { |
| ipVersion = isIP(match[1]); |
| |
| if (ipVersion !== 0) { |
| const port = NumberParseInt( |
| RegExpPrototypeSymbolReplace(addrSplitRE, serv, '$2')) || IANA_DNS_PORT; |
| return ArrayPrototypePush(newSet, [ipVersion, match[1], port]); |
| } |
| } |
| |
| // addr::port |
| const addrSplitMatch = RegExpPrototypeExec(addrSplitRE, serv); |
| |
| if (addrSplitMatch) { |
| const hostIP = addrSplitMatch[1]; |
| const port = addrSplitMatch[2] || IANA_DNS_PORT; |
| |
| ipVersion = isIP(hostIP); |
| |
| if (ipVersion !== 0) { |
| return ArrayPrototypePush( |
| newSet, [ipVersion, hostIP, NumberParseInt(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(ArrayPrototypeJoin(orig, ',')); |
| const err = strerror(errorNumber); |
| throw new ERR_DNS_SET_SERVERS_FAILED(err, servers); |
| } |
| } |
| |
| setLocalAddress(ipv4, ipv6) { |
| validateString(ipv4, 'ipv4'); |
| |
| if (ipv6 !== undefined) { |
| validateString(ipv6, 'ipv6'); |
| } |
| |
| this._handle.setLocalAddress(ipv4, ipv6); |
| } |
| } |
| |
| let defaultResolver = new Resolver(); |
| const resolverKeys = [ |
| 'getServers', |
| 'resolve', |
| 'resolve4', |
| 'resolve6', |
| 'resolveAny', |
| 'resolveCaa', |
| 'resolveCname', |
| 'resolveMx', |
| 'resolveNaptr', |
| 'resolveNs', |
| 'resolvePtr', |
| 'resolveSoa', |
| 'resolveSrv', |
| 'resolveTxt', |
| 'reverse', |
| ]; |
| |
| function getDefaultResolver() { |
| return defaultResolver; |
| } |
| |
| function setDefaultResolver(resolver) { |
| defaultResolver = resolver; |
| } |
| |
| function bindDefaultResolver(target, source) { |
| ArrayPrototypeForEach(resolverKeys, (key) => { |
| target[key] = FunctionPrototypeBind(source[key], defaultResolver); |
| }); |
| } |
| |
| function validateHints(hints) { |
| if ((hints & ~(AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED)) !== 0) { |
| throw new ERR_INVALID_ARG_VALUE('hints', hints); |
| } |
| } |
| |
| let invalidHostnameWarningEmitted = false; |
| function emitInvalidHostnameWarning(hostname) { |
| if (!invalidHostnameWarningEmitted) { |
| process.emitWarning( |
| `The provided hostname "${hostname}" is not a valid ` + |
| 'hostname, and is supported in the dns module solely for compatibility.', |
| 'DeprecationWarning', |
| 'DEP0118' |
| ); |
| invalidHostnameWarningEmitted = true; |
| } |
| } |
| |
| let dnsOrder = getOptionValue('--dns-result-order') || 'verbatim'; |
| |
| function getDefaultVerbatim() { |
| return dnsOrder !== 'ipv4first'; |
| } |
| |
| function setDefaultResultOrder(value) { |
| validateOneOf(value, 'dnsOrder', ['verbatim', 'ipv4first']); |
| dnsOrder = value; |
| } |
| |
| // ERROR CODES |
| const errorCodes = { |
| NODATA: 'ENODATA', |
| FORMERR: 'EFORMERR', |
| SERVFAIL: 'ESERVFAIL', |
| NOTFOUND: 'ENOTFOUND', |
| NOTIMP: 'ENOTIMP', |
| REFUSED: 'EREFUSED', |
| BADQUERY: 'EBADQUERY', |
| BADNAME: 'EBADNAME', |
| BADFAMILY: 'EBADFAMILY', |
| BADRESP: 'EBADRESP', |
| CONNREFUSED: 'ECONNREFUSED', |
| TIMEOUT: 'ETIMEOUT', |
| EOF: 'EOF', |
| FILE: 'EFILE', |
| NOMEM: 'ENOMEM', |
| DESTRUCTION: 'EDESTRUCTION', |
| BADSTR: 'EBADSTR', |
| BADFLAGS: 'EBADFLAGS', |
| NONAME: 'ENONAME', |
| BADHINTS: 'EBADHINTS', |
| NOTINITIALIZED: 'ENOTINITIALIZED', |
| LOADIPHLPAPI: 'ELOADIPHLPAPI', |
| ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS', |
| CANCELLED: 'ECANCELLED', |
| }; |
| |
| module.exports = { |
| bindDefaultResolver, |
| getDefaultResolver, |
| setDefaultResolver, |
| validateHints, |
| validateTimeout, |
| validateTries, |
| Resolver, |
| emitInvalidHostnameWarning, |
| getDefaultVerbatim, |
| setDefaultResultOrder, |
| errorCodes, |
| }; |