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
29pub 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 pub fn split(self) -> Result<SplitConnection<ClientSide>, Error> {
58 self.inner.split()
59 }
60
61 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 pub fn is_early_data_accepted(&self) -> bool {
99 self.inner.core.is_early_data_accepted()
100 }
101
102 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 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
197pub 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 pub fn with_alpn(mut self, alpn_protocols: Vec<ApplicationProtocol<'static>>) -> Self {
209 self.alpn_protocols = Some(alpn_protocols);
210 self
211 }
212
213 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
234pub 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 pub fn bytes_left(&self) -> usize {
251 self.sess
252 .inner
253 .core
254 .side
255 .early_data
256 .bytes_left()
257 }
258
259 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#[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}