From 680ca4e9b9be5fc9ed7099acf0656afcb5052118 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sat, 27 May 2023 00:10:41 -0400 Subject: [PATCH] Flesh out most of `sender` functionality --- src/certificate_store.rs | 2 +- src/host/mod.rs | 14 ++++++++- src/sender/error.rs | 3 ++ src/sender/mod.rs | 66 +++++++++++++++++++++++++++++----------- src/sender/verifier.rs | 6 ++-- 5 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/certificate_store.rs b/src/certificate_store.rs index 8123279..44d2c60 100644 --- a/src/certificate_store.rs +++ b/src/certificate_store.rs @@ -3,7 +3,7 @@ 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 + /// 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 diff --git a/src/host/mod.rs b/src/host/mod.rs index c7c98a4..c2ec8ef 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -1,4 +1,8 @@ -use std::{fmt, str::FromStr}; +use std::{ + fmt, + net::{SocketAddr, ToSocketAddrs}, + str::FromStr, +}; mod error; pub use error::Error; @@ -72,6 +76,14 @@ impl FromStr for Host { } } +impl ToSocketAddrs for Host { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> std::io::Result { + format!("{}:1958", self).to_socket_addrs() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/sender/error.rs b/src/sender/error.rs index 13a9ce4..29fbcfd 100644 --- a/src/sender/error.rs +++ b/src/sender/error.rs @@ -6,6 +6,7 @@ use { #[derive(Debug)] /// Errors which might occur when sending a message pub enum Error { + DnsError, TlsError(rustls::Error), RequestError(ParseRequestError), ResponseError(ParseResponseError), @@ -15,6 +16,7 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Self::DnsError => write!(f, "Dns Error"), Self::TlsError(e) => write!(f, "{e}"), Self::RequestError(e) => write!(f, "{e}"), Self::ResponseError(e) => write!(f, "{e}"), @@ -26,6 +28,7 @@ impl fmt::Display for Error { impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { + Self::DnsError => None, Self::RequestError(e) => Some(e), Self::ResponseError(e) => Some(e), Self::TlsError(e) => Some(e), diff --git a/src/sender/mod.rs b/src/sender/mod.rs index 69d9fbb..2a1a3ba 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -1,6 +1,15 @@ +use std::{ + io, + net::{TcpStream, ToSocketAddrs}, + sync::Arc, + time::Duration, +}; + +use rustls::{ClientConfig, ClientConnection, StreamOwned}; + pub use self::{error::Error, verifier::Verifier}; use { - crate::prelude::{CertificateStore, Request, Response}, + crate::prelude::{CertificateStore, Request}, std::io::{Read, Write}, }; @@ -9,33 +18,56 @@ mod verifier; #[derive(Debug)] /// Sends a piece of mail from the sending server to the receiving server -pub struct Sender +pub struct Sender where S: CertificateStore, - C: Sized, - T: Read + Write + Sized, { /// The full message text to be sent pub request: Request, - /// Verifies the receiving server's certificate - pub verifier: Verifier, - /// The TLS stream used for the connection - pub stream: rustls::StreamOwned, + /// A [CertificateStore] for servers known to us + pub store: S, + /// A [CertificateStore] for mailboxes which exist on this system + pub client_store: S, } -impl Sender +impl Sender where - S: CertificateStore, - C: Sized, - T: Read + Write + Sized, + S: CertificateStore + 'static { - pub fn new(request_str: &str, store: S) -> Result { + pub fn new(request_str: &str, store: S, client_store: S) -> Result { let request: Request = request_str.parse()?; - let verifier = Verifier::new(store); - unimplemented!(); + Ok(Self { request, store, client_store }) } - pub fn send(&mut self) -> Result { - unimplemented!(); + fn host_string(&self) -> String { + self.request.sender.host.to_string() + } + + pub fn send(self) -> Result, Error> { + let dnsname = self + .host_string() + .as_str() + .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 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()); + }; + let tcp_stream = TcpStream::connect_timeout(&socket_addrs, Duration::new(10, 0))?; + let cfg = ClientConfig::builder() + .with_safe_defaults() + .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) + }; + 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)?; + Ok(buf) } } diff --git a/src/sender/verifier.rs b/src/sender/verifier.rs index 29ebcb4..9287b5c 100644 --- a/src/sender/verifier.rs +++ b/src/sender/verifier.rs @@ -6,7 +6,7 @@ use { }, std::{ borrow::BorrowMut, - sync::{Arc, Mutex}, + sync::Mutex, time, }, }; @@ -30,7 +30,7 @@ use { /// ``` pub struct Verifier { /// An item which serves as storage for certificates - pub store: Arc>, + pub store: Mutex, } impl ServerCertVerifier for Verifier { @@ -82,7 +82,7 @@ impl ServerCertVerifier for Verifier { impl From for Verifier { fn from(value: T) -> Self { Self { - store: Arc::new(Mutex::new(value)), + store: Mutex::new(value), } } }