diff --git a/src/certificate.rs b/src/certificate.rs new file mode 100644 index 0000000..bfa3a1d --- /dev/null +++ b/src/certificate.rs @@ -0,0 +1,103 @@ +use crate::mailuser::Mailuser; +use std::collections::{BTreeMap, HashMap}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +pub struct Certificate { + pub der: Vec, + pub key: Vec, +} + +/// An item which stores known certificates. For convenience, this trait is implemented for +/// `std::collections::HashMap` and `std::collections::BTreeMap` +pub trait CertificateStore: Send + Sync { + /// Retrieves a certificate fingerprint using the hostname as it's key if + /// it has previously been saved + fn get_certificate(&self, host: &str) -> Option; + /// Inserts a certificate fingerprint with the hostname as the key + fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option; + /// Returns true if a fingerprint has been stored for this host + fn contains_certificate(&self, host: &str) -> bool; +} + +/// An item which stores client certificates. For convenience, this trait is implemented for +/// `std::collections::HashMap` and `std::collections::BTreeMap` +pub trait ClientCertificateStore: Send + Sync { + /// Retrieves a client certificate using the sender's address as the key + fn get_certificate(&self, user: &Mailuser) -> Option; + /// Inserts a client certificate with the sender's address as the key + fn insert_certificate( + &mut self, + user: &Mailuser, + certificate: Certificate, + ) -> Option; + /// Returns true if the sender has a certificate in this store + fn contains_certificate(&self, user: &Mailuser) -> bool; +} + +impl CertificateStore for HashMap { + fn get_certificate(&self, host: &str) -> Option { + self.get(host).cloned() + } + + fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option { + self.insert(host.to_string(), fingerprint.to_string()) + } + + fn contains_certificate(&self, host: &str) -> bool { + self.contains_key(host) + } +} + +impl CertificateStore for BTreeMap { + fn get_certificate(&self, host: &str) -> Option { + self.get(host).cloned() + } + + fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option { + self.insert(host.to_string(), fingerprint.to_string()) + } + + fn contains_certificate(&self, host: &str) -> bool { + self.contains_key(host) + } +} + +impl ClientCertificateStore for HashMap { + fn get_certificate(&self, user: &Mailuser) -> Option { + self.get(&user.to_string()).cloned() + } + + fn insert_certificate( + &mut self, + user: &Mailuser, + certificate: Certificate, + ) -> Option { + self.insert(user.to_string(), certificate) + } + + fn contains_certificate(&self, user: &Mailuser) -> bool { + self.contains_key(&user.to_string()) + } +} + +impl ClientCertificateStore for BTreeMap { + fn get_certificate(&self, user: &Mailuser) -> Option { + self.get(&user.to_string()).cloned() + } + + fn insert_certificate( + &mut self, + user: &Mailuser, + certificate: Certificate, + ) -> Option { + self.insert(user.to_string(), certificate) + } + + fn contains_certificate(&self, user: &Mailuser) -> bool { + self.contains_key(&user.to_string()) + } +} diff --git a/src/certificate_store.rs b/src/certificate_store.rs deleted file mode 100644 index 44d2c60..0000000 --- a/src/certificate_store.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; - -/// An item which stores known certificates. For convenience, this trait is implemented for -/// `std::collections::HashMap` and `std::collections::BTreeMap` -pub trait CertificateStore: Send + Sync { - /// Retrieves a certificate fingerprint using the hostname as it's key if - /// it has previously been saved - fn get_certificate(&self, host: &str) -> Option; - /// Inserts a certificate fingerprint with the hostname as the key - fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option; - /// Returns true if a fingerprint has been stored for this host - fn contains_certificate(&self, host: &str) -> bool; -} - -impl CertificateStore for HashMap { - fn get_certificate(&self, host: &str) -> Option { - self.get(host).cloned() - } - - fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option { - self.insert(host.to_string(), fingerprint.to_string()) - } - - fn contains_certificate(&self, host: &str) -> bool { - self.contains_key(host) - } -} - -impl CertificateStore for BTreeMap { - fn get_certificate(&self, host: &str) -> Option { - self.get(host).cloned() - } - - fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option { - self.insert(host.to_string(), fingerprint.to_string()) - } - - fn contains_certificate(&self, host: &str) -> bool { - self.contains_key(host) - } -} diff --git a/src/lib.rs b/src/lib.rs index 13cd7e8..80da445 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::all, clippy::pedantic)] -mod certificate_store; +mod certificate; mod fingerprint; mod host; mod mailbox; diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index 201da2c..53cf154 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -1,4 +1,4 @@ -use crate::prelude::Host; +use crate::{mailuser::Mailuser, prelude::Host}; use std::{fmt, str::FromStr}; #[cfg(feature = "serde")] @@ -64,3 +64,13 @@ impl FromStr for Mailbox { } } } + +impl From for Mailbox { + fn from(value: Mailuser) -> Self { + Self { + username: value.username, + host: value.host, + blurb: None, + } + } +} diff --git a/src/mailuser/mod.rs b/src/mailuser/mod.rs index 0da7a4e..02d2bdd 100644 --- a/src/mailuser/mod.rs +++ b/src/mailuser/mod.rs @@ -1,4 +1,4 @@ -use crate::prelude::{Host, ParseMailboxError as Error}; +use crate::prelude::{Host, Mailbox, ParseMailboxError as Error}; use std::{fmt, str::FromStr}; #[cfg(feature = "serde")] @@ -36,3 +36,12 @@ impl FromStr for Mailuser { } } } + +impl From for Mailuser { + fn from(value: Mailbox) -> Self { + Self { + username: value.username, + host: value.host, + } + } +} diff --git a/src/prelude.rs b/src/prelude.rs index f8eed48..e0ec573 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,5 @@ pub use super::{ - certificate_store::CertificateStore, + certificate::{Certificate, CertificateStore, ClientCertificateStore}, fingerprint::{Error as FingerprintError, Fingerprint, GetFingerprint}, host::{Error as ParseHostError, Host}, mailbox::{Error as ParseMailboxError, Mailbox}, diff --git a/src/sender/mod.rs b/src/sender/mod.rs index 2a1a3ba..931137d 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -32,11 +32,15 @@ where impl Sender where - S: CertificateStore + 'static + S: CertificateStore + 'static, { pub fn new(request_str: &str, store: S, client_store: S) -> Result { let request: Request = request_str.parse()?; - Ok(Self { request, store, client_store }) + Ok(Self { + request, + store, + client_store, + }) } fn host_string(&self) -> String { @@ -50,7 +54,9 @@ where .try_into() .map_err(|_| Error::DnsError)?; let mut it = self.request.sender.host.to_socket_addrs()?; - let client_cert = self.client_store.get_certificate(&self.request.sender.to_string()); + let client_cert = self + .client_store + .get_certificate(&self.request.sender.to_string()); let verifier = Arc::new(Verifier::new(self.store)); let Some(socket_addrs) = it.next() else { return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into()); @@ -61,13 +67,15 @@ where .with_custom_certificate_verifier(verifier); let cfg = match client_cert { None => cfg.with_no_client_auth(), - Some(_) => cfg.with_no_client_auth(),// todo: cfg.with_single_cert(cert_chain, key_der) + Some(_) => cfg.with_no_client_auth(), // todo: cfg.with_single_cert(cert_chain, key_der) }; let client = ClientConnection::new(Arc::new(cfg), dnsname)?; let mut stream = StreamOwned::new(client, tcp_stream); stream.write_all(self.request.to_string().as_bytes())?; let mut buf = vec![]; stream.read_to_end(&mut buf)?; + stream.conn.send_close_notify(); + drop(stream); Ok(buf) } } diff --git a/src/sender/verifier.rs b/src/sender/verifier.rs index 9287b5c..663ed1f 100644 --- a/src/sender/verifier.rs +++ b/src/sender/verifier.rs @@ -4,11 +4,7 @@ use { client::{ServerCertVerified, ServerCertVerifier}, Certificate, }, - std::{ - borrow::BorrowMut, - sync::Mutex, - time, - }, + std::{borrow::BorrowMut, sync::Mutex, time}, }; #[derive(Debug)]