Skip to content

Commit 135b70e

Browse files
authored
tls: add tlsSocket.socket, to access the raw stream beneath TLS
Signed-off-by: Tim Perry <pimterry@gmail.com> PR-URL: #66598 Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent 3ab2db8 commit 135b70e

3 files changed

Lines changed: 82 additions & 0 deletions

File tree

‎doc/api/tls.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,27 @@ and their processing can be delayed due to packet loss or reordering. However,
16701670
smaller fragments add extra TLS framing bytes and CPU overhead, which may
16711671
decrease overall server throughput.
16721672

1673+
### `tlsSocket.socket`
1674+
1675+
<!-- YAML
1676+
added: REPLACEME
1677+
-->
1678+
1679+
* Type: {net.Socket|stream.Duplex|null}
1680+
1681+
The underlying socket or stream that this socket reads and writes TLS data
1682+
through. This is the `socket` passed to [`new tls.TLSSocket()`][] or
1683+
[`tls.connect()`][], or the connection given to a [`tls.Server`][] by its
1684+
`'connection'` event.
1685+
1686+
This is only `null` for client sockets that manage their own connection,
1687+
created by [`tls.connect()`][] with a `port` or `path` rather than a `socket`.
1688+
Connection details such as `remoteAddress` are then available directly on this
1689+
socket. Server sockets always have an underlying socket.
1690+
1691+
This remains available after the socket has been destroyed, for example to
1692+
identify the underlying connection from a [`'tlsClientError'`][] listener.
1693+
16731694
## `tls.checkServerIdentity(hostname, cert)`
16741695

16751696
<!-- YAML
@@ -2596,6 +2617,7 @@ added: v0.11.3
25962617
[`'secureConnect'`]: #event-secureconnect
25972618
[`'secureConnection'`]: #event-secureconnection
25982619
[`'session'`]: #event-session
2620+
[`'tlsClientError'`]: #event-tlsclienterror
25992621
[`--tls-cipher-list`]: cli.md#--tls-cipher-listlist
26002622
[`--use-bundled-ca`]: cli.md#--use-bundled-ca---use-openssl-ca
26012623
[`--use-openssl-ca`]: cli.md#--use-bundled-ca---use-openssl-ca
@@ -2611,6 +2633,7 @@ added: v0.11.3
26112633
[`net.Server`]: net.md#class-netserver
26122634
[`net.Socket`]: net.md#class-netsocket
26132635
[`net.createServer()`]: net.md#netcreateserveroptions-connectionlistener
2636+
[`new tls.TLSSocket()`]: #new-tlstlssocketsocket-options
26142637
[`server.addContext()`]: #serveraddcontexthostname-context
26152638
[`server.getTicketKeys()`]: #servergetticketkeys
26162639
[`server.listen()`]: net.md#serverlisten

‎lib/internal/tls/wrap.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@ function TLSSocket(socket, opts) {
700700
// Proxy for API compatibility
701701
this.ssl = this._handle; // C++ TLSWrap object
702702

703+
this.socket = socket ?? null;
704+
703705
this.on('error', this._tlsError);
704706

705707
this._init(socket, wrap);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const fixtures = require('../common/fixtures');
8+
const net = require('net');
9+
const tls = require('tls');
10+
const { duplexPair } = require('stream');
11+
12+
const key = fixtures.readKey('agent1-key.pem');
13+
const cert = fixtures.readKey('agent1-cert.pem');
14+
15+
// Server sockets over a plain connection expose that net.Socket.
16+
{
17+
const server = tls.createServer({ key, cert });
18+
let rawSocket;
19+
server.prependListener('connection', common.mustCall((socket) => {
20+
rawSocket = socket;
21+
}));
22+
server.on('secureConnection', common.mustCall((tlsSocket) => {
23+
assert.ok(rawSocket instanceof net.Socket);
24+
assert.strictEqual(tlsSocket.socket, rawSocket);
25+
tlsSocket.end();
26+
server.close();
27+
}));
28+
server.listen(0, common.mustCall(() => {
29+
const client = tls.connect({
30+
port: server.address().port,
31+
rejectUnauthorized: false,
32+
}, common.mustCall(() => {
33+
assert.strictEqual(client.socket, null);
34+
client.end();
35+
}));
36+
}));
37+
}
38+
39+
// Server sockets over any other stream expose that stream, and still do after
40+
// a failed handshake has destroyed them.
41+
{
42+
const server = tls.createServer({ key, cert, minVersion: 'TLSv1.3' });
43+
const [serverSide, clientSide] = duplexPair();
44+
server.on('secureConnection', common.mustNotCall());
45+
server.on('tlsClientError', common.mustCall((err, tlsSocket) => {
46+
assert.strictEqual(tlsSocket.socket, serverSide);
47+
setImmediate(common.mustCall(() => {
48+
assert.strictEqual(tlsSocket.destroyed, true);
49+
assert.strictEqual(tlsSocket.socket, serverSide);
50+
}));
51+
}));
52+
server.emit('connection', serverSide);
53+
54+
const client = tls.connect({ socket: clientSide, maxVersion: 'TLSv1.2' });
55+
assert.strictEqual(client.socket, clientSide);
56+
client.on('error', common.mustCall());
57+
}

0 commit comments

Comments
 (0)