use std::collections::VecDeque;
use std::io;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::time::Instant;
use crate::crypto::Fingerprint;
use crate::crypto::Sha256Provider;
use crate::crypto::dtls::{DtlsCert, DtlsOutput};
use crate::crypto::dtls::{DtlsInstance, DtlsProvider, DtlsVersion};
use crate::crypto::{CryptoError, DtlsError};
use crate::io::DatagramSend;
use crate::util::already_happened;
pub struct Dtls {
instance: Box<dyn DtlsInstance>,
fingerprint: Fingerprint,
remote_fingerprint: Option<Fingerprint>,
active_state: Option<bool>,
pending_packets: VecDeque<DatagramSend>,
}
pub(crate) fn is_would_block(error: &DtlsError) -> bool {
match error {
DtlsError::Io(e) => e.kind() == io::ErrorKind::WouldBlock,
DtlsError::CryptoError(crypto_err) => match crypto_err {
CryptoError::Io(e) => e.kind() == io::ErrorKind::WouldBlock,
#[allow(unreachable_patterns)]
_ => false,
},
}
}
impl UnwindSafe for Dtls {}
impl RefUnwindSafe for Dtls {}
impl Dtls {
pub fn new(
cert: &DtlsCert,
dtls_provider: &dyn DtlsProvider,
sha256_provider: &dyn Sha256Provider,
now: Instant,
dtls_version: DtlsVersion,
) -> Result<Self, DtlsError> {
let instance = dtls_provider
.new_dtls(cert, now, dtls_version)
.map_err(DtlsError::CryptoError)?;
let fingerprint = Fingerprint {
hash_func: "sha-256".to_string(),
bytes: sha256_provider.sha256(&cert.certificate).to_vec(),
};
Ok(Self {
instance,
fingerprint,
remote_fingerprint: None,
active_state: None,
pending_packets: VecDeque::new(),
})
}
pub fn is_inited(&self) -> bool {
self.active_state.is_some()
}
pub fn set_active(&mut self, active: bool) {
self.active_state = Some(active);
self.instance.set_active(active)
}
pub fn is_active(&self) -> Option<bool> {
self.active_state
}
pub fn local_fingerprint(&self) -> &Fingerprint {
&self.fingerprint
}
pub fn remote_fingerprint(&self) -> Option<&Fingerprint> {
self.remote_fingerprint.as_ref()
}
pub fn set_remote_fingerprint(&mut self, fingerprint: Fingerprint) {
self.remote_fingerprint = Some(fingerprint);
}
pub fn poll_output<'a>(&mut self, buf: &'a mut [u8]) -> DtlsOutput<'a> {
let next = self.instance.poll_output(buf);
if let DtlsOutput::Packet(packet) = next {
self.pending_packets.push_back(packet.to_vec().into());
return DtlsOutput::Timeout(already_happened());
}
next
}
pub fn poll_packet(&mut self) -> Option<DatagramSend> {
self.pending_packets.pop_front()
}
pub fn handle_receive(&mut self, packet: &[u8]) -> Result<(), DtlsError> {
if self.active_state.is_none() {
debug!("Ignoring DTLS datagram prior to DTLS start");
return Ok(());
}
self.instance
.handle_packet(packet)
.map_err(|e| DtlsError::CryptoError(CryptoError::Other(format!("DTLS error: {}", e))))
}
pub fn handle_input(&mut self, data: &[u8]) -> Result<(), DtlsError> {
self.instance.send_application_data(data).map_err(|e| {
if matches!(e, dimpl::Error::HandshakePending) {
DtlsError::Io(io::Error::new(io::ErrorKind::WouldBlock, e))
} else {
DtlsError::CryptoError(CryptoError::Other(format!("DTLS error: {}", e)))
}
})
}
pub fn handle_timeout(&mut self, now: Instant) -> Result<(), DtlsError> {
self.instance
.handle_timeout(now)
.map_err(|e| DtlsError::CryptoError(CryptoError::Other(format!("DTLS error: {}", e))))
}
}