1 // Copyright Joyent, Inc. and other Node contributors.
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:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
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.
22 var net = require('net');
23 var util = require('util');
25 var cares = process.binding('cares_wrap');
26 var uv = process.binding('uv');
31 function errnoException(err, syscall, hostname) {
32 // FIXME(bnoordhuis) Remove this backwards compatibility shite and pass
33 // the true error to the user. ENOTFOUND is not even a proper POSIX error!
34 if (err === uv.UV_EAI_MEMORY ||
35 err === uv.UV_EAI_NODATA ||
36 err === uv.UV_EAI_NONAME) {
40 if (typeof err === 'string') { // c-ares error code.
41 ex = new Error(syscall + ' ' + err);
46 ex = util._errnoException(err, syscall);
49 ex.hostname = hostname;
55 // c-ares invokes a callback either synchronously or asynchronously,
56 // but the dns API should always invoke a callback asynchronously.
58 // This function makes sure that the callback is invoked asynchronously.
59 // It returns a function that invokes the callback within nextTick().
61 // To avoid invoking unnecessary nextTick(), `immediately` property of
62 // returned function should be set to true after c-ares returned.
66 // function someAPI(callback) {
67 // callback = makeAsync(callback);
68 // channel.someAPI(..., callback);
69 // callback.immediately = true;
71 function makeAsync(callback) {
72 if (!util.isFunction(callback)) {
75 return function asyncCallback() {
76 if (asyncCallback.immediately) {
77 // The API already returned, we can invoke the callback immediately.
78 callback.apply(null, arguments);
81 process.nextTick(function() {
82 callback.apply(null, args);
89 function onlookup(err, addresses) {
91 return this.callback(errnoException(err, 'getaddrinfo', this.hostname));
94 this.callback(null, addresses[0], this.family);
96 this.callback(null, addresses[0], addresses[0].indexOf(':') >= 0 ? 6 : 4);
101 // Easy DNS A/AAAA look up
102 // lookup(hostname, [family,] callback)
103 exports.lookup = function(hostname, family, callback) {
105 if (arguments.length === 2) {
108 } else if (!family) {
112 if (family !== 4 && family !== 6) {
113 throw new Error('invalid argument: `family` must be 4 or 6');
116 callback = makeAsync(callback);
119 callback(null, null, family === 6 ? 6 : 4);
123 var matchedFamily = net.isIP(hostname);
125 callback(null, hostname, matchedFamily);
135 var err = cares.getaddrinfo(req, hostname, family);
136 if (err) throw errnoException(err, 'getaddrinfo', hostname);
138 callback.immediately = true;
143 function onresolve(err, result) {
145 this.callback(errnoException(err, this.bindingName, this.hostname));
147 this.callback(null, result);
151 function resolver(bindingName) {
152 var binding = cares[bindingName];
154 return function query(name, callback) {
155 callback = makeAsync(callback);
157 bindingName: bindingName,
160 oncomplete: onresolve
162 var err = binding(req, name);
163 if (err) throw errnoException(err, bindingName);
164 callback.immediately = true;
171 exports.resolve4 = resolveMap.A = resolver('queryA');
172 exports.resolve6 = resolveMap.AAAA = resolver('queryAaaa');
173 exports.resolveCname = resolveMap.CNAME = resolver('queryCname');
174 exports.resolveMx = resolveMap.MX = resolver('queryMx');
175 exports.resolveNs = resolveMap.NS = resolver('queryNs');
176 exports.resolveTxt = resolveMap.TXT = resolver('queryTxt');
177 exports.resolveSrv = resolveMap.SRV = resolver('querySrv');
178 exports.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr');
179 exports.resolveSoa = resolveMap.SOA = resolver('querySoa');
180 exports.reverse = resolveMap.PTR = resolver('getHostByAddr');
183 exports.resolve = function(hostname, type_, callback_) {
184 var resolver, callback;
185 if (util.isString(type_)) {
186 resolver = resolveMap[type_];
187 callback = callback_;
188 } else if (util.isFunction(type_)) {
189 resolver = exports.resolve4;
192 throw new Error('Type must be a string');
195 if (util.isFunction(resolver)) {
196 return resolver(hostname, callback);
198 throw new Error('Unknown type "' + type_ + '"');
203 exports.getServers = function() {
204 return cares.getServers();
208 exports.setServers = function(servers) {
209 // cache the original servers because in the event of an error setting the
210 // servers cares won't have any servers available for resolution
211 var orig = cares.getServers();
215 servers.forEach(function(serv) {
216 var ver = isIp(serv);
219 return newSet.push([ver, serv]);
221 var match = serv.match(/\[(.*)\](:\d+)?/);
223 // we have an IPv6 in brackets
225 ver = isIp(match[1]);
227 return newSet.push([ver, match[1]]);
230 var s = serv.split(/:\d+$/)[0];
234 return newSet.push([ver, s]);
236 throw new Error('IP address is not properly formatted: ' + serv);
239 var r = cares.setServers(newSet);
242 // reset the servers to the old servers, because ares probably unset them
243 cares.setServers(orig.join(','));
245 var err = cares.strerror(r);
246 throw new Error('c-ares failed to set servers: "' + err +
247 '" [' + servers + ']');
253 exports.NODATA = 'ENODATA';
254 exports.FORMERR = 'EFORMERR';
255 exports.SERVFAIL = 'ESERVFAIL';
256 exports.NOTFOUND = 'ENOTFOUND';
257 exports.NOTIMP = 'ENOTIMP';
258 exports.REFUSED = 'EREFUSED';
259 exports.BADQUERY = 'EBADQUERY';
260 exports.ADNAME = 'EADNAME';
261 exports.BADFAMILY = 'EBADFAMILY';
262 exports.BADRESP = 'EBADRESP';
263 exports.CONNREFUSED = 'ECONNREFUSED';
264 exports.TIMEOUT = 'ETIMEOUT';
266 exports.FILE = 'EFILE';
267 exports.NOMEM = 'ENOMEM';
268 exports.DESTRUCTION = 'EDESTRUCTION';
269 exports.BADSTR = 'EBADSTR';
270 exports.BADFLAGS = 'EBADFLAGS';
271 exports.NONAME = 'ENONAME';
272 exports.BADHINTS = 'EBADHINTS';
273 exports.NOTINITIALIZED = 'ENOTINITIALIZED';
274 exports.LOADIPHLPAPI = 'ELOADIPHLPAPI';
275 exports.ADDRGETNETWORKPARAMS = 'EADDRGETNETWORKPARAMS';
276 exports.CANCELLED = 'ECANCELLED';