Commit 1ef43aea167 for nodejs
commit 1ef43aea1670900e01b64d901346ae0090cbe649
Author: Harjoth Khara <harjoth.khara@gmail.com>
Date: Wed Oct 28 12:34:49 2026 -0700
net,tls: fix session reuse with provided sockets
Preserve literal IP hosts on net.Socket before connection. This keeps
wrapped TLS sessions bound to the correct peer when tls.connect() uses a
provided socket.
Fixes: https://github.com/nodejs/node/issues/64402
Signed-off-by: harjoth <harjoth.khara@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/64430
Reviewed-By: Tim Perry <pimterry@gmail.com>
diff --git a/lib/net.js b/lib/net.js
index 5d20ea3ff45..ae7cca8ed6e 100644
--- a/lib/net.js
+++ b/lib/net.js
@@ -1887,6 +1887,8 @@ function lookupAndConnect(self, options) {
// If host is an IP, skip performing a lookup
const addressType = isIP(host);
+ // Retain the host before the IP fast path for TLS session identity binding.
+ self._host = host;
if (addressType) {
// An adopted BoundSocket needs no lookup: issue connect(2) in-tick so the
// kernel-resolved concrete source address is observable synchronously.
@@ -1928,7 +1930,6 @@ function lookupAndConnect(self, options) {
debug('connect: find host', host);
debug('connect: dns options', dnsopts);
- self._host = host;
const lookup = options.lookup || dns.lookup;
if (dnsopts.family !== 4 && dnsopts.family !== 6 && !localAddress && autoSelectFamily) {
diff --git a/test/parallel/test-tls-session-reuse-provided-socket.js b/test/parallel/test-tls-session-reuse-provided-socket.js
new file mode 100644
index 00000000000..e23d4424463
--- /dev/null
+++ b/test/parallel/test-tls-session-reuse-provided-socket.js
@@ -0,0 +1,99 @@
+// Flags: --expose-internals
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const fixtures = require('../common/fixtures');
+const { hasMultiLocalhost } = require('../common/net');
+const net = require('net');
+const tls = require('tls');
+const { once } = require('events');
+
+async function connectWithSession(
+ socket, session, maxVersion, expectedReuse = true, servername,
+) {
+ const tlsSocket = tls.connect({
+ socket,
+ session,
+ servername,
+ maxVersion,
+ rejectUnauthorized: false,
+ });
+ const close = once(tlsSocket, 'close');
+ tlsSocket.resume();
+ await once(tlsSocket, 'secureConnect');
+ assert.strictEqual(tlsSocket.isSessionReused(), expectedReuse);
+ await close;
+}
+
+async function connectAndCaptureSession(
+ port, maxVersion, host = '127.0.0.1', lookup,
+) {
+ const socket = tls.connect({
+ host,
+ lookup,
+ port,
+ maxVersion,
+ rejectUnauthorized: false,
+ });
+ const close = once(socket, 'close');
+ const sessionPromise = once(socket, 'session').then(([session]) => session);
+ socket.resume();
+ await once(socket, 'secureConnect');
+ assert.strictEqual(socket.isSessionReused(), false);
+ const session = await sessionPromise;
+ assert(Buffer.isBuffer(session));
+ await close;
+ return session;
+}
+
+async function testVersion(maxVersion) {
+ const server = tls.createServer({
+ key: fixtures.readKey('agent1-key.pem'),
+ cert: fixtures.readKey('agent1-cert.pem'),
+ minVersion: maxVersion,
+ maxVersion,
+ }, (socket) => socket.end('ok'));
+
+ server.listen(0, '0.0.0.0');
+ await once(server, 'listening');
+ const { port } = server.address();
+ const session = await connectAndCaptureSession(port, maxVersion);
+
+ let socket = net.connect({ host: '127.0.0.1', port });
+ await once(socket, 'connect');
+ await connectWithSession(socket, session, maxVersion);
+
+ socket = net.connect({ host: '127.0.0.1', port });
+ await connectWithSession(socket, session, maxVersion);
+
+ if (hasMultiLocalhost()) {
+ socket = net.connect({ host: '127.0.0.2', port });
+ await connectWithSession(socket, session, maxVersion, false);
+ }
+
+ const lookup = (_hostname, _options, callback) => {
+ if (_options.all) {
+ callback(null, [{ address: '127.0.0.1', family: 4 }]);
+ } else {
+ callback(null, '127.0.0.1', 4);
+ }
+ };
+ const hostnameSession = await connectAndCaptureSession(
+ port, maxVersion, 'localhost', lookup);
+ socket = net.connect({ host: 'localhost', port, lookup });
+ await connectWithSession(socket, hostnameSession, maxVersion);
+
+ socket = net.connect({ host: '127.0.0.1', port });
+ await connectWithSession(socket, session, maxVersion, false, 'localhost');
+
+ server.close();
+ await once(server, 'close');
+}
+
+(async function() {
+ await testVersion('TLSv1.2');
+ await testVersion('TLSv1.3');
+})().then(common.mustCall());