• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24const {
25  ObjectDefineProperties,
26  ObjectDefineProperty,
27  Symbol,
28} = primordials;
29
30const cares = internalBinding('cares_wrap');
31const { isIP } = require('internal/net');
32const { customPromisifyArgs } = require('internal/util');
33const errors = require('internal/errors');
34const {
35  bindDefaultResolver,
36  setDefaultResolver,
37  validateHints,
38  emitInvalidHostnameWarning,
39  getDefaultVerbatim,
40  getDefaultResultOrder,
41  setDefaultResultOrder,
42  errorCodes: dnsErrorCodes,
43} = require('internal/dns/utils');
44const {
45  Resolver,
46} = require('internal/dns/callback_resolver');
47const {
48  NODATA,
49  FORMERR,
50  SERVFAIL,
51  NOTFOUND,
52  NOTIMP,
53  REFUSED,
54  BADQUERY,
55  BADNAME,
56  BADFAMILY,
57  BADRESP,
58  CONNREFUSED,
59  TIMEOUT,
60  EOF,
61  FILE,
62  NOMEM,
63  DESTRUCTION,
64  BADSTR,
65  BADFLAGS,
66  NONAME,
67  BADHINTS,
68  NOTINITIALIZED,
69  LOADIPHLPAPI,
70  ADDRGETNETWORKPARAMS,
71  CANCELLED,
72} = dnsErrorCodes;
73const {
74  ERR_INVALID_ARG_TYPE,
75  ERR_INVALID_ARG_VALUE,
76  ERR_MISSING_ARGS,
77} = errors.codes;
78const {
79  validateBoolean,
80  validateFunction,
81  validateNumber,
82  validateOneOf,
83  validatePort,
84  validateString,
85} = require('internal/validators');
86
87const {
88  GetAddrInfoReqWrap,
89  GetNameInfoReqWrap,
90} = cares;
91
92const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
93const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
94
95const {
96  hasObserver,
97  startPerf,
98  stopPerf,
99} = require('internal/perf/observe');
100
101const dnsException = errors.dnsException;
102
103let promises = null; // Lazy loaded
104
105function onlookup(err, addresses) {
106  if (err) {
107    return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
108  }
109  this.callback(null, addresses[0], this.family || isIP(addresses[0]));
110  if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) {
111    stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } });
112  }
113}
114
115
116function onlookupall(err, addresses) {
117  if (err) {
118    return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
119  }
120
121  const family = this.family;
122  for (let i = 0; i < addresses.length; i++) {
123    const addr = addresses[i];
124    addresses[i] = {
125      address: addr,
126      family: family || isIP(addr),
127    };
128  }
129
130  this.callback(null, addresses);
131  if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) {
132    stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } });
133  }
134}
135
136
137// Easy DNS A/AAAA look up
138// lookup(hostname, [options,] callback)
139const validFamilies = [0, 4, 6];
140function lookup(hostname, options, callback) {
141  let hints = 0;
142  let family = 0;
143  let all = false;
144  let verbatim = getDefaultVerbatim();
145
146  // Parse arguments
147  if (hostname) {
148    validateString(hostname, 'hostname');
149  }
150
151  if (typeof options === 'function') {
152    callback = options;
153    family = 0;
154  } else if (typeof options === 'number') {
155    validateFunction(callback, 'callback');
156
157    validateOneOf(options, 'family', validFamilies);
158    family = options;
159  } else if (options !== undefined && typeof options !== 'object') {
160    validateFunction(arguments.length === 2 ? options : callback, 'callback');
161    throw new ERR_INVALID_ARG_TYPE('options', ['integer', 'object'], options);
162  } else {
163    validateFunction(callback, 'callback');
164
165    if (options?.hints != null) {
166      validateNumber(options.hints, 'options.hints');
167      hints = options.hints >>> 0;
168      validateHints(hints);
169    }
170    if (options?.family != null) {
171      switch (options.family) {
172        case 'IPv4':
173          family = 4;
174          break;
175        case 'IPv6':
176          family = 6;
177          break;
178        default:
179          validateOneOf(options.family, 'options.family', validFamilies);
180          family = options.family;
181          break;
182      }
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  if (!hostname) {
195    emitInvalidHostnameWarning(hostname);
196    if (all) {
197      process.nextTick(callback, null, []);
198    } else {
199      process.nextTick(callback, null, null, family === 6 ? 6 : 4);
200    }
201    return {};
202  }
203
204  const matchedFamily = isIP(hostname);
205  if (matchedFamily) {
206    if (all) {
207      process.nextTick(
208        callback, null, [{ address: hostname, family: matchedFamily }]);
209    } else {
210      process.nextTick(callback, null, hostname, matchedFamily);
211    }
212    return {};
213  }
214
215  const req = new GetAddrInfoReqWrap();
216  req.callback = callback;
217  req.family = family;
218  req.hostname = hostname;
219  req.oncomplete = all ? onlookupall : onlookup;
220
221  const err = cares.getaddrinfo(
222    req, hostname, family, hints, verbatim,
223  );
224  if (err) {
225    process.nextTick(callback, dnsException(err, 'getaddrinfo', hostname));
226    return {};
227  }
228  if (hasObserver('dns')) {
229    const detail = {
230      hostname,
231      family,
232      hints,
233      verbatim,
234    };
235    startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
236  }
237  return req;
238}
239
240ObjectDefineProperty(lookup, customPromisifyArgs,
241                     { __proto__: null, value: ['address', 'family'], enumerable: false });
242
243
244function onlookupservice(err, hostname, service) {
245  if (err)
246    return this.callback(dnsException(err, 'getnameinfo', this.hostname));
247
248  this.callback(null, hostname, service);
249  if (this[kPerfHooksDnsLookupServiceContext] && hasObserver('dns')) {
250    stopPerf(this, kPerfHooksDnsLookupServiceContext, { detail: { hostname, service } });
251  }
252}
253
254
255function lookupService(address, port, callback) {
256  if (arguments.length !== 3)
257    throw new ERR_MISSING_ARGS('address', 'port', 'callback');
258
259  if (isIP(address) === 0)
260    throw new ERR_INVALID_ARG_VALUE('address', address);
261
262  validatePort(port);
263
264  validateFunction(callback, 'callback');
265
266  port = +port;
267
268  const req = new GetNameInfoReqWrap();
269  req.callback = callback;
270  req.hostname = address;
271  req.port = port;
272  req.oncomplete = onlookupservice;
273
274  const err = cares.getnameinfo(req, address, port);
275  if (err) throw dnsException(err, 'getnameinfo', address);
276  if (hasObserver('dns')) {
277    startPerf(req, kPerfHooksDnsLookupServiceContext, {
278      type: 'dns',
279      name: 'lookupService',
280      detail: {
281        host: address,
282        port,
283      },
284    });
285  }
286  return req;
287}
288
289ObjectDefineProperty(lookupService, customPromisifyArgs,
290                     { __proto__: null, value: ['hostname', 'service'], enumerable: false });
291
292function defaultResolverSetServers(servers) {
293  const resolver = new Resolver();
294
295  resolver.setServers(servers);
296  setDefaultResolver(resolver);
297  bindDefaultResolver(module.exports, Resolver.prototype);
298
299  if (promises !== null)
300    bindDefaultResolver(promises, promises.Resolver.prototype);
301}
302
303module.exports = {
304  lookup,
305  lookupService,
306
307  Resolver,
308  getDefaultResultOrder,
309  setDefaultResultOrder,
310  setServers: defaultResolverSetServers,
311
312  // uv_getaddrinfo flags
313  ADDRCONFIG: cares.AI_ADDRCONFIG,
314  ALL: cares.AI_ALL,
315  V4MAPPED: cares.AI_V4MAPPED,
316
317  // ERROR CODES
318  NODATA,
319  FORMERR,
320  SERVFAIL,
321  NOTFOUND,
322  NOTIMP,
323  REFUSED,
324  BADQUERY,
325  BADNAME,
326  BADFAMILY,
327  BADRESP,
328  CONNREFUSED,
329  TIMEOUT,
330  EOF,
331  FILE,
332  NOMEM,
333  DESTRUCTION,
334  BADSTR,
335  BADFLAGS,
336  NONAME,
337  BADHINTS,
338  NOTINITIALIZED,
339  LOADIPHLPAPI,
340  ADDRGETNETWORKPARAMS,
341  CANCELLED,
342};
343
344bindDefaultResolver(module.exports, Resolver.prototype);
345
346ObjectDefineProperties(module.exports, {
347  promises: {
348    __proto__: null,
349    configurable: true,
350    enumerable: true,
351    get() {
352      if (promises === null) {
353        promises = require('internal/dns/promises');
354      }
355      return promises;
356    },
357  },
358});
359