1use std::{
9 io::{self, Read, Write},
10 sync::Arc,
11 time::{Duration, Instant},
12};
13
14use crate::transport::SyncTransport;
15
16#[derive(Clone)]
18pub struct TlsClientConfig {
19 pub(crate) config: Arc<rustls::ClientConfig>,
20}
21
22impl TlsClientConfig {
23 pub fn new(roots: rustls::RootCertStore) -> io::Result<Self> {
28 let config = rustls::ClientConfig::builder_with_provider(Arc::new(
29 rustls::crypto::ring::default_provider(),
30 ))
31 .with_protocol_versions(&[&rustls::version::TLS13])
32 .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?
33 .with_root_certificates(roots)
34 .with_no_client_auth();
35 Ok(Self::from_rustls(config))
36 }
37
38 pub fn from_rustls(mut config: rustls::ClientConfig) -> Self {
43 config.alpn_protocols = vec![b"h2".to_vec()];
44 config.enable_early_data = false;
45 Self {
46 config: Arc::new(config),
47 }
48 }
49
50 pub fn get_ref(&self) -> &rustls::ClientConfig {
52 &self.config
53 }
54}
55
56#[derive(Clone)]
58pub struct TlsServerConfig {
59 pub(crate) config: Arc<rustls::ServerConfig>,
60}
61
62impl TlsServerConfig {
63 pub fn new(
71 cert_chain: Vec<rustls::pki_types::CertificateDer<'static>>,
72 private_key: rustls::pki_types::PrivateKeyDer<'static>,
73 ) -> io::Result<Self> {
74 let config = rustls::ServerConfig::builder_with_provider(Arc::new(
75 rustls::crypto::ring::default_provider(),
76 ))
77 .with_protocol_versions(&[&rustls::version::TLS13])
78 .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?
79 .with_no_client_auth()
80 .with_single_cert(cert_chain, private_key)
81 .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?;
82 Ok(Self::from_rustls(config))
83 }
84
85 pub fn from_rustls(mut config: rustls::ServerConfig) -> Self {
90 config.alpn_protocols = vec![b"h2".to_vec()];
91 config.max_early_data_size = 0;
92 Self {
93 config: Arc::new(config),
94 }
95 }
96
97 pub fn get_ref(&self) -> &rustls::ServerConfig {
99 &self.config
100 }
101}
102
103pub struct TlsTransport<C, T: SyncTransport> {
111 stream: rustls::StreamOwned<C, T>,
112 state: HandshakeState,
113 write_closed: bool,
114}
115
116#[derive(Clone, Copy)]
117enum HandshakeState {
118 Negotiating,
119 Ready,
120 Failed(io::ErrorKind),
121}
122
123impl<T: SyncTransport> TlsTransport<rustls::ClientConnection, T> {
124 pub fn client(
129 sock: T,
130 config: &TlsClientConfig,
131 server_name: rustls::pki_types::ServerName<'static>,
132 ) -> io::Result<Self> {
133 let conn = rustls::ClientConnection::new(Arc::clone(&config.config), server_name)
134 .map_err(io::Error::other)?;
135 Ok(Self {
136 stream: rustls::StreamOwned::new(conn, sock),
137 state: HandshakeState::Negotiating,
138 write_closed: false,
139 })
140 }
141}
142
143impl<T: SyncTransport> TlsTransport<rustls::ServerConnection, T> {
144 pub fn server(sock: T, config: &TlsServerConfig) -> io::Result<Self> {
149 let conn =
150 rustls::ServerConnection::new(Arc::clone(&config.config)).map_err(io::Error::other)?;
151 Ok(Self {
152 stream: rustls::StreamOwned::new(conn, sock),
153 state: HandshakeState::Negotiating,
154 write_closed: false,
155 })
156 }
157}
158
159impl<C, T: SyncTransport> TlsTransport<C, T> {
160 pub fn get_ref(&self) -> &T {
162 &self.stream.sock
163 }
164}
165
166fn remaining_handshake_time(deadline: Option<Instant>) -> io::Result<Option<Duration>> {
167 deadline
168 .map(|deadline| {
169 deadline
170 .checked_duration_since(Instant::now())
171 .filter(|left| !left.is_zero())
172 .ok_or_else(|| io::Error::new(io::ErrorKind::TimedOut, "tls handshake timed out"))
173 })
174 .transpose()
175}
176
177fn prepare_handshake_io<T: SyncTransport>(
178 transport: &mut T,
179 deadline: Option<Instant>,
180) -> io::Result<()> {
181 if let Some(remaining) = remaining_handshake_time(deadline)? {
182 transport.set_read_timeout(Some(remaining))?;
183 transport.set_write_timeout(Some(remaining))?;
184 }
185 Ok(())
186}
187
188macro_rules! impl_tls_transport {
189 ($connection:ty, $drain_final_output:expr) => {
190 impl<T: SyncTransport> TlsTransport<$connection, T> {
191 pub fn handshake(&mut self) -> io::Result<()> {
200 self.handshake_until(None)
201 }
202
203 pub fn handshake_until(&mut self, deadline: Option<Instant>) -> io::Result<()> {
216 match self.state {
217 HandshakeState::Ready => return Ok(()),
218 HandshakeState::Failed(kind) => {
219 return Err(io::Error::new(kind, "TLS handshake previously failed"));
220 }
221 HandshakeState::Negotiating => {}
222 }
223 let result = if deadline.is_some() {
224 remaining_handshake_time(deadline)?;
225 let read_timeout = self.stream.sock.read_timeout()?;
226 let write_timeout = self.stream.sock.write_timeout()?;
227 let result = self.drive_handshake(deadline);
228 let restored_read = self.stream.sock.set_read_timeout(read_timeout);
229 let restored_write = self.stream.sock.set_write_timeout(write_timeout);
230 if restored_read.is_err() || restored_write.is_err() {
231 let kind = restored_read
232 .as_ref()
233 .err()
234 .or(restored_write.as_ref().err())
235 .map_or(io::ErrorKind::Other, io::Error::kind);
236 self.state = HandshakeState::Failed(kind);
237 return Err(io::Error::new(
238 kind,
239 format!(
240 "restore TLS transport timeouts: read={restored_read:?}, \
241 write={restored_write:?}; handshake={result:?}"
242 ),
243 ));
244 }
245 result
246 } else {
247 self.drive_handshake(None)
248 };
249 if let Err(error) = result {
250 let error = if error.kind() == io::ErrorKind::TimedOut && deadline.is_some() {
251 remaining_handshake_time(deadline).err().unwrap_or(error)
252 } else {
253 error
254 };
255 if !matches!(
256 error.kind(),
257 io::ErrorKind::WouldBlock
258 | io::ErrorKind::Interrupted
259 | io::ErrorKind::TimedOut
260 ) {
261 self.state = HandshakeState::Failed(error.kind());
262 }
263 return Err(error);
264 }
265 self.state = HandshakeState::Ready;
266 Ok(())
267 }
268
269 fn drive_handshake(&mut self, deadline: Option<Instant>) -> io::Result<()> {
270 while self.stream.conn.is_handshaking() {
271 self.drain_handshake_output(deadline)?;
272 prepare_handshake_io(&mut self.stream.sock, deadline)?;
273 if self.stream.conn.read_tls(&mut self.stream.sock)? == 0 {
274 return Err(io::ErrorKind::UnexpectedEof.into());
275 }
276 if let Err(error) = self.stream.conn.process_new_packets() {
277 drop(self.drain_handshake_output(deadline));
279 return Err(io::Error::new(io::ErrorKind::InvalidData, error));
280 }
281 }
282 if self.stream.conn.alpn_protocol() != Some(b"h2".as_slice()) {
283 return Err(io::Error::new(
284 io::ErrorKind::InvalidData,
285 "TLS peer did not negotiate HTTP/2 ALPN",
286 ));
287 }
288 if $drain_final_output {
291 self.drain_handshake_output(deadline)?;
292 }
293 Ok(())
294 }
295
296 fn drain_handshake_output(&mut self, deadline: Option<Instant>) -> io::Result<()> {
297 while self.stream.conn.wants_write() {
298 prepare_handshake_io(&mut self.stream.sock, deadline)?;
299 if self.stream.conn.write_tls(&mut self.stream.sock)? == 0 {
300 return Err(io::ErrorKind::WriteZero.into());
301 }
302 }
303 prepare_handshake_io(&mut self.stream.sock, deadline)?;
304 self.stream.sock.flush()
305 }
306
307 pub fn negotiated_alpn(&self) -> Option<&[u8]> {
309 matches!(self.state, HandshakeState::Ready)
310 .then(|| self.stream.conn.alpn_protocol())
311 .flatten()
312 }
313
314 pub fn protocol_version(&self) -> Option<rustls::ProtocolVersion> {
316 matches!(self.state, HandshakeState::Ready)
317 .then(|| self.stream.conn.protocol_version())
318 .flatten()
319 }
320
321 pub fn close_notify(&mut self) -> io::Result<()> {
327 self.handshake()?;
328 self.write_closed = true;
329 self.stream.conn.send_close_notify();
330 self.stream.flush()
331 }
332 }
333
334 impl<T: SyncTransport> Read for TlsTransport<$connection, T> {
335 fn read(&mut self, destination: &mut [u8]) -> io::Result<usize> {
336 if destination.is_empty() {
337 return Ok(0);
338 }
339 self.handshake()?;
340 self.stream.read(destination)
341 }
342 }
343
344 impl<T: SyncTransport> Write for TlsTransport<$connection, T> {
345 fn write(&mut self, source: &[u8]) -> io::Result<usize> {
346 if source.is_empty() {
347 return Ok(0);
348 }
349 self.handshake()?;
350 if self.write_closed {
351 return Err(io::Error::new(
352 io::ErrorKind::BrokenPipe,
353 "TLS write direction is closed",
354 ));
355 }
356 self.stream.write(source)
359 }
360
361 fn flush(&mut self) -> io::Result<()> {
362 self.handshake()?;
363 self.stream.flush()
364 }
365 }
366
367 impl<T: SyncTransport> SyncTransport for TlsTransport<$connection, T> {
368 fn set_nonblocking(&mut self, nonblocking: bool) -> io::Result<()> {
369 self.stream.sock.set_nonblocking(nonblocking)
370 }
371
372 fn set_read_timeout(&mut self, timeout: Option<Duration>) -> io::Result<()> {
373 self.stream.sock.set_read_timeout(timeout)
374 }
375
376 fn set_write_timeout(&mut self, timeout: Option<Duration>) -> io::Result<()> {
377 self.stream.sock.set_write_timeout(timeout)
378 }
379
380 fn read_timeout(&self) -> io::Result<Option<Duration>> {
381 self.stream.sock.read_timeout()
382 }
383
384 fn write_timeout(&self) -> io::Result<Option<Duration>> {
385 self.stream.sock.write_timeout()
386 }
387
388 fn peer_endpoint(&self) -> io::Result<String> {
389 self.stream.sock.peer_endpoint()
390 }
391 }
392 };
393}
394
395impl_tls_transport!(rustls::ClientConnection, true);
396impl_tls_transport!(rustls::ServerConnection, false);
397
398#[cfg(test)]
399#[path = "../tests/unit/tls.rs"]
400mod tests;