Skip to main content

giong_tls/
lib.rs

1//! # giong-tls
2//!
3//! Server-side TLS for the [giong] thread-per-core runtime, built on
4//! [rustls] (TLS 1.2 and 1.3, `ring` for cryptography).
5//!
6//! ```no_run
7//! use giong::net::TcpListener;
8//! use giong_tls::TlsAcceptor;
9//!
10//! # fn main() -> std::io::Result<()> {
11//! let cert = std::fs::read("cert.pem")?;
12//! let key = std::fs::read("key.pem")?;
13//! let acceptor = TlsAcceptor::from_pem(&cert, &key)?;
14//!
15//! giong::block_on(async move {
16//!     let listener = TcpListener::bind("0.0.0.0:8443")?;
17//!     loop {
18//!         let tcp = listener.accept().await?;
19//!         let acceptor = acceptor.clone();
20//!         giong::spawn(async move {
21//!             let Ok(tls) = acceptor.accept(tcp).await else { return };
22//!             while let Ok(data) = tls.recv().await {
23//!                 if data.is_empty() {
24//!                     break; // closed by the peer
25//!                 }
26//!                 if tls.write_all(data).await.0.is_err() {
27//!                     break;
28//!                 }
29//!             }
30//!             let _ = tls.shutdown().await;
31//!         });
32//!     }
33//! })
34//! # }
35//! ```
36//!
37//! The API mirrors [`giong::net::TcpStream`]: [`TlsStream::recv`] returns the
38//! next piece of decrypted data and [`TlsStream::write_all`] takes a buffer by
39//! value and hands it back.
40
41#![warn(missing_docs)]
42
43use std::cell::{Cell, RefCell};
44use std::future::poll_fn;
45use std::io::{self, Read, Write};
46use std::mem;
47use std::net::Shutdown;
48use std::sync::Arc;
49use std::task::{Poll, Waker};
50use std::time::Duration;
51
52use giong::net::TcpStream;
53use giong::time::timeout;
54use rustls::pki_types::pem::PemObject;
55use rustls::pki_types::{CertificateDer, PrivateKeyDer};
56use rustls::{ServerConfig, ServerConnection};
57
58pub use rustls;
59
60fn invalid(e: impl std::fmt::Display) -> io::Error {
61    io::Error::new(io::ErrorKind::InvalidData, e.to_string())
62}
63
64/// Turns accepted TCP connections into TLS connections.
65///
66/// Cheap to clone; share one between all workers.
67#[derive(Clone)]
68pub struct TlsAcceptor {
69    config: Arc<ServerConfig>,
70    handshake_timeout: Duration,
71}
72
73impl TlsAcceptor {
74    /// An acceptor using an existing rustls configuration.
75    pub fn new(config: Arc<ServerConfig>) -> Self {
76        Self {
77            config,
78            handshake_timeout: Duration::from_secs(10),
79        }
80    }
81
82    /// An acceptor for one certificate chain and its private key, both in PEM
83    /// format. TLS 1.2 and 1.3 with rustls' default cipher suites, no client
84    /// authentication, and `http/1.1` offered through ALPN.
85    pub fn from_pem(cert_chain: &[u8], private_key: &[u8]) -> io::Result<Self> {
86        let certs = CertificateDer::pem_slice_iter(cert_chain)
87            .collect::<Result<Vec<_>, _>>()
88            .map_err(invalid)?;
89        if certs.is_empty() {
90            return Err(invalid("no certificate found in the PEM data"));
91        }
92        let key = PrivateKeyDer::from_pem_slice(private_key).map_err(invalid)?;
93        let provider = Arc::new(rustls::crypto::ring::default_provider());
94        let mut config = ServerConfig::builder_with_provider(provider)
95            .with_safe_default_protocol_versions()
96            .map_err(invalid)?
97            .with_no_client_auth()
98            .with_single_cert(certs, key)
99            .map_err(invalid)?;
100        config.alpn_protocols = vec![b"http/1.1".to_vec()];
101        Ok(Self::new(Arc::new(config)))
102    }
103
104    /// How long a client may take to complete the TLS handshake (default:
105    /// 10 s).
106    pub fn handshake_timeout(mut self, timeout: Duration) -> Self {
107        self.handshake_timeout = timeout;
108        self
109    }
110
111    /// Performs the TLS handshake on an accepted connection.
112    pub async fn accept(&self, tcp: TcpStream) -> io::Result<TlsStream> {
113        let conn = ServerConnection::new(self.config.clone()).map_err(invalid)?;
114        let stream = TlsStream {
115            tcp,
116            conn: RefCell::new(conn),
117            plain: RefCell::new(Vec::new()),
118            wire: RefCell::new(Vec::new()),
119            eof: Cell::new(false),
120            sending: Cell::new(false),
121            broken: Cell::new(false),
122            failed: Cell::new(None),
123            waiters: RefCell::new(Vec::new()),
124        };
125        let handshake = async {
126            while stream.conn.borrow().is_handshaking() {
127                stream.flush_tls().await?;
128                if !stream.conn.borrow().is_handshaking() {
129                    break;
130                }
131                if stream.eof.get() {
132                    return Err(io::ErrorKind::UnexpectedEof.into());
133                }
134                stream.pump().await?;
135            }
136            stream.flush_tls().await
137        };
138        match timeout(self.handshake_timeout, handshake).await {
139            Ok(Ok(())) => Ok(stream),
140            Ok(Err(e)) => Err(e),
141            Err(_) => Err(io::Error::new(
142                io::ErrorKind::TimedOut,
143                "TLS handshake timed out",
144            )),
145        }
146    }
147}
148
149impl std::fmt::Debug for TlsAcceptor {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        f.debug_struct("TlsAcceptor")
152            .field("handshake_timeout", &self.handshake_timeout)
153            .finish_non_exhaustive()
154    }
155}
156
157/// A TLS connection over a [`TcpStream`].
158pub struct TlsStream {
159    tcp: TcpStream,
160    conn: RefCell<ServerConnection>,
161    /// Decrypted data not handed out yet.
162    plain: RefCell<Vec<u8>>,
163    /// Scratch buffer for outgoing TLS records, reused between writes.
164    wire: RefCell<Vec<u8>>,
165    /// The peer closed the connection (with or without `close_notify`).
166    eof: Cell<bool>,
167    /// A task is writing TLS records to the socket. Records must not
168    /// interleave, so other tasks wait for it in `waiters`.
169    sending: Cell<bool>,
170    /// A write to the socket failed or was cancelled half way: the record
171    /// stream is cut in the middle and nothing more can be sent.
172    broken: Cell<bool>,
173    /// A receive error held back until the data before it has been handed out.
174    failed: Cell<Option<io::Error>>,
175    /// Tasks waiting for `sending` to clear.
176    waiters: RefCell<Vec<Waker>>,
177}
178
179/// Held while TLS records are being written to the socket.
180struct Sending<'a> {
181    stream: &'a TlsStream,
182    complete: bool,
183}
184
185impl Drop for Sending<'_> {
186    fn drop(&mut self) {
187        if !self.complete {
188            self.stream.broken.set(true);
189        }
190        self.stream.sending.set(false);
191        let waiters = mem::take(&mut *self.stream.waiters.borrow_mut());
192        for waker in waiters {
193            waker.wake();
194        }
195    }
196}
197
198impl TlsStream {
199    /// Sends every TLS record rustls has queued.
200    async fn flush_tls(&self) -> io::Result<()> {
201        loop {
202            if !self.conn.borrow().wants_write() {
203                return Ok(());
204            }
205            if self.broken.get() {
206                return Err(io::Error::new(
207                    io::ErrorKind::BrokenPipe,
208                    "a TLS write failed or was cancelled; nothing more can be sent",
209                ));
210            }
211            if self.sending.get() {
212                // Another task is sending. Wait for it, then send what is
213                // left (it may already have sent this task's records too).
214                poll_fn(|cx| {
215                    if !self.sending.get() {
216                        return Poll::Ready(());
217                    }
218                    self.waiters.borrow_mut().push(cx.waker().clone());
219                    Poll::Pending
220                })
221                .await;
222                continue;
223            }
224            let mut wire = mem::take(&mut *self.wire.borrow_mut());
225            {
226                let mut conn = self.conn.borrow_mut();
227                while conn.wants_write() {
228                    conn.write_tls(&mut wire)?;
229                }
230            }
231            self.sending.set(true);
232            let mut sending = Sending {
233                stream: self,
234                complete: false,
235            };
236            let (res, mut wire) = self.tcp.write_all(wire).await;
237            res?;
238            sending.complete = true;
239            drop(sending);
240            wire.clear();
241            *self.wire.borrow_mut() = wire;
242        }
243    }
244
245    /// Receives one chunk of TLS data, decrypts it into `plain`, and sends
246    /// whatever the protocol wants to send in response.
247    async fn pump(&self) -> io::Result<()> {
248        // Cancelling here loses nothing: `recv` is cancel safe.
249        let chunk = self.tcp.recv().await?;
250        if chunk.is_empty() {
251            self.eof.set(true);
252            return Ok(());
253        }
254        let result = {
255            let mut conn = self.conn.borrow_mut();
256            let mut plain = self.plain.borrow_mut();
257            let mut input = &chunk[..];
258            let mut result = Ok(());
259            while !input.is_empty() {
260                // rustls buffers a bounded amount of plaintext, so it has to
261                // be drained as the records are fed in.
262                match conn.read_tls(&mut input) {
263                    Ok(0) => {
264                        result = Err(invalid("rustls accepted no more TLS data"));
265                        break;
266                    }
267                    Ok(_) => {}
268                    Err(e) => {
269                        result = Err(e);
270                        break;
271                    }
272                }
273                match conn.process_new_packets() {
274                    Ok(state) => {
275                        let n = state.plaintext_bytes_to_read();
276                        if n > 0 {
277                            let at = plain.len();
278                            plain.resize(at + n, 0);
279                            if let Err(e) = conn.reader().read_exact(&mut plain[at..]) {
280                                plain.truncate(at);
281                                result = Err(e);
282                                break;
283                            }
284                        }
285                        if state.peer_has_closed() {
286                            self.eof.set(true);
287                            break;
288                        }
289                    }
290                    Err(e) => {
291                        result = Err(invalid(e));
292                        break;
293                    }
294                }
295            }
296            result
297        };
298        drop(chunk);
299        // Handshake messages, key updates, or the alert describing `result`.
300        // Not being able to send them is no reason to withhold what was
301        // received, if sending has failed before.
302        if self.broken.get() {
303            return result;
304        }
305        let flushed = self.flush_tls().await;
306        result.and(flushed)
307    }
308
309    /// Receives decrypted data. An empty buffer means that the peer has closed
310    /// the connection, with a `close_notify` alert or by closing the socket.
311    ///
312    /// Cancel safe: dropping the future loses no data. (If it is dropped at
313    /// the rare moment a TLS protocol message is being sent, sending fails
314    /// from then on instead of continuing on a broken stream.)
315    ///
316    /// One task may receive while another one sends.
317    pub async fn recv(&self) -> io::Result<Vec<u8>> {
318        loop {
319            {
320                let mut plain = self.plain.borrow_mut();
321                if !plain.is_empty() {
322                    return Ok(mem::take(&mut *plain));
323                }
324            }
325            if let Some(e) = self.failed.take() {
326                return Err(e);
327            }
328            if self.eof.get() {
329                return Ok(Vec::new());
330            }
331            if let Err(e) = self.pump().await {
332                // Data that arrived intact before the failure comes first.
333                if self.plain.borrow().is_empty() {
334                    return Err(e);
335                }
336                self.failed.set(Some(e));
337            }
338        }
339    }
340
341    /// Encrypts and sends all of `buf`, then hands the buffer back.
342    ///
343    /// Not cancel safe: if the future is dropped, part of `buf` may have been
344    /// sent, and if that happened in the middle of a TLS record the
345    /// connection cannot send any more: later writes return an error, while
346    /// data the peer already sent can still be received.
347    pub async fn write_all(&self, buf: Vec<u8>) -> (io::Result<()>, Vec<u8>) {
348        let mut sent = 0;
349        while sent < buf.len() {
350            // rustls accepts a bounded amount per call; send as it fills up.
351            let accepted = self.conn.borrow_mut().writer().write(&buf[sent..]);
352            match accepted {
353                Ok(n) => sent += n,
354                Err(e) => return (Err(e), buf),
355            }
356            if let Err(e) = self.flush_tls().await {
357                return (Err(e), buf);
358            }
359        }
360        (Ok(()), buf)
361    }
362
363    /// Sends the TLS `close_notify` alert and closes the sending direction.
364    pub async fn shutdown(&self) -> io::Result<()> {
365        self.conn.borrow_mut().send_close_notify();
366        self.flush_tls().await?;
367        self.tcp.shutdown(Shutdown::Write)
368    }
369
370    /// The protocol agreed on through ALPN, if any (for example `http/1.1`).
371    pub fn alpn_protocol(&self) -> Option<Vec<u8>> {
372        self.conn.borrow().alpn_protocol().map(<[u8]>::to_vec)
373    }
374
375    /// The underlying TCP connection.
376    pub fn tcp(&self) -> &TcpStream {
377        &self.tcp
378    }
379}
380
381impl std::fmt::Debug for TlsStream {
382    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383        f.debug_struct("TlsStream")
384            .field("tcp", &self.tcp)
385            .finish_non_exhaustive()
386    }
387}