|
19 | 19 | */ |
20 | 20 |
|
21 | 21 | import { TLSSocket, type TLSSocketOptions, connect } from 'node:tls' |
22 | | -import { serviceCapabilities } from '@libp2p/interface' |
| 22 | +import { InvalidCryptoExchangeError, serviceCapabilities } from '@libp2p/interface' |
23 | 23 | import { HandshakeTimeoutError } from './errors.js' |
24 | 24 | import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js' |
25 | 25 | import { PROTOCOL } from './index.js' |
26 | 26 | import type { TLSComponents } from './index.js' |
27 | | -import type { MultiaddrConnection, ConnectionEncrypter, SecuredConnection, Logger, SecureConnectionOptions, CounterGroup } from '@libp2p/interface' |
| 27 | +import type { MultiaddrConnection, ConnectionEncrypter, SecuredConnection, Logger, SecureConnectionOptions, CounterGroup, StreamMuxerFactory } from '@libp2p/interface' |
28 | 28 | import type { Duplex } from 'it-stream-types' |
29 | 29 | import type { Uint8ArrayList } from 'uint8arraylist' |
30 | 30 |
|
@@ -88,14 +88,41 @@ export class TLS implements ConnectionEncrypter { |
88 | 88 | * Encrypt connection |
89 | 89 | */ |
90 | 90 | async _encrypt <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (conn: Stream, isServer: boolean, options?: SecureConnectionOptions): Promise<SecuredConnection<Stream>> { |
| 91 | + let streamMuxer: StreamMuxerFactory | undefined |
| 92 | + |
91 | 93 | const opts: TLSSocketOptions = { |
92 | 94 | ...await generateCertificate(this.components.privateKey), |
93 | 95 | isServer, |
94 | 96 | // require TLS 1.3 or later |
95 | 97 | minVersion: 'TLSv1.3', |
96 | 98 | maxVersion: 'TLSv1.3', |
97 | 99 | // accept self-signed certificates |
98 | | - rejectUnauthorized: false |
| 100 | + rejectUnauthorized: false, |
| 101 | + |
| 102 | + // early negotiation of muxer via ALPN protocols |
| 103 | + ALPNProtocols: [ |
| 104 | + ...this.components.upgrader.getStreamMuxers().keys(), |
| 105 | + 'libp2p' |
| 106 | + ], |
| 107 | + ALPNCallback: ({ protocols }) => { |
| 108 | + this.log.trace('received protocols %s', protocols) |
| 109 | + let chosenProtocol: string | undefined |
| 110 | + |
| 111 | + for (const protocol of protocols) { |
| 112 | + if (protocol === 'libp2p') { |
| 113 | + chosenProtocol = 'libp2p' |
| 114 | + } |
| 115 | + |
| 116 | + streamMuxer = this.components.upgrader.getStreamMuxers().get(protocol) |
| 117 | + |
| 118 | + if (streamMuxer != null) { |
| 119 | + chosenProtocol = protocol |
| 120 | + break |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + return chosenProtocol |
| 125 | + } |
99 | 126 | } |
100 | 127 |
|
101 | 128 | let socket: TLSSocket |
@@ -131,12 +158,38 @@ export class TLS implements ConnectionEncrypter { |
131 | 158 | .then(remotePeer => { |
132 | 159 | this.log('remote certificate ok, remote peer %p', remotePeer) |
133 | 160 |
|
| 161 | + if (!isServer && typeof socket.alpnProtocol === 'string') { |
| 162 | + streamMuxer = this.components.upgrader.getStreamMuxers().get(socket.alpnProtocol) |
| 163 | + |
| 164 | + if (streamMuxer == null) { |
| 165 | + this.log.error('selected muxer that did not exist') |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + // 'libp2p' is a special protocol - if it's sent the remote does not |
| 170 | + // support early muxer negotiation |
| 171 | + if (!isServer && typeof socket.alpnProtocol === 'string' && socket.alpnProtocol !== 'libp2p') { |
| 172 | + this.log.trace('got early muxer', socket.alpnProtocol) |
| 173 | + streamMuxer = this.components.upgrader.getStreamMuxers().get(socket.alpnProtocol) |
| 174 | + |
| 175 | + if (streamMuxer == null) { |
| 176 | + const err = new InvalidCryptoExchangeError(`Selected muxer ${socket.alpnProtocol} did not exist`) |
| 177 | + this.log.error(`Selected muxer ${socket.alpnProtocol} did not exist - %e`, err) |
| 178 | + |
| 179 | + if (isAbortable(conn)) { |
| 180 | + conn.abort(err) |
| 181 | + reject(err) |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | + |
134 | 186 | resolve({ |
135 | 187 | remotePeer, |
136 | 188 | conn: { |
137 | 189 | ...conn, |
138 | 190 | ...streamToIt(socket) |
139 | | - } |
| 191 | + }, |
| 192 | + streamMuxer |
140 | 193 | }) |
141 | 194 | }) |
142 | 195 | .catch((err: Error) => { |
|
0 commit comments