Skip to main content

rustls/client/
connection.rs

1use alloc::vec::Vec;
2use core::ops::Deref;
3use core::{fmt, mem};
4use std::io;
5
6use pki_types::{FipsStatus, ServerName};
7
8use super::config::ClientConfig;
9use super::hs::ClientHelloInput;
10use crate::TlsInputBuffer;
11use crate::client::EchStatus;
12use crate::common_state::{CommonState, ConnectionOutputs, EarlyDataEvent, Event, Protocol, Side};
13use crate::conn::private::SideOutput;
14use crate::conn::split::SplitConnection;
15use crate::conn::{
16    Connection, ConnectionCommon, ConnectionCore, IoState, KeyingMaterialExporter, Reader,
17    SideCommonOutput, SideData, Writer,
18};
19#[cfg(doc)]
20use crate::crypto;
21use crate::enums::ApplicationProtocol;
22use crate::error::Error;
23use crate::log::trace;
24use crate::msgs::ClientExtensionsInput;
25use crate::quic::QuicOutput;
26use crate::suites::ExtractedSecrets;
27use crate::sync::Arc;
28
29/// This represents a single TLS client connection.
30pub struct ClientConnection {
31    inner: ConnectionCommon<ClientSide>,
32}
33
34impl fmt::Debug for ClientConnection {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        f.debug_struct("ClientConnection")
37            .finish_non_exhaustive()
38    }
39}
40
41impl ClientConnection {
42    /// Split a post-handshake connection into a [`SplitConnection`].
43    ///
44    /// This allows the two directions (transmit and receive) of the connection to be progressed
45    /// separately (including by different threads, which would allow dedicating a CPU core for each
46    /// direction rather than one per connection; this can dramatically improve performance for
47    /// full-duplex protocols).
48    ///
49    /// It also separates out the [`ConnectionOutputs`] which gives the application direct control
50    /// of how long this is kept.
51    ///
52    /// This fails if:
53    ///
54    /// - the handshake is not complete. Check with [`Connection::is_handshaking()`].
55    /// - there is any buffered application data.  Check with [`Connection::reader()`].
56    /// - there is any buffered TLS data to send.  Obtain it first with [`Connection::write_tls()`].
57    pub fn split(self) -> Result<SplitConnection<ClientSide>, Error> {
58        self.inner.split()
59    }
60
61    /// Returns an `io::Write` implementer you can write bytes to
62    /// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server.
63    ///
64    /// This returns None in many circumstances when the capability to
65    /// send early data is not available, including but not limited to:
66    ///
67    /// - The server hasn't been talked to previously.
68    /// - The server does not support resumption.
69    /// - The server does not support early data.
70    /// - The resumption data for the server has expired.
71    ///
72    /// The server specifies a maximum amount of early data.  You can
73    /// learn this limit through the returned object, and writes through
74    /// it will process only this many bytes.
75    ///
76    /// The server can choose not to accept any sent early data --
77    /// in this case the data is lost but the connection continues.  You
78    /// can tell this happened using `is_early_data_accepted`.
79    pub fn early_data(&mut self) -> Option<WriteEarlyData<'_>> {
80        if self
81            .inner
82            .core
83            .side
84            .early_data
85            .is_enabled()
86        {
87            Some(WriteEarlyData::new(self))
88        } else {
89            None
90        }
91    }
92
93    /// Returns True if the server signalled it will process early data.
94    ///
95    /// If you sent early data and this returns false at the end of the
96    /// handshake then the server will not process the data.  This
97    /// is not an error, but you may wish to resend the data.
98    pub fn is_early_data_accepted(&self) -> bool {
99        self.inner.core.is_early_data_accepted()
100    }
101
102    /// Return the connection's Encrypted Client Hello (ECH) status.
103    pub fn ech_status(&self) -> EchStatus {
104        self.inner.core.side.ech_status
105    }
106
107    fn write_early_data(&mut self, data: &[u8]) -> io::Result<usize> {
108        self.inner
109            .core
110            .side
111            .early_data
112            .check_write(data.len())
113            .map(|sz| {
114                self.inner
115                    .send
116                    .send_early_plaintext(&data[..sz])
117            })
118    }
119
120    /// Returns the number of TLS1.3 tickets that have been received.
121    pub fn tls13_tickets_received(&self) -> u32 {
122        self.inner
123            .core
124            .common
125            .recv
126            .tls13_tickets_received
127    }
128}
129
130impl Connection for ClientConnection {
131    fn write_tls(&mut self, wr: &mut dyn io::Write) -> Result<usize, io::Error> {
132        self.inner.write_tls(wr)
133    }
134
135    fn wants_read(&self) -> bool {
136        self.inner.wants_read()
137    }
138
139    fn wants_write(&self) -> bool {
140        self.inner.wants_write()
141    }
142
143    fn reader(&mut self) -> Reader<'_> {
144        self.inner.reader()
145    }
146
147    fn writer(&mut self) -> Writer<'_> {
148        self.inner.writer()
149    }
150
151    fn process_new_packets(&mut self, input: &mut dyn TlsInputBuffer) -> Result<IoState, Error> {
152        self.inner.process_new_packets(input)
153    }
154
155    fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
156        self.inner.exporter()
157    }
158
159    fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> {
160        self.inner.dangerous_extract_secrets()
161    }
162
163    fn set_buffer_limit(&mut self, limit: Option<usize>) {
164        self.inner.set_buffer_limit(limit)
165    }
166
167    fn set_plaintext_buffer_limit(&mut self, limit: Option<usize>) {
168        self.inner
169            .set_plaintext_buffer_limit(limit)
170    }
171
172    fn refresh_traffic_keys(&mut self) -> Result<(), Error> {
173        self.inner.refresh_traffic_keys()
174    }
175
176    fn send_close_notify(&mut self) {
177        self.inner.send_close_notify();
178    }
179
180    fn is_handshaking(&self) -> bool {
181        self.inner.is_handshaking()
182    }
183
184    fn fips(&self) -> FipsStatus {
185        self.inner.fips
186    }
187}
188
189impl Deref for ClientConnection {
190    type Target = ConnectionOutputs;
191
192    fn deref(&self) -> &Self::Target {
193        &self.inner
194    }
195}
196
197/// Builder for [`ClientConnection`] values.
198///
199/// Create one with [`ClientConfig::connect()`].
200pub struct ClientConnectionBuilder {
201    pub(crate) config: Arc<ClientConfig>,
202    pub(crate) name: ServerName<'static>,
203    pub(crate) alpn_protocols: Option<Vec<ApplicationProtocol<'static>>>,
204}
205
206impl ClientConnectionBuilder {
207    /// Specify the ALPN protocols to use for this connection.
208    pub fn with_alpn(mut self, alpn_protocols: Vec<ApplicationProtocol<'static>>) -> Self {
209        self.alpn_protocols = Some(alpn_protocols);
210        self
211    }
212
213    /// Finalize the builder and create the `ClientConnection`.
214    pub fn build(self) -> Result<ClientConnection, Error> {
215        let Self {
216            config,
217            name,
218            alpn_protocols,
219        } = self;
220
221        let alpn_protocols = alpn_protocols.unwrap_or_else(|| config.alpn_protocols.clone());
222        Ok(ClientConnection {
223            inner: ConnectionCommon::new(ConnectionCore::for_client(
224                config,
225                name,
226                ClientExtensionsInput::from_alpn(alpn_protocols),
227                None,
228                Protocol::Tcp,
229            )?),
230        })
231    }
232}
233
234/// Allows writing of early data in resumed TLS 1.3 connections.
235///
236/// "Early data" is also known as "0-RTT data".
237///
238/// This type implements [`io::Write`].
239pub struct WriteEarlyData<'a> {
240    sess: &'a mut ClientConnection,
241}
242
243impl<'a> WriteEarlyData<'a> {
244    fn new(sess: &'a mut ClientConnection) -> Self {
245        WriteEarlyData { sess }
246    }
247
248    /// How many bytes you may send.  Writes will become short
249    /// once this reaches zero.
250    pub fn bytes_left(&self) -> usize {
251        self.sess
252            .inner
253            .core
254            .side
255            .early_data
256            .bytes_left()
257    }
258
259    /// Returns the "early" exporter that can derive key material for use in early data
260    ///
261    /// See [RFC5705][] for general details on what exporters are, and [RFC8446 S7.5][] for
262    /// specific details on the "early" exporter.
263    ///
264    /// **Beware** that the early exporter requires care, as it is subject to the same
265    /// potential for replay as early data itself.  See [RFC8446 appendix E.5.1][] for
266    /// more detail.
267    ///
268    /// This function can be called at most once per connection. This function will error:
269    /// if called more than once per connection.
270    ///
271    /// If you are looking for the normal exporter, this is available from
272    /// [`Connection::exporter()`].
273    ///
274    /// [RFC5705]: https://datatracker.ietf.org/doc/html/rfc5705
275    /// [RFC8446 S7.5]: https://datatracker.ietf.org/doc/html/rfc8446#section-7.5
276    /// [RFC8446 appendix E.5.1]: https://datatracker.ietf.org/doc/html/rfc8446#appendix-E.5.1
277    /// [`Connection::exporter()`]: crate::conn::Connection::exporter()
278    pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
279        self.sess.inner.core.early_exporter()
280    }
281}
282
283impl io::Write for WriteEarlyData<'_> {
284    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
285        self.sess.write_early_data(buf)
286    }
287
288    fn flush(&mut self) -> io::Result<()> {
289        Ok(())
290    }
291}
292
293impl ConnectionCore<ClientSide> {
294    pub(crate) fn for_client(
295        config: Arc<ClientConfig>,
296        name: ServerName<'static>,
297        extra_exts: ClientExtensionsInput,
298        quic: Option<&mut dyn QuicOutput>,
299        protocol: Protocol,
300    ) -> Result<Self, Error> {
301        let mut common_state = CommonState::new(Side::Client, config.fips());
302        common_state
303            .send
304            .set_max_fragment_size(config.max_fragment_size)?;
305        let mut data = ClientConnectionData::new();
306
307        let mut output = SideCommonOutput {
308            side: &mut data,
309            quic,
310            common: &mut common_state,
311        };
312
313        let input = ClientHelloInput::new(name, &extra_exts, protocol, &mut output, config)?;
314        let state = input.start_handshake(extra_exts, &mut output)?;
315
316        Ok(Self::new(state, data, common_state))
317    }
318
319    pub(crate) fn is_early_data_accepted(&self) -> bool {
320        self.side.early_data.is_accepted()
321    }
322}
323
324pub(super) struct EarlyData {
325    state: EarlyDataState,
326    left: usize,
327}
328
329impl EarlyData {
330    fn new() -> Self {
331        Self {
332            state: EarlyDataState::Disabled,
333            left: 0,
334        }
335    }
336
337    fn is_enabled(&self) -> bool {
338        matches!(
339            self.state,
340            EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted
341        )
342    }
343
344    fn is_accepted(&self) -> bool {
345        matches!(
346            self.state,
347            EarlyDataState::Accepted | EarlyDataState::AcceptedFinished
348        )
349    }
350
351    fn enable(&mut self, max_data: usize) {
352        assert_eq!(self.state, EarlyDataState::Disabled);
353        self.state = EarlyDataState::Ready;
354        self.left = max_data;
355    }
356
357    fn start(&mut self) {
358        assert_eq!(self.state, EarlyDataState::Ready);
359        self.state = EarlyDataState::Sending;
360    }
361
362    fn rejected(&mut self) {
363        trace!("EarlyData rejected");
364        self.state = EarlyDataState::Rejected;
365    }
366
367    fn accepted(&mut self) {
368        trace!("EarlyData accepted");
369        assert_eq!(self.state, EarlyDataState::Sending);
370        self.state = EarlyDataState::Accepted;
371    }
372
373    pub(super) fn finished(&mut self) {
374        trace!("EarlyData finished");
375        self.state = match self.state {
376            EarlyDataState::Accepted => EarlyDataState::AcceptedFinished,
377            _ => panic!("bad EarlyData state"),
378        }
379    }
380
381    fn check_write(&mut self, sz: usize) -> io::Result<usize> {
382        self.check_write_opt(sz)
383            .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidInput))
384    }
385
386    fn check_write_opt(&mut self, sz: usize) -> Option<usize> {
387        match self.state {
388            EarlyDataState::Disabled => unreachable!(),
389            EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted => {
390                let take = if self.left < sz {
391                    mem::replace(&mut self.left, 0)
392                } else {
393                    self.left -= sz;
394                    sz
395                };
396
397                Some(take)
398            }
399            EarlyDataState::Rejected | EarlyDataState::AcceptedFinished => None,
400        }
401    }
402
403    fn bytes_left(&self) -> usize {
404        self.left
405    }
406}
407
408#[derive(Debug, PartialEq)]
409enum EarlyDataState {
410    Disabled,
411    Ready,
412    Sending,
413    Accepted,
414    AcceptedFinished,
415    Rejected,
416}
417
418pub(crate) struct ClientConnectionData {
419    early_data: EarlyData,
420    ech_status: EchStatus,
421}
422
423impl ClientConnectionData {
424    fn new() -> Self {
425        Self {
426            early_data: EarlyData::new(),
427            ech_status: EchStatus::default(),
428        }
429    }
430}
431
432/// State associated with a client connection.
433#[expect(clippy::exhaustive_structs)]
434#[derive(Debug)]
435pub struct ClientSide;
436
437impl SideData for ClientSide {}
438
439impl crate::conn::private::Side for ClientSide {
440    type Data = ClientConnectionData;
441    type State = super::hs::ClientState;
442}
443
444impl SideOutput for ClientConnectionData {
445    fn emit(&mut self, ev: Event<'_>) {
446        match ev {
447            Event::EchStatus(ech) => self.ech_status = ech,
448            Event::EarlyData(EarlyDataEvent::Accepted) => self.early_data.accepted(),
449            Event::EarlyData(EarlyDataEvent::Enable(sz)) => self.early_data.enable(sz),
450            Event::EarlyData(EarlyDataEvent::Finished) => self.early_data.finished(),
451            Event::EarlyData(EarlyDataEvent::Start) => self.early_data.start(),
452            Event::EarlyData(EarlyDataEvent::Rejected) => self.early_data.rejected(),
453            _ => unreachable!(),
454        }
455    }
456}