• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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