1'use strict'; 2const { 3 ArrayPrototypeMap, 4 ObjectDefineProperty, 5 Promise, 6 ReflectApply, 7 Symbol, 8} = primordials; 9 10const { 11 bindDefaultResolver, 12 createResolverClass, 13 validateHints, 14 emitInvalidHostnameWarning, 15 getDefaultVerbatim, 16 errorCodes: dnsErrorCodes, 17 getDefaultResultOrder, 18 setDefaultResultOrder, 19 setDefaultResolver, 20} = require('internal/dns/utils'); 21 22const { 23 NODATA, 24 FORMERR, 25 SERVFAIL, 26 NOTFOUND, 27 NOTIMP, 28 REFUSED, 29 BADQUERY, 30 BADNAME, 31 BADFAMILY, 32 BADRESP, 33 CONNREFUSED, 34 TIMEOUT, 35 EOF, 36 FILE, 37 NOMEM, 38 DESTRUCTION, 39 BADSTR, 40 BADFLAGS, 41 NONAME, 42 BADHINTS, 43 NOTINITIALIZED, 44 LOADIPHLPAPI, 45 ADDRGETNETWORKPARAMS, 46 CANCELLED, 47} = dnsErrorCodes; 48const { codes, dnsException } = require('internal/errors'); 49const { isIP } = require('internal/net'); 50const { 51 getaddrinfo, 52 getnameinfo, 53 GetAddrInfoReqWrap, 54 GetNameInfoReqWrap, 55 QueryReqWrap, 56} = internalBinding('cares_wrap'); 57const { 58 ERR_INVALID_ARG_TYPE, 59 ERR_INVALID_ARG_VALUE, 60 ERR_MISSING_ARGS, 61} = codes; 62const { 63 validateBoolean, 64 validateNumber, 65 validateOneOf, 66 validatePort, 67 validateString, 68} = require('internal/validators'); 69 70const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext'); 71const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext'); 72const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext'); 73 74const { 75 hasObserver, 76 startPerf, 77 stopPerf, 78} = require('internal/perf/observe'); 79 80function onlookup(err, addresses) { 81 if (err) { 82 this.reject(dnsException(err, 'getaddrinfo', this.hostname)); 83 return; 84 } 85 86 const family = this.family || isIP(addresses[0]); 87 this.resolve({ address: addresses[0], family }); 88 if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) { 89 stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } }); 90 } 91} 92 93function onlookupall(err, addresses) { 94 if (err) { 95 this.reject(dnsException(err, 'getaddrinfo', this.hostname)); 96 return; 97 } 98 99 const family = this.family; 100 101 for (let i = 0; i < addresses.length; i++) { 102 const address = addresses[i]; 103 104 addresses[i] = { 105 address, 106 family: family || isIP(addresses[i]), 107 }; 108 } 109 110 this.resolve(addresses); 111 if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) { 112 stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } }); 113 } 114} 115 116/** 117 * Creates a promise that resolves with the IP address of the given hostname. 118 * @param {0 | 4 | 6} family - The IP address family (4 or 6, or 0 for both). 119 * @param {string} hostname - The hostname to resolve. 120 * @param {boolean} all - Whether to resolve with all IP addresses for the hostname. 121 * @param {number} hints - One or more supported getaddrinfo flags (supply multiple via 122 * bitwise OR). 123 * @param {boolean} verbatim - Whether to use the hostname verbatim. 124 * @returns {Promise<DNSLookupResult | DNSLookupResult[]>} The IP address(es) of the hostname. 125 * @typedef {object} DNSLookupResult 126 * @property {string} address - The IP address. 127 * @property {0 | 4 | 6} family - The IP address type. 4 for IPv4 or 6 for IPv6, or 0 (for both). 128 */ 129function createLookupPromise(family, hostname, all, hints, verbatim) { 130 return new Promise((resolve, reject) => { 131 if (!hostname) { 132 emitInvalidHostnameWarning(hostname); 133 resolve(all ? [] : { address: null, family: family === 6 ? 6 : 4 }); 134 return; 135 } 136 137 const matchedFamily = isIP(hostname); 138 139 if (matchedFamily !== 0) { 140 const result = { address: hostname, family: matchedFamily }; 141 resolve(all ? [result] : result); 142 return; 143 } 144 145 const req = new GetAddrInfoReqWrap(); 146 147 req.family = family; 148 req.hostname = hostname; 149 req.oncomplete = all ? onlookupall : onlookup; 150 req.resolve = resolve; 151 req.reject = reject; 152 153 const err = getaddrinfo(req, hostname, family, hints, verbatim); 154 155 if (err) { 156 reject(dnsException(err, 'getaddrinfo', hostname)); 157 } else if (hasObserver('dns')) { 158 const detail = { 159 hostname, 160 family, 161 hints, 162 verbatim, 163 }; 164 startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail }); 165 } 166 }); 167} 168 169const validFamilies = [0, 4, 6]; 170/** 171 * Get the IP address for a given hostname. 172 * @param {string} hostname - The hostname to resolve (ex. 'nodejs.org'). 173 * @param {object} [options] - Optional settings. 174 * @param {boolean} [options.all=false] - Whether to return all or just the first resolved address. 175 * @param {0 | 4 | 6} [options.family=0] - The record family. Must be 4, 6, or 0 (for both). 176 * @param {number} [options.hints] - One or more supported getaddrinfo flags (supply multiple via 177 * bitwise OR). 178 * @param {boolean} [options.verbatim=false] - Return results in same order DNS resolved them; 179 * otherwise IPv4 then IPv6. New code should supply `true`. 180 */ 181function lookup(hostname, options) { 182 let hints = 0; 183 let family = 0; 184 let all = false; 185 let verbatim = getDefaultVerbatim(); 186 187 // Parse arguments 188 if (hostname) { 189 validateString(hostname, 'hostname'); 190 } 191 192 if (typeof options === 'number') { 193 validateOneOf(options, 'family', validFamilies); 194 family = options; 195 } else if (options !== undefined && typeof options !== 'object') { 196 throw new ERR_INVALID_ARG_TYPE('options', ['integer', 'object'], options); 197 } else { 198 if (options?.hints != null) { 199 validateNumber(options.hints, 'options.hints'); 200 hints = options.hints >>> 0; 201 validateHints(hints); 202 } 203 if (options?.family != null) { 204 validateOneOf(options.family, 'options.family', validFamilies); 205 family = options.family; 206 } 207 if (options?.all != null) { 208 validateBoolean(options.all, 'options.all'); 209 all = options.all; 210 } 211 if (options?.verbatim != null) { 212 validateBoolean(options.verbatim, 'options.verbatim'); 213 verbatim = options.verbatim; 214 } 215 } 216 217 return createLookupPromise(family, hostname, all, hints, verbatim); 218} 219 220 221function onlookupservice(err, hostname, service) { 222 if (err) { 223 this.reject(dnsException(err, 'getnameinfo', this.host)); 224 return; 225 } 226 227 this.resolve({ hostname, service }); 228 if (this[kPerfHooksDnsLookupServiceContext] && hasObserver('dns')) { 229 stopPerf(this, kPerfHooksDnsLookupServiceContext, { detail: { hostname, service } }); 230 } 231} 232 233function createLookupServicePromise(hostname, port) { 234 return new Promise((resolve, reject) => { 235 const req = new GetNameInfoReqWrap(); 236 237 req.hostname = hostname; 238 req.port = port; 239 req.oncomplete = onlookupservice; 240 req.resolve = resolve; 241 req.reject = reject; 242 243 const err = getnameinfo(req, hostname, port); 244 245 if (err) 246 reject(dnsException(err, 'getnameinfo', hostname)); 247 else if (hasObserver('dns')) { 248 startPerf(req, kPerfHooksDnsLookupServiceContext, { 249 type: 'dns', 250 name: 'lookupService', 251 detail: { 252 host: hostname, 253 port, 254 }, 255 }); 256 } 257 }); 258} 259 260function lookupService(address, port) { 261 if (arguments.length !== 2) 262 throw new ERR_MISSING_ARGS('address', 'port'); 263 264 if (isIP(address) === 0) 265 throw new ERR_INVALID_ARG_VALUE('address', address); 266 267 validatePort(port); 268 269 return createLookupServicePromise(address, +port); 270} 271 272 273function onresolve(err, result, ttls) { 274 if (err) { 275 this.reject(dnsException(err, this.bindingName, this.hostname)); 276 return; 277 } 278 279 if (ttls && this.ttl) 280 result = ArrayPrototypeMap( 281 result, (address, index) => ({ address, ttl: ttls[index] })); 282 283 this.resolve(result); 284 if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) { 285 stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } }); 286 } 287} 288 289function createResolverPromise(resolver, bindingName, hostname, ttl) { 290 return new Promise((resolve, reject) => { 291 const req = new QueryReqWrap(); 292 293 req.bindingName = bindingName; 294 req.hostname = hostname; 295 req.oncomplete = onresolve; 296 req.resolve = resolve; 297 req.reject = reject; 298 req.ttl = ttl; 299 300 const err = resolver._handle[bindingName](req, hostname); 301 302 if (err) 303 reject(dnsException(err, bindingName, hostname)); 304 else if (hasObserver('dns')) { 305 startPerf(req, kPerfHooksDnsLookupResolveContext, { 306 type: 'dns', 307 name: bindingName, 308 detail: { 309 host: hostname, 310 ttl, 311 }, 312 }); 313 } 314 }); 315} 316 317function resolver(bindingName) { 318 function query(name, options) { 319 validateString(name, 'name'); 320 321 const ttl = !!(options && options.ttl); 322 return createResolverPromise(this, bindingName, name, ttl); 323 } 324 325 ObjectDefineProperty(query, 'name', { __proto__: null, value: bindingName }); 326 return query; 327} 328 329function resolve(hostname, rrtype) { 330 let resolver; 331 332 if (rrtype !== undefined) { 333 validateString(rrtype, 'rrtype'); 334 335 resolver = resolveMap[rrtype]; 336 337 if (typeof resolver !== 'function') 338 throw new ERR_INVALID_ARG_VALUE('rrtype', rrtype); 339 } else { 340 resolver = resolveMap.A; 341 } 342 343 return ReflectApply(resolver, this, [hostname]); 344} 345 346// Promise-based resolver. 347const { Resolver, resolveMap } = createResolverClass(resolver); 348Resolver.prototype.resolve = resolve; 349 350function defaultResolverSetServers(servers) { 351 const resolver = new Resolver(); 352 353 resolver.setServers(servers); 354 setDefaultResolver(resolver); 355 bindDefaultResolver(module.exports, Resolver.prototype); 356} 357 358module.exports = { 359 lookup, 360 lookupService, 361 Resolver, 362 getDefaultResultOrder, 363 setDefaultResultOrder, 364 setServers: defaultResolverSetServers, 365 366 // ERROR CODES 367 NODATA, 368 FORMERR, 369 SERVFAIL, 370 NOTFOUND, 371 NOTIMP, 372 REFUSED, 373 BADQUERY, 374 BADNAME, 375 BADFAMILY, 376 BADRESP, 377 CONNREFUSED, 378 TIMEOUT, 379 EOF, 380 FILE, 381 NOMEM, 382 DESTRUCTION, 383 BADSTR, 384 BADFLAGS, 385 NONAME, 386 BADHINTS, 387 NOTINITIALIZED, 388 LOADIPHLPAPI, 389 ADDRGETNETWORKPARAMS, 390 CANCELLED, 391}; 392bindDefaultResolver(module.exports, Resolver.prototype); 393