1'use strict'; 2 3const { 4 ArrayIsArray, 5 ArrayPrototypeForEach, 6 ArrayPrototypeJoin, 7 ArrayPrototypeMap, 8 ArrayPrototypePush, 9 FunctionPrototypeBind, 10 NumberParseInt, 11 StringPrototypeMatch, 12 StringPrototypeReplace, 13} = primordials; 14 15const errors = require('internal/errors'); 16const { isIP } = require('internal/net'); 17const { getOptionValue } = require('internal/options'); 18const { 19 validateInt32, 20 validateOneOf, 21 validateString, 22} = require('internal/validators'); 23const { 24 ChannelWrap, 25 strerror, 26 AI_ADDRCONFIG, 27 AI_ALL, 28 AI_V4MAPPED, 29} = internalBinding('cares_wrap'); 30const IANA_DNS_PORT = 53; 31const IPv6RE = /^\[([^[\]]*)\]/; 32const addrSplitRE = /(^.+?)(?::(\d+))?$/; 33const { 34 ERR_DNS_SET_SERVERS_FAILED, 35 ERR_INVALID_ARG_TYPE, 36 ERR_INVALID_IP_ADDRESS, 37 ERR_INVALID_OPT_VALUE 38} = errors.codes; 39 40function validateTimeout(options) { 41 const { timeout = -1 } = { ...options }; 42 validateInt32(timeout, 'options.timeout', -1, 2 ** 31 - 1); 43 return timeout; 44} 45 46function validateTries(options) { 47 const { tries = 4 } = { ...options }; 48 validateInt32(tries, 'options.tries', 1, 2 ** 31 - 1); 49 return tries; 50} 51 52// Resolver instances correspond 1:1 to c-ares channels. 53class Resolver { 54 constructor(options = undefined) { 55 const timeout = validateTimeout(options); 56 const tries = validateTries(options); 57 this._handle = new ChannelWrap(timeout, tries); 58 } 59 60 cancel() { 61 this._handle.cancel(); 62 } 63 64 getServers() { 65 return ArrayPrototypeMap(this._handle.getServers(), (val) => { 66 if (!val[1] || val[1] === IANA_DNS_PORT) 67 return val[0]; 68 69 const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0]; 70 return `${host}:${val[1]}`; 71 }); 72 } 73 74 setServers(servers) { 75 if (!ArrayIsArray(servers)) { 76 throw new ERR_INVALID_ARG_TYPE('servers', 'Array', servers); 77 } 78 79 // Cache the original servers because in the event of an error while 80 // setting the servers, c-ares won't have any servers available for 81 // resolution. 82 const orig = this._handle.getServers(); 83 const newSet = []; 84 85 ArrayPrototypeForEach(servers, (serv, index) => { 86 validateString(serv, `servers[${index}]`); 87 let ipVersion = isIP(serv); 88 89 if (ipVersion !== 0) 90 return ArrayPrototypePush(newSet, [ipVersion, serv, IANA_DNS_PORT]); 91 92 const match = StringPrototypeMatch(serv, IPv6RE); 93 94 // Check for an IPv6 in brackets. 95 if (match) { 96 ipVersion = isIP(match[1]); 97 98 if (ipVersion !== 0) { 99 const port = NumberParseInt( 100 StringPrototypeReplace(serv, addrSplitRE, '$2')) || IANA_DNS_PORT; 101 return ArrayPrototypePush(newSet, [ipVersion, match[1], port]); 102 } 103 } 104 105 // addr::port 106 const addrSplitMatch = StringPrototypeMatch(serv, addrSplitRE); 107 108 if (addrSplitMatch) { 109 const hostIP = addrSplitMatch[1]; 110 const port = addrSplitMatch[2] || IANA_DNS_PORT; 111 112 ipVersion = isIP(hostIP); 113 114 if (ipVersion !== 0) { 115 return ArrayPrototypePush( 116 newSet, [ipVersion, hostIP, NumberParseInt(port)]); 117 } 118 } 119 120 throw new ERR_INVALID_IP_ADDRESS(serv); 121 }); 122 123 const errorNumber = this._handle.setServers(newSet); 124 125 if (errorNumber !== 0) { 126 // Reset the servers to the old servers, because ares probably unset them. 127 this._handle.setServers(ArrayPrototypeJoin(orig, ',')); 128 const err = strerror(errorNumber); 129 throw new ERR_DNS_SET_SERVERS_FAILED(err, servers); 130 } 131 } 132 133 setLocalAddress(ipv4, ipv6) { 134 validateString(ipv4, 'ipv4'); 135 136 if (typeof ipv6 !== 'string' && ipv6 !== undefined) { 137 throw new ERR_INVALID_ARG_TYPE('ipv6', ['String', 'undefined'], ipv6); 138 } 139 140 this._handle.setLocalAddress(ipv4, ipv6); 141 } 142} 143 144let defaultResolver = new Resolver(); 145const resolverKeys = [ 146 'getServers', 147 'resolve', 148 'resolve4', 149 'resolve6', 150 'resolveAny', 151 'resolveCaa', 152 'resolveCname', 153 'resolveMx', 154 'resolveNaptr', 155 'resolveNs', 156 'resolvePtr', 157 'resolveSoa', 158 'resolveSrv', 159 'resolveTxt', 160 'reverse', 161]; 162 163function getDefaultResolver() { 164 return defaultResolver; 165} 166 167function setDefaultResolver(resolver) { 168 defaultResolver = resolver; 169} 170 171function bindDefaultResolver(target, source) { 172 ArrayPrototypeForEach(resolverKeys, (key) => { 173 target[key] = FunctionPrototypeBind(source[key], defaultResolver); 174 }); 175} 176 177function validateHints(hints) { 178 if ((hints & ~(AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED)) !== 0) { 179 throw new ERR_INVALID_OPT_VALUE('hints', hints); 180 } 181} 182 183let invalidHostnameWarningEmitted = false; 184 185function emitInvalidHostnameWarning(hostname) { 186 if (invalidHostnameWarningEmitted) { 187 return; 188 } 189 invalidHostnameWarningEmitted = true; 190 process.emitWarning( 191 `The provided hostname "${hostname}" is not a valid ` + 192 'hostname, and is supported in the dns module solely for compatibility.', 193 'DeprecationWarning', 194 'DEP0118' 195 ); 196} 197 198let dnsOrder = getOptionValue('--dns-result-order') || 'ipv4first'; 199 200function getDefaultVerbatim() { 201 switch (dnsOrder) { 202 case 'verbatim': 203 return true; 204 case 'ipv4first': 205 default: 206 return false; 207 } 208} 209 210function setDefaultResultOrder(value) { 211 validateOneOf(value, 'dnsOrder', ['verbatim', 'ipv4first']); 212 dnsOrder = value; 213} 214 215module.exports = { 216 bindDefaultResolver, 217 getDefaultResolver, 218 setDefaultResolver, 219 validateHints, 220 validateTimeout, 221 validateTries, 222 Resolver, 223 emitInvalidHostnameWarning, 224 getDefaultVerbatim, 225 setDefaultResultOrder, 226}; 227