1'use strict'; 2const { 3 ArrayPrototypeMap, 4 ObjectCreate, 5 ObjectDefineProperty, 6 Promise, 7 ReflectApply, 8} = primordials; 9 10const { 11 bindDefaultResolver, 12 Resolver: CallbackResolver, 13 validateHints, 14 validateTimeout, 15 validateTries, 16 emitInvalidHostnameWarning, 17 getDefaultVerbatim, 18} = require('internal/dns/utils'); 19const { codes, dnsException } = require('internal/errors'); 20const { toASCII } = require('internal/idna'); 21const { isIP } = require('internal/net'); 22const { 23 getaddrinfo, 24 getnameinfo, 25 ChannelWrap, 26 GetAddrInfoReqWrap, 27 GetNameInfoReqWrap, 28 QueryReqWrap 29} = internalBinding('cares_wrap'); 30const { 31 ERR_INVALID_ARG_TYPE, 32 ERR_INVALID_OPT_VALUE, 33 ERR_MISSING_ARGS, 34} = codes; 35const { 36 validatePort, 37 validateString, 38 validateOneOf, 39} = require('internal/validators'); 40 41function onlookup(err, addresses) { 42 if (err) { 43 this.reject(dnsException(err, 'getaddrinfo', this.hostname)); 44 return; 45 } 46 47 const family = this.family ? this.family : isIP(addresses[0]); 48 this.resolve({ address: addresses[0], family }); 49} 50 51function onlookupall(err, addresses) { 52 if (err) { 53 this.reject(dnsException(err, 'getaddrinfo', this.hostname)); 54 return; 55 } 56 57 const family = this.family; 58 59 for (var i = 0; i < addresses.length; i++) { 60 const address = addresses[i]; 61 62 addresses[i] = { 63 address, 64 family: family ? family : isIP(addresses[i]) 65 }; 66 } 67 68 this.resolve(addresses); 69} 70 71function createLookupPromise(family, hostname, all, hints, verbatim) { 72 return new Promise((resolve, reject) => { 73 if (!hostname) { 74 emitInvalidHostnameWarning(hostname); 75 resolve(all ? [] : { address: null, family: family === 6 ? 6 : 4 }); 76 return; 77 } 78 79 const matchedFamily = isIP(hostname); 80 81 if (matchedFamily !== 0) { 82 const result = { address: hostname, family: matchedFamily }; 83 resolve(all ? [result] : result); 84 return; 85 } 86 87 const req = new GetAddrInfoReqWrap(); 88 89 req.family = family; 90 req.hostname = hostname; 91 req.oncomplete = all ? onlookupall : onlookup; 92 req.resolve = resolve; 93 req.reject = reject; 94 95 const err = getaddrinfo(req, toASCII(hostname), family, hints, verbatim); 96 97 if (err) { 98 reject(dnsException(err, 'getaddrinfo', hostname)); 99 } 100 }); 101} 102 103function lookup(hostname, options) { 104 var hints = 0; 105 var family = -1; 106 var all = false; 107 var verbatim = getDefaultVerbatim(); 108 109 // Parse arguments 110 if (hostname && typeof hostname !== 'string') { 111 throw new ERR_INVALID_ARG_TYPE('hostname', 'string', hostname); 112 } else if (options !== null && typeof options === 'object') { 113 hints = options.hints >>> 0; 114 family = options.family >>> 0; 115 all = options.all === true; 116 if (typeof options.verbatim === 'boolean') { 117 verbatim = options.verbatim === true; 118 } 119 120 validateHints(hints); 121 } else { 122 family = options >>> 0; 123 } 124 125 validateOneOf(family, 'family', [0, 4, 6], true); 126 127 return createLookupPromise(family, hostname, all, hints, verbatim); 128} 129 130 131function onlookupservice(err, hostname, service) { 132 if (err) { 133 this.reject(dnsException(err, 'getnameinfo', this.host)); 134 return; 135 } 136 137 this.resolve({ hostname, service }); 138} 139 140function createLookupServicePromise(hostname, port) { 141 return new Promise((resolve, reject) => { 142 const req = new GetNameInfoReqWrap(); 143 144 req.hostname = hostname; 145 req.port = port; 146 req.oncomplete = onlookupservice; 147 req.resolve = resolve; 148 req.reject = reject; 149 150 const err = getnameinfo(req, hostname, port); 151 152 if (err) 153 reject(dnsException(err, 'getnameinfo', hostname)); 154 }); 155} 156 157function lookupService(address, port) { 158 if (arguments.length !== 2) 159 throw new ERR_MISSING_ARGS('address', 'port'); 160 161 if (isIP(address) === 0) 162 throw new ERR_INVALID_OPT_VALUE('address', address); 163 164 validatePort(port); 165 166 return createLookupServicePromise(address, +port); 167} 168 169 170function onresolve(err, result, ttls) { 171 if (err) { 172 this.reject(dnsException(err, this.bindingName, this.hostname)); 173 return; 174 } 175 176 if (ttls && this.ttl) 177 result = ArrayPrototypeMap( 178 result, (address, index) => ({ address, ttl: ttls[index] })); 179 180 this.resolve(result); 181} 182 183function createResolverPromise(resolver, bindingName, hostname, ttl) { 184 return new Promise((resolve, reject) => { 185 const req = new QueryReqWrap(); 186 187 req.bindingName = bindingName; 188 req.hostname = hostname; 189 req.oncomplete = onresolve; 190 req.resolve = resolve; 191 req.reject = reject; 192 req.ttl = ttl; 193 194 const err = resolver._handle[bindingName](req, toASCII(hostname)); 195 196 if (err) 197 reject(dnsException(err, bindingName, hostname)); 198 }); 199} 200 201function resolver(bindingName) { 202 function query(name, options) { 203 validateString(name, 'name'); 204 205 const ttl = !!(options && options.ttl); 206 return createResolverPromise(this, bindingName, name, ttl); 207 } 208 209 ObjectDefineProperty(query, 'name', { value: bindingName }); 210 return query; 211} 212 213 214const resolveMap = ObjectCreate(null); 215 216// Resolver instances correspond 1:1 to c-ares channels. 217class Resolver { 218 constructor(options = undefined) { 219 const timeout = validateTimeout(options); 220 const tries = validateTries(options); 221 this._handle = new ChannelWrap(timeout, tries); 222 } 223} 224 225Resolver.prototype.getServers = CallbackResolver.prototype.getServers; 226Resolver.prototype.setServers = CallbackResolver.prototype.setServers; 227Resolver.prototype.cancel = CallbackResolver.prototype.cancel; 228Resolver.prototype.setLocalAddress = CallbackResolver.prototype.setLocalAddress; 229Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny'); 230Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA'); 231Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa'); 232Resolver.prototype.resolveCaa = resolveMap.CAA = resolver('queryCaa'); 233Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname'); 234Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx'); 235Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs'); 236Resolver.prototype.resolveTxt = resolveMap.TXT = resolver('queryTxt'); 237Resolver.prototype.resolveSrv = resolveMap.SRV = resolver('querySrv'); 238Resolver.prototype.resolvePtr = resolveMap.PTR = resolver('queryPtr'); 239Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr'); 240Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa'); 241Resolver.prototype.reverse = resolver('getHostByAddr'); 242Resolver.prototype.resolve = function resolve(hostname, rrtype) { 243 var resolver; 244 245 if (typeof rrtype === 'string') { 246 resolver = resolveMap[rrtype]; 247 248 if (typeof resolver !== 'function') 249 throw new ERR_INVALID_OPT_VALUE('rrtype', rrtype); 250 } else if (rrtype === undefined) { 251 resolver = resolveMap.A; 252 } else { 253 throw new ERR_INVALID_ARG_TYPE('rrtype', 'string', rrtype); 254 } 255 256 return ReflectApply(resolver, this, [hostname]); 257}; 258 259 260module.exports = { lookup, lookupService, Resolver }; 261bindDefaultResolver(module.exports, Resolver.prototype); 262