From 3b003dadd2c014a9073127589b33c0ae05087b8f Mon Sep 17 00:00:00 2001 From: finalchild Date: Thu, 8 Oct 2026 23:26:44 +0900 Subject: [PATCH] CASSNODEJS-16: Use system resolver for contact points patch by finalchild; reviewed by Jorge Bay and Jane He --- lib/control-connection.js | 33 ++------- test/unit/control-connection-tests.js | 99 +++++++++------------------ 2 files changed, 41 insertions(+), 91 deletions(-) diff --git a/lib/control-connection.js b/lib/control-connection.js index fd707529..e970d355 100644 --- a/lib/control-connection.js +++ b/lib/control-connection.js @@ -895,40 +895,21 @@ class ControlConnection extends events.EventEmitter { } /** - * Uses the DNS protocol to resolve a IPv4 and IPv6 addresses (A and AAAA records) for the hostname. + * Uses the operating system resolver to resolve all IPv4 and IPv6 addresses for the hostname. * It returns an Array of addresses that can be empty and logs the error. * @private * @param name */ async _resolveAll(name) { - const addresses = []; - const resolve4 = util.promisify(dns.resolve4); - const resolve6 = util.promisify(dns.resolve6); const lookup = util.promisify(dns.lookup); - // Ignore errors for resolve calls - const ipv4Promise = resolve4(name).catch(() => {}).then(r => r || utils.emptyArray); - const ipv6Promise = resolve6(name).catch(() => {}).then(r => r || utils.emptyArray); - - let arr; - arr = await ipv4Promise; - arr.forEach(address => addresses.push({address, isIPv6: false})); - - arr = await ipv6Promise; - arr.forEach(address => addresses.push({address, isIPv6: true})); - - if (addresses.length === 0) { - // In case dns.resolve*() methods don't yield a valid address for the host name - // Use system call getaddrinfo() that might resolve according to host system definitions - try { - arr = await lookup(name, { all: true }); - arr.forEach(({address, family}) => addresses.push({address, isIPv6: family === 6})); - } catch (err) { - this.log('error', `Host with name ${name} could not be resolved`, err); - } + try { + const addresses = await lookup(name, { all: true }); + return addresses.map(({address, family}) => ({address, isIPv6: family === 6})); + } catch (err) { + this.log('error', `Host with name ${name} could not be resolved`, err); + return utils.emptyArray; } - - return addresses; } /** diff --git a/test/unit/control-connection-tests.js b/test/unit/control-connection-tests.js index 8481d3d7..8bfc1e35 100644 --- a/test/unit/control-connection-tests.js +++ b/test/unit/control-connection-tests.js @@ -81,14 +81,12 @@ describe('ControlConnection', function () { it('should resolve IPv4 and IPv6 addresses, default host (localhost) and port', () => { const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { - cb(null, ifLocalhost(name, ['127.0.0.1'])); - }, - resolve6: function (name, cb) { - cb(null, ifLocalhost(name, ['::1'])); - }, - lookup: function () { - throw new Error('dns.lookup() should not be used'); + lookup: function (name, options, cb) { + assert.deepStrictEqual(options, { all: true }); + cb(null, ifLocalhost(name, [ + { address: '127.0.0.1', family: 4 }, + { address: '::1', family: 6 } + ])); } }}); @@ -99,14 +97,12 @@ describe('ControlConnection', function () { it('should resolve IPv4 and IPv6 addresses with non default port', () => { const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { - cb(null, ifLocalhost(name, ['127.0.0.1'])); - }, - resolve6: function (name, cb) { - cb(null, ifLocalhost(name, ['::1'])); - }, - lookup: function () { - throw new Error('dns.lookup() should not be used'); + lookup: function (name, options, cb) { + assert.deepStrictEqual(options, { all: true }); + cb(null, ifLocalhost(name, [ + { address: '127.0.0.1', family: 4 }, + { address: '::1', family: 6 } + ])); } }}); @@ -116,16 +112,23 @@ describe('ControlConnection', function () { 'localhost:9999'); }); - it('should resolve all IPv4 and IPv6 addresses provided by dns.resolve()', () => { + it('should resolve all IPv4 and IPv6 addresses using the operating system resolver', () => { const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { - cb(null, ['1', '2']); + resolve4: function () { + throw new Error('dns.resolve4() should not be used'); }, - resolve6: function (name, cb) { - cb(null, ['10', '20']); + resolve6: function () { + throw new Error('dns.resolve6() should not be used'); }, - lookup: function () { - throw new Error('dns.lookup() should not be used'); + lookup: function (name, options, cb) { + assert.strictEqual(name, 'localhost'); + assert.deepStrictEqual(options, { all: true }); + cb(null, [ + { address: '1', family: 4 }, + { address: '2', family: 4 }, + { address: '10', family: 6 }, + { address: '20', family: 6 } + ]); } }}); @@ -134,52 +137,24 @@ describe('ControlConnection', function () { [ '1:9042', '2:9042', '[10]:9042', '[20]:9042' ]); }); - it('should ignore IPv4 or IPv6 resolution errors', function () { + it('should handle system resolver errors', function () { const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { - cb(null, ['1', '2']); - }, - resolve6: function (name, cb) { + lookup: function (name, options, cb) { cb(new Error('Test error')); - }, - lookup: function () { - throw new Error('dns.lookup() should not be used'); } }}); - return testResolution(ControlConnectionMock, [ '1:9042', '2:9042']); + return testResolution(ControlConnectionMock, [], utils.emptyArray); }); - it('should use dns.lookup() as failover', () => { + it('should handle an empty system resolver result', () => { const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { - cb(new Error('Test error')); - }, - resolve6: function (name, cb) { - cb(new Error('Test error')); - }, lookup: function (name, options, cb) { - cb(null, [{ address: '123', family: 4 }]); - } - }}); - - return testResolution(ControlConnectionMock, [ '123:9042' ]); - }); - - it('should use dns.lookup() when no address was resolved', () => { - const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { - cb(null); - }, - resolve6: function (name, cb) { cb(null, []); - }, - lookup: function (name, options, cb) { - cb(null, [{ address: '1234', family: 4 }]); } }}); - return testResolution(ControlConnectionMock, [ '1234:9042' ]); + return testResolution(ControlConnectionMock, [], utils.emptyArray); }); it('should continue iterating through the hosts when borrowing a connection fails',async () => { @@ -284,19 +259,13 @@ describe('ControlConnection', function () { const resolvedAddresses = ['1','2']; const ControlConnectionMock = proxyquire('../../lib/control-connection', { dns: { - resolve4: function (name, cb) { + lookup: function (name, options, cb) { if (dnsWorks) { - cb(null, resolvedAddresses); + cb(null, resolvedAddresses.map(address => ({ address, family: 4 }))); } else { cb(null, []); } - }, - resolve6: function (name, cb) { - throw new Error('IPv6 resolution errors should be ignored'); - }, - lookup: function () { - throw new Error('dns.lookup() should not be used'); } }}); const cc = new ControlConnectionMock( @@ -590,4 +559,4 @@ function getContext(options) { return state.connection = getFakeConnection(endpoint, options.queryResults); } }; -} \ No newline at end of file +}