Skip to content

Commit ee5056f

Browse files
committed
dns: gate setServers to avoid async cares conflicts
Use an EE with some state data to gate async resolution operations in c-ares so that setServers() can run independently. Likely a temporary fix for nodejs#894 with a better solution being to fix c-ares to do this without barfing.
1 parent 41c9daa commit ee5056f

File tree

2 files changed

+83
-11
lines changed

2 files changed

+83
-11
lines changed

lib/dns.js

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const net = require('net');
44
const util = require('util');
5+
const EventEmitter = require('events').EventEmitter;
56

67
const cares = process.binding('cares_wrap');
78
const uv = process.binding('uv');
@@ -11,6 +12,12 @@ const GetNameInfoReqWrap = cares.GetNameInfoReqWrap;
1112

1213
const isIp = net.isIP;
1314

15+
// `resolving` serves as a gate to `setServers()` which is not async
16+
// friendly, we have to wait for resolutions to finish before changing
17+
// the servers in c-ares
18+
const resolving = new EventEmitter();
19+
resolving.count = 0;
20+
resolving.paused = false;
1421

1522
function errnoException(err, syscall, hostname) {
1623
// FIXME(bnoordhuis) Remove this backwards compatibility shite and pass
@@ -56,7 +63,10 @@ function makeAsync(callback) {
5663
if (typeof callback !== 'function') {
5764
return callback;
5865
}
66+
resolving.count++;
5967
return function asyncCallback() {
68+
if (--resolving.count === 0)
69+
resolving.emit('empty');
6070
if (asyncCallback.immediately) {
6171
// The API already returned, we can invoke the callback immediately.
6272
callback.apply(null, arguments);
@@ -164,13 +174,20 @@ exports.lookup = function lookup(hostname, options, callback) {
164174
req.hostname = hostname;
165175
req.oncomplete = all ? onlookupall : onlookup;
166176

167-
var err = cares.getaddrinfo(req, hostname, family, hints);
168-
if (err) {
169-
callback(errnoException(err, 'getaddrinfo', hostname));
170-
return {};
177+
function enqueue() {
178+
var err = cares.getaddrinfo(req, hostname, family, hints);
179+
if (err) {
180+
callback(errnoException(err, 'getaddrinfo', hostname));
181+
return {};
182+
}
183+
callback.immediately = true;
184+
return req;
171185
}
172186

173-
callback.immediately = true;
187+
if (!resolving.paused)
188+
return enqueue();
189+
190+
resolving.once('ready', enqueue);
174191
return req;
175192
};
176193

@@ -199,10 +216,18 @@ exports.lookupService = function(host, port, callback) {
199216
req.port = port;
200217
req.oncomplete = onlookupservice;
201218

202-
var err = cares.getnameinfo(req, host, port);
203-
if (err) throw errnoException(err, 'getnameinfo', host);
219+
function enqueue() {
220+
var err = cares.getnameinfo(req, host, port);
221+
if (err) throw errnoException(err, 'getnameinfo', host);
222+
}
204223

205224
callback.immediately = true;
225+
226+
if (resolving.paused)
227+
resolving.once('ready', enqueue);
228+
else
229+
return enqueue();
230+
206231
return req;
207232
};
208233

@@ -232,9 +257,19 @@ function resolver(bindingName) {
232257
hostname: name,
233258
oncomplete: onresolve
234259
};
235-
var err = binding(req, name);
236-
if (err) throw errnoException(err, bindingName);
260+
261+
function enqueue() {
262+
var err = binding(req, name);
263+
if (err) throw errnoException(err, bindingName);
264+
}
265+
237266
callback.immediately = true;
267+
268+
if (resolving.paused)
269+
resolving.once('ready', enqueue);
270+
else
271+
return enqueue();
272+
238273
return req;
239274
}
240275
}
@@ -278,7 +313,31 @@ exports.getServers = function() {
278313
};
279314

280315

281-
exports.setServers = function(servers) {
316+
exports.setServers = function setServers(servers) {
317+
if (resolving.paused) {
318+
return resolving.once('ready', function() {
319+
setServers(servers);
320+
});
321+
}
322+
323+
if (resolving.count === 0) {
324+
return _setServers(servers);
325+
}
326+
327+
resolving.paused = true
328+
resolving.once('empty', function() {
329+
setImmediate(function() {
330+
try {
331+
_setServers(servers);
332+
} finally {
333+
resolving.paused = false;
334+
resolving.emit('ready');
335+
}
336+
});
337+
});
338+
}
339+
340+
function _setServers(servers) {
282341
// cache the original servers because in the event of an error setting the
283342
// servers cares won't have any servers available for resolution
284343
var orig = cares.getServers();
@@ -319,7 +378,7 @@ exports.setServers = function(servers) {
319378
throw new Error('c-ares failed to set servers: "' + err +
320379
'" [' + servers + ']');
321380
}
322-
};
381+
}
323382

324383
// uv_getaddrinfo flags
325384
exports.ADDRCONFIG = cares.AI_ADDRCONFIG;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// should not crash
2+
// see https://github.com/iojs/io.js/issues/894 for what this is trying
3+
// to test
4+
5+
var common = require('../common');
6+
var assert = require('assert');
7+
var dns = require('dns');
8+
9+
dns.resolve4('www.microsoft.com', function (err, result) {
10+
dns.setServers([ '8.8.8.9' ]);
11+
dns.resolve4('test.com', function (err, result) {});
12+
});
13+
dns.setServers([ '8.8.8.8' ]);

0 commit comments

Comments
 (0)