dope-tls 0.1.1

TCP TLS streams for dope runtime, shin-backed
Documentation

use std::cell::RefCell;
use std::rc::Rc;

use dope_session::{CodecLayer, WriteBufArr};

use crate::state::TlsState;
use crate::wire_bufs::WireBufs;

thread_local! {
    static CURRENT_SERVER_CONFIG: RefCell<Option<Rc<shin::server::Config>>> =
        const { RefCell::new(None) };
}

pub struct TlsConnState {
    pub tls: Option<TlsState>,
    pub bufs: WireBufs,
}

impl Default for TlsConnState {
    fn default() -> Self {
        let tls = TlsCodec::current_server_config().map(|cfg| TlsState::new_server((*cfg).clone()));
        let mut bufs = WireBufs::new();
        bufs.close = tls.is_none();
        Self { tls, bufs }
    }
}

pub struct TlsCodec;

impl TlsCodec {
    pub fn install_server_config(cfg: shin::server::Config) {
        CURRENT_SERVER_CONFIG.with(|slot| {
            *slot.borrow_mut() = Some(Rc::new(cfg));
        });
    }

    fn current_server_config() -> Option<Rc<shin::server::Config>> {
        CURRENT_SERVER_CONFIG.with(|slot| slot.borrow().clone())
    }
}

impl CodecLayer for TlsCodec {
    type ConnState = TlsConnState;
    type Staging = WriteBufArr;
    const IS_PASSTHROUGH: bool = false;

    #[inline(always)]
    fn is_active(state: &Self::ConnState) -> bool {
        state.tls.as_ref().is_some_and(|s| s.is_established())
    }

    fn process_inbound(state: &mut Self::ConnState, wire_in: &[u8]) {
        let Some(tls) = state.tls.as_mut() else {
            state.bufs.close = true;
            return;
        };
        if !tls.try_read_tcp(wire_in) {
            state.bufs.close = true;
            return;
        }
        while let Some(chunk) = tls.pull_app() {
            state.bufs.plaintext.extend_from_slice(&chunk);
        }
        tls.pull_send_into(&mut state.bufs.wire);
        if tls.is_closed() {
            state.bufs.close = true;
        }
    }

    fn process_outbound(state: &mut Self::ConnState, plaintext_in: &[u8]) {
        let Some(tls) = state.tls.as_mut() else {
            state.bufs.close = true;
            return;
        };
        if !tls.is_established() {
            return;
        }
        if tls.write_app(plaintext_in).is_err() {
            state.bufs.close = true;
            return;
        }
        tls.pull_send_into(&mut state.bufs.wire);
    }

    #[inline(always)]
    fn wire_slice(state: &Self::ConnState) -> &[u8] {
        state.bufs.wire_slice()
    }

    #[inline(always)]
    fn take_plaintext(state: &mut Self::ConnState) -> Vec<u8> {
        state.bufs.take_plaintext()
    }

    #[inline(always)]
    fn install_plaintext(state: &mut Self::ConnState, plaintext: Vec<u8>) {
        state.bufs.install_plaintext(plaintext);
    }

    fn take_inflight(state: &mut Self::ConnState) -> Option<(*const u8, u32)> {
        state.bufs.take_inflight()
    }

    fn consume_inflight(state: &mut Self::ConnState, n: usize) -> bool {
        state.bufs.consume_inflight(n)
    }

    #[inline(always)]
    fn has_inflight(state: &Self::ConnState) -> bool {
        state.bufs.has_inflight()
    }

    #[inline(always)]
    fn close_pending(state: &Self::ConnState) -> bool {
        state.bufs.close_pending()
    }
}