dope-tls 0.17.0

TCP TLS streams for dope runtime, shin-backed
Documentation
use dope_net::wire::buffered::{Buffer, Scratch};
use dope_net::wire::send::Vectored;
use shin::wire::record::MAX_PLAINTEXT_BODY;

use crate::send::Cursor;
use crate::staging::TLS13_RECORD_OVERHEAD;
use crate::state::State;
use crate::state::sessions::Session;
use crate::state::status::PeerClose;

use super::{Role, TlsConnection};

pub(super) struct Egress<'a, 'd, R: Role> {
    wire: &'a mut TlsConnection<'d, R>,
}

impl<'a, 'd, R: Role> Egress<'a, 'd, R> {
    pub(super) fn new(wire: &'a mut TlsConnection<'d, R>) -> Self {
        Self { wire }
    }

    pub(super) fn encrypt(&mut self, output: &mut Buffer<Scratch>, plain: &[u8]) -> usize {
        if self.wire.send_inflight {
            return 0;
        }
        self.drain(output);
        if !self.established() {
            return 0;
        }
        let mut consumed = 0;
        while consumed < plain.len() {
            let end = (consumed + MAX_PLAINTEXT_BODY).min(plain.len());
            if !Self::record_fits(output, end - consumed) {
                break;
            }
            let count = self.seal_record(output, &plain[consumed..end]);
            if count == 0 {
                break;
            }
            consumed += count;
        }
        consumed
    }

    pub(super) fn encrypt_vectored(
        &mut self,
        output: &mut Buffer<Scratch>,
        vectored: &Vectored<'_>,
    ) -> usize {
        if self.wire.send_inflight {
            return 0;
        }
        self.drain(output);
        if !self.established() {
            return 0;
        }
        let total = vectored.bytes();
        let mut cursor = Cursor::new(vectored.iter());
        let mut consumed = 0;
        while consumed < total {
            let record_len = (total - consumed).min(MAX_PLAINTEXT_BODY);
            if !Self::record_fits(output, record_len) {
                break;
            }
            let tls = &mut self.wire.state.tls;
            match tls.write_app_parts_into(output, record_len, cursor.take(record_len)) {
                Ok(0) => return consumed,
                Ok(count) => consumed += count,
                Err(_) => {
                    self.wire.state.close = true;
                    return consumed;
                }
            }
            self.drain(output);
        }
        debug_assert_eq!(cursor.consumed(), consumed);
        consumed
    }

    pub(super) fn drain(&mut self, output: &mut Buffer<Scratch>) {
        if self.wire.send_inflight {
            return;
        }
        if !Self::drain_tls(&mut self.wire.state.tls, output) {
            self.wire.state.close = true;
        }
    }

    pub(super) fn propagate_close(&self) -> bool {
        self.wire.state.close || self.wire.peer_close() == PeerClose::CloseNotify
    }

    pub(super) fn seal_close_notify(&mut self, output: &mut Buffer<Scratch>) {
        if self.wire.state.close || self.wire.state.close_notify_sent {
            return;
        }
        let tls = &mut self.wire.state.tls;
        let sealed = tls.can_close_notify() && tls.send_close_notify().is_ok();
        if sealed {
            self.wire.state.close_notify_sent = true;
            self.drain(output);
        }
    }

    fn established(&self) -> bool {
        self.wire.state.tls.is_established()
    }

    fn record_fits(output: &Buffer<Scratch>, plaintext_len: usize) -> bool {
        output.spare_capacity() >= plaintext_len + TLS13_RECORD_OVERHEAD
    }

    fn seal_record(&mut self, output: &mut Buffer<Scratch>, chunk: &[u8]) -> usize {
        let tls = &mut self.wire.state.tls;
        let consumed = match tls.write_app_into(output, chunk) {
            Ok(count) => count,
            Err(_) => {
                self.wire.state.close = true;
                return 0;
            }
        };
        self.drain(output);
        consumed
    }

    fn drain_tls<S: Session>(tls: &mut State<S>, output: &mut Buffer<Scratch>) -> bool {
        let spare = output.spare_capacity();
        if spare == 0 {
            return true;
        }
        let pending = tls.pending_send_slice();
        let count = pending.len().min(spare);
        if count > 0
            && output.try_extend_from_slice(&pending[..count]).is_ok()
            && tls.consume_pending_send(count).is_err()
        {
            return false;
        }
        true
    }
}