• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 { toASCII } = require('internal/idna');
50const { isIP } = require('internal/net');
51const {
52  getaddrinfo,
53  getnameinfo,
54  GetAddrInfoReqWrap,
55  GetNameInfoReqWrap,
56  QueryReqWrap,
57} = internalBinding('cares_wrap');
58const {
59  ERR_INVALID_ARG_TYPE,
60  ERR_INVALID_ARG_VALUE,
61  ERR_MISSING_ARGS,
62} = codes;
63const {
64  validateBoolean,
65  validateNumber,
66  validateOneOf,
67  validatePort,
68  validateString,
69} = require('internal/validators');
70
71const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
72const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
73const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
74
75const {
76  hasObserver,
77  startPerf,
78  stopPerf,
79} = require('internal/perf/observe');
80
81function onlookup(err, addresses) {
82  if (err) {
83    this.reject(dnsException(err, 'getaddrinfo', this.hostname));
84    return;
85  }
86
87  const family = this.family || isIP(addresses[0]);
88  this.resolve({ address: addresses[0], family });
89  if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) {
90    stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } });
91  }
92}
93
94function onlookupall(err, addresses) {
95  if (err) {
96    this.reject(dnsException(err, 'getaddrinfo', this.hostname));
97    return;
98  }
99
100  const family = this.family;
101
102  for (let i = 0; i < addresses.length; i++) {
103    const address = addresses[i];
104
105    addresses[i] = {
106      address,
107      family: family || isIP(addresses[i]),
108    };
109  }
110
111  this.resolve(addresses);
112  if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) {
113    stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } });
114  }
115}
116
117function createLookupPromise(family, hostname, all, hints, verbatim) {
118  return new Promise((resolve, reject) => {
119    if (!hostname) {
120      emitInvalidHostnameWarning(hostname);
121      resolve(all ? [] : { address: null, family: family === 6 ? 6 : 4 });
122      return;
123    }
124
125    const matchedFamily = isIP(hostname);
126
127    if (matchedFamily !== 0) {
128      const result = { address: hostname, family: matchedFamily };
129      resolve(all ? [result] : result);
130      return;
131    }
132
133    const req = new GetAddrInfoReqWrap();
134
135    req.family = family;
136    req.hostname = hostname;
137    req.oncomplete = all ? onlookupall : onlookup;
138    req.resolve = resolve;
139    req.reject = reject;
140
141    const err = getaddrinfo(req, toASCII(hostname), family, hints, verbatim);
142
143    if (err) {
144      reject(dnsException(err, 'getaddrinfo', hostname));
145    } else if (hasObserver('dns')) {
146      const detail = {
147        hostname,
148        family,
149        hints,
150        verbatim,
151      };
152      startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
153    }
154  });
155}
156
157const validFamilies = [0, 4, 6];
158function lookup(hostname, options) {
159  let hints = 0;
160  let family = 0;
161  let all = false;
162  let verbatim = getDefaultVerbatim();
163
164  // Parse arguments
165  if (hostname) {
166    validateString(hostname, 'hostname');
167  }
168
169  if (typeof options === 'number') {
170    validateOneOf(options, 'family', validFamilies);
171    family = options;
172  } else if (options !== undefined && typeof options !== 'object') {
173    throw new ERR_INVALID_ARG_TYPE('options', ['integer', 'object'], options);
174  } else {
175    if (options?.hints != null) {
176      validateNumber(options.hints, 'options.hints');
177      hints = options.hints >>> 0;
178      validateHints(hints);
179    }
180    if (options?.family != null) {
181      validateOneOf(options.family, 'options.family', validFamilies);
182      family = options.family;
183    }
184    if (options?.all != null) {
185      validateBoolean(options.all, 'options.all');
186      all = options.all;
187    }
188    if (options?.verbatim != null) {
189      validateBoolean(options.verbatim, 'options.verbatim');
190      verbatim = options.verbatim;
191    }
192  }
193
194  return createLookupPromise(family, hostname, all, hints, verbatim);
195}
196
197
198function onlookupservice(err, hostname, service) {
199  if (err) {
200    this.reject(dnsException(err, 'getnameinfo', this.host));
201    return;
202  }
203
204  this.resolve({ hostname, service });
205  if (this[kPerfHooksDnsLookupServiceContext] && hasObserver('dns')) {
206    stopPerf(this, kPerfHooksDnsLookupServiceContext, { detail: { hostname, service } });
207  }
208}
209
210function createLookupServicePromise(hostname, port) {
211  return new Promise((resolve, reject) => {
212    const req = new GetNameInfoReqWrap();
213
214    req.hostname = hostname;
215    req.port = port;
216    req.oncomplete = onlookupservice;
217    req.resolve = resolve;
218    req.reject = reject;
219
220    const err = getnameinfo(req, hostname, port);
221
222    if (err)
223      reject(dnsException(err, 'getnameinfo', hostname));
224    else if (hasObserver('dns')) {
225      startPerf(req, kPerfHooksDnsLookupServiceContext, {
226        type: 'dns',
227        name: 'lookupService',
228        detail: {
229          host: hostname,
230          port,
231        },
232      });
233    }
234  });
235}
236
237function lookupService(address, port) {
238  if (arguments.length !== 2)
239    throw new ERR_MISSING_ARGS('address', 'port');
240
241  if (isIP(address) === 0)
242    throw new ERR_INVALID_ARG_VALUE('address', address);
243
244  validatePort(port);
245
246  return createLookupServicePromise(address, +port);
247}
248
249
250function onresolve(err, result, ttls) {
251  if (err) {
252    this.reject(dnsException(err, this.bindingName, this.hostname));
253    return;
254  }
255
256  if (ttls && this.ttl)
257    result = ArrayPrototypeMap(
258      result, (address, index) => ({ address, ttl: ttls[index] }));
259
260  this.resolve(result);
261  if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) {
262    stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } });
263  }
264}
265
266function createResolverPromise(resolver, bindingName, hostname, ttl) {
267  return new Promise((resolve, reject) => {
268    const req = new QueryReqWrap();
269
270    req.bindingName = bindingName;
271    req.hostname = hostname;
272    req.oncomplete = onresolve;
273    req.resolve = resolve;
274    req.reject = reject;
275    req.ttl = ttl;
276
277    const err = resolver._handle[bindingName](req, toASCII(hostname));
278
279    if (err)
280      reject(dnsException(err, bindingName, hostname));
281    else if (hasObserver('dns')) {
282      startPerf(req, kPerfHooksDnsLookupResolveContext, {
283        type: 'dns',
284        name: bindingName,
285        detail: {
286          host: hostname,
287          ttl,
288        },
289      });
290    }
291  });
292}
293
294function resolver(bindingName) {
295  function query(name, options) {
296    validateString(name, 'name');
297
298    const ttl = !!(options && options.ttl);
299    return createResolverPromise(this, bindingName, name, ttl);
300  }
301
302  ObjectDefineProperty(query, 'name', { __proto__: null, value: bindingName });
303  return query;
304}
305
306function resolve(hostname, rrtype) {
307  let resolver;
308
309  if (rrtype !== undefined) {
310    validateString(rrtype, 'rrtype');
311
312    resolver = resolveMap[rrtype];
313
314    if (typeof resolver !== 'function')
315      throw new ERR_INVALID_ARG_VALUE('rrtype', rrtype);
316  } else {
317    resolver = resolveMap.A;
318  }
319
320  return ReflectApply(resolver, this, [hostname]);
321}
322
323// Promise-based resolver.
324const { Resolver, resolveMap } = createResolverClass(resolver);
325Resolver.prototype.resolve = resolve;
326
327function defaultResolverSetServers(servers) {
328  const resolver = new Resolver();
329
330  resolver.setServers(servers);
331  setDefaultResolver(resolver);
332  bindDefaultResolver(module.exports, Resolver.prototype);
333}
334
335module.exports = {
336  lookup,
337  lookupService,
338  Resolver,
339  getDefaultResultOrder,
340  setDefaultResultOrder,
341  setServers: defaultResolverSetServers,
342
343  // ERROR CODES
344  NODATA,
345  FORMERR,
346  SERVFAIL,
347  NOTFOUND,
348  NOTIMP,
349  REFUSED,
350  BADQUERY,
351  BADNAME,
352  BADFAMILY,
353  BADRESP,
354  CONNREFUSED,
355  TIMEOUT,
356  EOF,
357  FILE,
358  NOMEM,
359  DESTRUCTION,
360  BADSTR,
361  BADFLAGS,
362  NONAME,
363  BADHINTS,
364  NOTINITIALIZED,
365  LOADIPHLPAPI,
366  ADDRGETNETWORKPARAMS,
367  CANCELLED,
368};
369bindDefaultResolver(module.exports, Resolver.prototype);
370