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