Flesh out most of sender functionality

This commit is contained in:
Nathan Fisher 2023-05-27 00:10:41 -04:00
parent 887ab8e085
commit 680ca4e9b9
5 changed files with 69 additions and 22 deletions

View file

@ -1,4 +1,8 @@
use std::{fmt, str::FromStr}; use std::{
fmt,
net::{SocketAddr, ToSocketAddrs},
str::FromStr,
};
mod error; mod error;
pub use error::Error; pub use error::Error;
@ -72,6 +76,14 @@ impl FromStr for Host {
} }
} }
impl ToSocketAddrs for Host {
type Iter = std::vec::IntoIter<SocketAddr>;
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
format!("{}:1958", self).to_socket_addrs()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -6,6 +6,7 @@ use {
#[derive(Debug)] #[derive(Debug)]
/// Errors which might occur when sending a message /// Errors which might occur when sending a message
pub enum Error { pub enum Error {
DnsError,
TlsError(rustls::Error), TlsError(rustls::Error),
RequestError(ParseRequestError), RequestError(ParseRequestError),
ResponseError(ParseResponseError), ResponseError(ParseResponseError),
@ -15,6 +16,7 @@ pub enum Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::DnsError => write!(f, "Dns Error"),
Self::TlsError(e) => write!(f, "{e}"), Self::TlsError(e) => write!(f, "{e}"),
Self::RequestError(e) => write!(f, "{e}"), Self::RequestError(e) => write!(f, "{e}"),
Self::ResponseError(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 { impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
Self::DnsError => None,
Self::RequestError(e) => Some(e), Self::RequestError(e) => Some(e),
Self::ResponseError(e) => Some(e), Self::ResponseError(e) => Some(e),
Self::TlsError(e) => Some(e), Self::TlsError(e) => Some(e),

View file

@ -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}; pub use self::{error::Error, verifier::Verifier};
use { use {
crate::prelude::{CertificateStore, Request, Response}, crate::prelude::{CertificateStore, Request},
std::io::{Read, Write}, std::io::{Read, Write},
}; };
@ -9,33 +18,56 @@ mod verifier;
#[derive(Debug)] #[derive(Debug)]
/// Sends a piece of mail from the sending server to the receiving server /// Sends a piece of mail from the sending server to the receiving server
pub struct Sender<S, C, T> pub struct Sender<S>
where where
S: CertificateStore, S: CertificateStore,
C: Sized,
T: Read + Write + Sized,
{ {
/// The full message text to be sent /// The full message text to be sent
pub request: Request, pub request: Request,
/// Verifies the receiving server's certificate /// A [CertificateStore] for servers known to us
pub verifier: Verifier<S>, pub store: S,
/// The TLS stream used for the connection /// A [CertificateStore] for mailboxes which exist on this system
pub stream: rustls::StreamOwned<C, T>, pub client_store: S,
} }
impl<S, C, T> Sender<S, C, T> impl<S> Sender<S>
where where
S: CertificateStore, S: CertificateStore + 'static
C: Sized,
T: Read + Write + Sized,
{ {
pub fn new(request_str: &str, store: S) -> Result<Self, Error> { pub fn new(request_str: &str, store: S, client_store: S) -> Result<Self, Error> {
let request: Request = request_str.parse()?; let request: Request = request_str.parse()?;
let verifier = Verifier::new(store); Ok(Self { request, store, client_store })
unimplemented!();
} }
pub fn send(&mut self) -> Result<Response, Error> { fn host_string(&self) -> String {
unimplemented!(); self.request.sender.host.to_string()
}
pub fn send(self) -> Result<Vec<u8>, 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)
} }
} }

View file

@ -6,7 +6,7 @@ use {
}, },
std::{ std::{
borrow::BorrowMut, borrow::BorrowMut,
sync::{Arc, Mutex}, sync::Mutex,
time, time,
}, },
}; };
@ -30,7 +30,7 @@ use {
/// ``` /// ```
pub struct Verifier<S: CertificateStore> { pub struct Verifier<S: CertificateStore> {
/// An item which serves as storage for certificates /// An item which serves as storage for certificates
pub store: Arc<Mutex<S>>, pub store: Mutex<S>,
} }
impl<S: CertificateStore> ServerCertVerifier for Verifier<S> { impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
@ -82,7 +82,7 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
impl<T: CertificateStore> From<T> for Verifier<T> { impl<T: CertificateStore> From<T> for Verifier<T> {
fn from(value: T) -> Self { fn from(value: T) -> Self {
Self { Self {
store: Arc::new(Mutex::new(value)), store: Mutex::new(value),
} }
} }
} }