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