• 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  ArrayPrototypeMap,
26  ObjectCreate,
27  ObjectDefineProperties,
28  ObjectDefineProperty,
29  ReflectApply,
30} = primordials;
31
32const cares = internalBinding('cares_wrap');
33const { toASCII } = require('internal/idna');
34const { isIP } = require('internal/net');
35const { customPromisifyArgs } = require('internal/util');
36const errors = require('internal/errors');
37const {
38  bindDefaultResolver,
39  getDefaultResolver,
40  setDefaultResolver,
41  Resolver,
42  validateHints,
43  emitInvalidHostnameWarning,
44  getDefaultVerbatim,
45  setDefaultResultOrder,
46} = require('internal/dns/utils');
47const {
48  ERR_INVALID_ARG_TYPE,
49  ERR_INVALID_CALLBACK,
50  ERR_INVALID_OPT_VALUE,
51  ERR_MISSING_ARGS,
52} = errors.codes;
53const {
54  validatePort,
55  validateString,
56  validateOneOf,
57} = require('internal/validators');
58
59const {
60  GetAddrInfoReqWrap,
61  GetNameInfoReqWrap,
62  QueryReqWrap,
63} = cares;
64
65const dnsException = errors.dnsException;
66
67let promises = null; // Lazy loaded
68
69function onlookup(err, addresses) {
70  if (err) {
71    return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
72  }
73  this.callback(null, addresses[0], this.family || isIP(addresses[0]));
74}
75
76
77function onlookupall(err, addresses) {
78  if (err) {
79    return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
80  }
81
82  const family = this.family;
83  for (let i = 0; i < addresses.length; i++) {
84    const addr = addresses[i];
85    addresses[i] = {
86      address: addr,
87      family: family || isIP(addr)
88    };
89  }
90
91  this.callback(null, addresses);
92}
93
94
95// Easy DNS A/AAAA look up
96// lookup(hostname, [options,] callback)
97function lookup(hostname, options, callback) {
98  let hints = 0;
99  let family = -1;
100  let all = false;
101  let verbatim = getDefaultVerbatim();
102
103  // Parse arguments
104  if (hostname && typeof hostname !== 'string') {
105    throw new ERR_INVALID_ARG_TYPE('hostname', 'string', hostname);
106  } else if (typeof options === 'function') {
107    callback = options;
108    family = 0;
109  } else if (typeof callback !== 'function') {
110    throw new ERR_INVALID_CALLBACK(callback);
111  } else if (options !== null && typeof options === 'object') {
112    hints = options.hints >>> 0;
113    family = options.family >>> 0;
114    all = options.all === true;
115    if (typeof options.verbatim === 'boolean') {
116      verbatim = options.verbatim === true;
117    }
118
119    validateHints(hints);
120  } else {
121    family = options >>> 0;
122  }
123
124  validateOneOf(family, 'family', [0, 4, 6], true);
125
126  if (!hostname) {
127    emitInvalidHostnameWarning(hostname);
128    if (all) {
129      process.nextTick(callback, null, []);
130    } else {
131      process.nextTick(callback, null, null, family === 6 ? 6 : 4);
132    }
133    return {};
134  }
135
136  const matchedFamily = isIP(hostname);
137  if (matchedFamily) {
138    if (all) {
139      process.nextTick(
140        callback, null, [{ address: hostname, family: matchedFamily }]);
141    } else {
142      process.nextTick(callback, null, hostname, matchedFamily);
143    }
144    return {};
145  }
146
147  const req = new GetAddrInfoReqWrap();
148  req.callback = callback;
149  req.family = family;
150  req.hostname = hostname;
151  req.oncomplete = all ? onlookupall : onlookup;
152
153  const err = cares.getaddrinfo(
154    req, toASCII(hostname), family, hints, verbatim
155  );
156  if (err) {
157    process.nextTick(callback, dnsException(err, 'getaddrinfo', hostname));
158    return {};
159  }
160  return req;
161}
162
163ObjectDefineProperty(lookup, customPromisifyArgs,
164                     { value: ['address', 'family'], enumerable: false });
165
166
167function onlookupservice(err, hostname, service) {
168  if (err)
169    return this.callback(dnsException(err, 'getnameinfo', this.hostname));
170
171  this.callback(null, hostname, service);
172}
173
174
175function lookupService(address, port, callback) {
176  if (arguments.length !== 3)
177    throw new ERR_MISSING_ARGS('address', 'port', 'callback');
178
179  if (isIP(address) === 0)
180    throw new ERR_INVALID_OPT_VALUE('address', address);
181
182  validatePort(port);
183
184  if (typeof callback !== 'function')
185    throw new ERR_INVALID_CALLBACK(callback);
186
187  port = +port;
188
189  const req = new GetNameInfoReqWrap();
190  req.callback = callback;
191  req.hostname = address;
192  req.port = port;
193  req.oncomplete = onlookupservice;
194
195  const err = cares.getnameinfo(req, address, port);
196  if (err) throw dnsException(err, 'getnameinfo', address);
197  return req;
198}
199
200ObjectDefineProperty(lookupService, customPromisifyArgs,
201                     { value: ['hostname', 'service'], enumerable: false });
202
203
204function onresolve(err, result, ttls) {
205  if (ttls && this.ttl)
206    result = ArrayPrototypeMap(
207      result, (address, index) => ({ address, ttl: ttls[index] }));
208
209  if (err)
210    this.callback(dnsException(err, this.bindingName, this.hostname));
211  else
212    this.callback(null, result);
213}
214
215function resolver(bindingName) {
216  function query(name, /* options, */ callback) {
217    let options;
218    if (arguments.length > 2) {
219      options = callback;
220      callback = arguments[2];
221    }
222
223    validateString(name, 'name');
224    if (typeof callback !== 'function') {
225      throw new ERR_INVALID_CALLBACK(callback);
226    }
227
228    const req = new QueryReqWrap();
229    req.bindingName = bindingName;
230    req.callback = callback;
231    req.hostname = name;
232    req.oncomplete = onresolve;
233    req.ttl = !!(options && options.ttl);
234    const err = this._handle[bindingName](req, toASCII(name));
235    if (err) throw dnsException(err, bindingName, name);
236    return req;
237  }
238  ObjectDefineProperty(query, 'name', { value: bindingName });
239  return query;
240}
241
242const resolveMap = ObjectCreate(null);
243Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny');
244Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA');
245Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
246Resolver.prototype.resolveCaa = resolveMap.CAA = resolver('queryCaa');
247Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname');
248Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx');
249Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs');
250Resolver.prototype.resolveTxt = resolveMap.TXT = resolver('queryTxt');
251Resolver.prototype.resolveSrv = resolveMap.SRV = resolver('querySrv');
252Resolver.prototype.resolvePtr = resolveMap.PTR = resolver('queryPtr');
253Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr');
254Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa');
255Resolver.prototype.reverse = resolver('getHostByAddr');
256
257Resolver.prototype.resolve = resolve;
258
259function resolve(hostname, rrtype, callback) {
260  let resolver;
261  if (typeof rrtype === 'string') {
262    resolver = resolveMap[rrtype];
263  } else if (typeof rrtype === 'function') {
264    resolver = resolveMap.A;
265    callback = rrtype;
266  } else {
267    throw new ERR_INVALID_ARG_TYPE('rrtype', 'string', rrtype);
268  }
269
270  if (typeof resolver === 'function') {
271    return ReflectApply(resolver, this, [hostname, callback]);
272  }
273  throw new ERR_INVALID_OPT_VALUE('rrtype', rrtype);
274}
275
276function defaultResolverSetServers(servers) {
277  const resolver = new Resolver();
278
279  resolver.setServers(servers);
280  setDefaultResolver(resolver);
281  bindDefaultResolver(module.exports, Resolver.prototype);
282
283  if (promises !== null)
284    bindDefaultResolver(promises, promises.Resolver.prototype);
285}
286
287module.exports = {
288  lookup,
289  lookupService,
290
291  Resolver,
292  setDefaultResultOrder,
293  setServers: defaultResolverSetServers,
294
295  // uv_getaddrinfo flags
296  ADDRCONFIG: cares.AI_ADDRCONFIG,
297  ALL: cares.AI_ALL,
298  V4MAPPED: cares.AI_V4MAPPED,
299
300  // ERROR CODES
301  NODATA: 'ENODATA',
302  FORMERR: 'EFORMERR',
303  SERVFAIL: 'ESERVFAIL',
304  NOTFOUND: 'ENOTFOUND',
305  NOTIMP: 'ENOTIMP',
306  REFUSED: 'EREFUSED',
307  BADQUERY: 'EBADQUERY',
308  BADNAME: 'EBADNAME',
309  BADFAMILY: 'EBADFAMILY',
310  BADRESP: 'EBADRESP',
311  CONNREFUSED: 'ECONNREFUSED',
312  TIMEOUT: 'ETIMEOUT',
313  EOF: 'EOF',
314  FILE: 'EFILE',
315  NOMEM: 'ENOMEM',
316  DESTRUCTION: 'EDESTRUCTION',
317  BADSTR: 'EBADSTR',
318  BADFLAGS: 'EBADFLAGS',
319  NONAME: 'ENONAME',
320  BADHINTS: 'EBADHINTS',
321  NOTINITIALIZED: 'ENOTINITIALIZED',
322  LOADIPHLPAPI: 'ELOADIPHLPAPI',
323  ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS',
324  CANCELLED: 'ECANCELLED'
325};
326
327bindDefaultResolver(module.exports, getDefaultResolver());
328
329ObjectDefineProperties(module.exports, {
330  promises: {
331    configurable: true,
332    enumerable: true,
333    get() {
334      if (promises === null) {
335        promises = require('internal/dns/promises');
336        promises.setServers = defaultResolverSetServers;
337        promises.setDefaultResultOrder = setDefaultResultOrder;
338      }
339      return promises;
340    }
341  }
342});
343