text: // Copyright Joyent, Inc. and other Node contributors.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to permit
  // persons to whom the Software is furnished to do so, subject to the
  // following conditions:
  //
  // The above copyright notice and this permission notice shall be included
  // in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  'use strict';
  
  const {
    ObjectCreate,
    ObjectDefineProperties,
    ObjectDefineProperty,
  } = primordials;
  
  const cares = internalBinding('cares_wrap');
  const { toASCII } = require('internal/idna');
  const { isIP } = require('internal/net');
  const { customPromisifyArgs } = require('internal/util');
  const errors = require('internal/errors');
  const {
    bindDefaultResolver,
    getDefaultResolver,
    setDefaultResolver,
    Resolver,
    validateHints,
    emitInvalidHostnameWarning,
  } = require('internal/dns/utils');
  const {
    ERR_INVALID_ARG_TYPE,
    ERR_INVALID_CALLBACK,
    ERR_INVALID_OPT_VALUE,
    ERR_MISSING_ARGS,
  } = errors.codes;
  const {
    validatePort,
    validateString,
  } = require('internal/validators');
  
  const {
    GetAddrInfoReqWrap,
    GetNameInfoReqWrap,
    QueryReqWrap,
  } = cares;
  
  const dnsException = errors.dnsException;
  
  let promises = null; // Lazy loaded
  
  function onlookup(err, addresses) {
    if (err) {
      return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
    }
    if (this.family) {
      this.callback(null, addresses[0], this.family);
    } else {
      this.callback(null, addresses[0], isIP(addresses[0]));
    }
  }
  
  
  function onlookupall(err, addresses) {
    if (err) {
      return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
    }
  
    const family = this.family;
    for (let i = 0; i < addresses.length; i++) {
      const addr = addresses[i];
      addresses[i] = {
        address: addr,
        family: family || isIP(addr)
      };
    }
  
    this.callback(null, addresses);
  }
  
  
  // Easy DNS A/AAAA look up
  // lookup(hostname, [options,] callback)
  function lookup(hostname, options, callback) {
    let hints = 0;
    let family = -1;
    let all = false;
    let verbatim = false;
  
    // Parse arguments
    if (hostname && typeof hostname !== 'string') {
      throw new ERR_INVALID_ARG_TYPE('hostname', 'string', hostname);
    } else if (typeof options === 'function') {
      callback = options;
      family = 0;
    } else if (typeof callback !== 'function') {
      throw new ERR_INVALID_CALLBACK(callback);
    } else if (options !== null && typeof options === 'object') {
      hints = options.hints >>> 0;
      family = options.family >>> 0;
      all = options.all === true;
      verbatim = options.verbatim === true;
  
      validateHints(hints);
    } else {
      family = options >>> 0;
    }
  
    if (family !== 0 && family !== 4 && family !== 6)
      throw new ERR_INVALID_OPT_VALUE('family', family);
  
    if (!hostname) {
      emitInvalidHostnameWarning(hostname);
      if (all) {
        process.nextTick(callback, null, []);
      } else {
        process.nextTick(callback, null, null, family === 6 ? 6 : 4);
      }
      return {};
    }
  
    const matchedFamily = isIP(hostname);
    if (matchedFamily) {
      if (all) {
        process.nextTick(
          callback, null, [{ address: hostname, family: matchedFamily }]);
      } else {
        process.nextTick(callback, null, hostname, matchedFamily);
      }
      return {};
    }
  
    const req = new GetAddrInfoReqWrap();
    req.callback = callback;
    req.family = family;
    req.hostname = hostname;
    req.oncomplete = all ? onlookupall : onlookup;
  
    const err = cares.getaddrinfo(
      req, toASCII(hostname), family, hints, verbatim
    );
    if (err) {
      process.nextTick(callback, dnsException(err, 'getaddrinfo', hostname));
      return {};
    }
    return req;
  }
  
  ObjectDefineProperty(lookup, customPromisifyArgs,
                       { value: ['address', 'family'], enumerable: false });
  
  
  function onlookupservice(err, hostname, service) {
    if (err)
      return this.callback(dnsException(err, 'getnameinfo', this.hostname));
  
    this.callback(null, hostname, service);
  }
  
  
  function lookupService(address, port, callback) {
    if (arguments.length !== 3)
      throw new ERR_MISSING_ARGS('address', 'port', 'callback');
  
    if (isIP(address) === 0)
      throw new ERR_INVALID_OPT_VALUE('address', address);
  
    validatePort(port);
  
    if (typeof callback !== 'function')
      throw new ERR_INVALID_CALLBACK(callback);
  
    port = +port;
  
    const req = new GetNameInfoReqWrap();
    req.callback = callback;
    req.hostname = address;
    req.port = port;
    req.oncomplete = onlookupservice;
  
    const err = cares.getnameinfo(req, address, port);
    if (err) throw dnsException(err, 'getnameinfo', address);
    return req;
  }
  
  ObjectDefineProperty(lookupService, customPromisifyArgs,
                       { value: ['hostname', 'service'], enumerable: false });
  
  
  function onresolve(err, result, ttls) {
    if (ttls && this.ttl)
      result = result.map((address, index) => ({ address, ttl: ttls[index] }));
  
    if (err)
      this.callback(dnsException(err, this.bindingName, this.hostname));
    else
      this.callback(null, result);
  }
  
  function resolver(bindingName) {
    function query(name, /* options, */ callback) {
      let options;
      if (arguments.length > 2) {
        options = callback;
        callback = arguments[2];
      }
  
      validateString(name, 'name');
      if (typeof callback !== 'function') {
        throw new ERR_INVALID_CALLBACK(callback);
      }
  
      const req = new QueryReqWrap();
      req.bindingName = bindingName;
      req.callback = callback;
      req.hostname = name;
      req.oncomplete = onresolve;
      req.ttl = !!(options && options.ttl);
      const err = this._handle[bindingName](req, toASCII(name));
      if (err) throw dnsException(err, bindingName, name);
      return req;
    }
    ObjectDefineProperty(query, 'name', { value: bindingName });
    return query;
  }
  
  const resolveMap = ObjectCreate(null);
  Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny');
  Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA');
  Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
  Resolver.prototype.resolveCname = resolveMap.CNAME = resolver('queryCname');
  Resolver.prototype.resolveMx = resolveMap.MX = resolver('queryMx');
  Resolver.prototype.resolveNs = resolveMap.NS = resolver('queryNs');
  Resolver.prototype.resolveTxt = resolveMap.TXT = resolver('queryTxt');
  Resolver.prototype.resolveSrv = resolveMap.SRV = resolver('querySrv');
  Resolver.prototype.resolvePtr = resolveMap.PTR = resolver('queryPtr');
  Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr');
  Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa');
  Resolver.prototype.reverse = resolver('getHostByAddr');
  
  Resolver.prototype.resolve = resolve;
  
  function resolve(hostname, rrtype, callback) {
    let resolver;
    if (typeof rrtype === 'string') {
      resolver = resolveMap[rrtype];
    } else if (typeof rrtype === 'function') {
      resolver = resolveMap.A;
      callback = rrtype;
    } else {
      throw new ERR_INVALID_ARG_TYPE('rrtype', 'string', rrtype);
    }
  
    if (typeof resolver === 'function') {
      return resolver.call(this, hostname, callback);
    } else {
      throw new ERR_INVALID_OPT_VALUE('rrtype', rrtype);
    }
  }
  
  function defaultResolverSetServers(servers) {
    const resolver = new Resolver();
  
    resolver.setServers(servers);
    setDefaultResolver(resolver);
    bindDefaultResolver(module.exports, Resolver.prototype);
  
    if (promises !== null)
      bindDefaultResolver(promises, promises.Resolver.prototype);
  }
  
  module.exports = {
    lookup,
    lookupService,
  
    Resolver,
    setServers: defaultResolverSetServers,
  
    // uv_getaddrinfo flags
    ADDRCONFIG: cares.AI_ADDRCONFIG,
    V4MAPPED: cares.AI_V4MAPPED,
  
    // ERROR CODES
    NODATA: 'ENODATA',
    FORMERR: 'EFORMERR',
    SERVFAIL: 'ESERVFAIL',
    NOTFOUND: 'ENOTFOUND',
    NOTIMP: 'ENOTIMP',
    REFUSED: 'EREFUSED',
    BADQUERY: 'EBADQUERY',
    BADNAME: 'EBADNAME',
    BADFAMILY: 'EBADFAMILY',
    BADRESP: 'EBADRESP',
    CONNREFUSED: 'ECONNREFUSED',
    TIMEOUT: 'ETIMEOUT',
    EOF: 'EOF',
    FILE: 'EFILE',
    NOMEM: 'ENOMEM',
    DESTRUCTION: 'EDESTRUCTION',
    BADSTR: 'EBADSTR',
    BADFLAGS: 'EBADFLAGS',
    NONAME: 'ENONAME',
    BADHINTS: 'EBADHINTS',
    NOTINITIALIZED: 'ENOTINITIALIZED',
    LOADIPHLPAPI: 'ELOADIPHLPAPI',
    ADDRGETNETWORKPARAMS: 'EADDRGETNETWORKPARAMS',
    CANCELLED: 'ECANCELLED'
  };
  
  bindDefaultResolver(module.exports, getDefaultResolver());
  
  ObjectDefineProperties(module.exports, {
    promises: {
      configurable: true,
      enumerable: true,
      get() {
        if (promises === null) {
          promises = require('internal/dns/promises');
          promises.setServers = defaultResolverSetServers;
        }
        return promises;
      }
    }
  });
  