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