From 46d04405ad0c6557d470048cf8fd413bb96277b1 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sat, 27 May 2023 10:58:14 -0400 Subject: [PATCH] Send client certs if they exist in ClientCertificateStore; Impl TryFrom> for Response; Adjust error types to fit all new cases; --- src/response/error.rs | 11 ++++++++++- src/response/mod.rs | 8 ++++++++ src/sender/error.rs | 11 ++++++++++- src/sender/mod.rs | 46 +++++++++++++++++++++++-------------------- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/response/error.rs b/src/response/error.rs index 5783cfa..0b47d13 100644 --- a/src/response/error.rs +++ b/src/response/error.rs @@ -1,6 +1,6 @@ use { crate::prelude::ParseStatusError, - std::{fmt, num::ParseIntError}, + std::{fmt, num::ParseIntError, string::FromUtf8Error}, }; #[derive(Debug, PartialEq)] @@ -16,6 +16,8 @@ pub enum Error { StatusError, /// The response was malformed Malformed, + /// The response is not valid utf8 + Utf8Error, } impl fmt::Display for Error { @@ -25,6 +27,7 @@ impl fmt::Display for Error { Self::ParseInt(e) => write!(f, "ParseResponseError: {e}"), Self::StatusError => write!(f, "ParseResponseError: Invalid Status"), Self::Malformed => write!(f, "ParseResponseError: Malformed"), + Self::Utf8Error => write!(f, "ParseResponseError: Not Utf8"), } } } @@ -49,3 +52,9 @@ impl From for Error { Self::StatusError } } + +impl From for Error { + fn from(_value: FromUtf8Error) -> Self { + Self::Utf8Error + } +} diff --git a/src/response/mod.rs b/src/response/mod.rs index fc5cef7..46f4939 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -43,6 +43,14 @@ impl FromStr for Response { } } +impl TryFrom> for Response { + type Error = Error; + + fn try_from(value: Vec) -> Result { + String::from_utf8(value)?.as_str().parse() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/sender/error.rs b/src/sender/error.rs index 29fbcfd..d618383 100644 --- a/src/sender/error.rs +++ b/src/sender/error.rs @@ -1,11 +1,13 @@ use { crate::prelude::{ParseRequestError, ParseResponseError}, + rustls::InvalidMessage, std::{fmt, io}, }; #[derive(Debug)] /// Errors which might occur when sending a message pub enum Error { + CertificateError(InvalidMessage), DnsError, TlsError(rustls::Error), RequestError(ParseRequestError), @@ -16,6 +18,7 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Self::CertificateError(e) => write!(f, "{e:?}"), Self::DnsError => write!(f, "Dns Error"), Self::TlsError(e) => write!(f, "{e}"), Self::RequestError(e) => write!(f, "{e}"), @@ -28,15 +31,21 @@ 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), Self::IoError(e) => Some(e), + _ => None, } } } +impl From for Error { + fn from(value: InvalidMessage) -> Self { + Self::CertificateError(value) + } +} + impl From for Error { fn from(value: rustls::Error) -> Self { Self::TlsError(value) diff --git a/src/sender/mod.rs b/src/sender/mod.rs index 931137d..91c18b1 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -1,40 +1,40 @@ -use std::{ - io, - net::{TcpStream, ToSocketAddrs}, - sync::Arc, - time::Duration, +use { + crate::prelude::{CertificateStore, ClientCertificateStore, Request, Response}, + rustls::{internal::msgs::codec::Codec, ClientConfig, ClientConnection, StreamOwned}, + std::{ + io::{self, Read, Write}, + net::{TcpStream, ToSocketAddrs}, + sync::Arc, + time::Duration, + }, }; -use rustls::{ClientConfig, ClientConnection, StreamOwned}; - pub use self::{error::Error, verifier::Verifier}; -use { - crate::prelude::{CertificateStore, Request}, - std::io::{Read, Write}, -}; mod error; 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: ClientCertificateStore, { /// The full message text to be sent pub request: Request, /// A [CertificateStore] for servers known to us pub store: S, /// A [CertificateStore] for mailboxes which exist on this system - pub client_store: S, + pub client_store: C, } -impl Sender +impl Sender where S: CertificateStore + 'static, + C: ClientCertificateStore, { - pub fn new(request_str: &str, store: S, client_store: S) -> Result { + pub fn new(request_str: &str, store: S, client_store: C) -> Result { let request: Request = request_str.parse()?; Ok(Self { request, @@ -47,16 +47,14 @@ where self.request.sender.host.to_string() } - pub fn send(self) -> Result, Error> { + pub fn send(self) -> Result { 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 client_cert = self.client_store.get_certificate(&self.request.sender); 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()); @@ -67,7 +65,12 @@ 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(c) => { + let rustls_cert = rustls::Certificate::read_bytes(&c.der)?; + let cert_chain = vec![rustls_cert]; + let key_der = rustls::PrivateKey(c.key); + cfg.with_single_cert(cert_chain, key_der)? + } }; let client = ClientConnection::new(Arc::new(cfg), dnsname)?; let mut stream = StreamOwned::new(client, tcp_stream); @@ -76,6 +79,7 @@ where stream.read_to_end(&mut buf)?; stream.conn.send_close_notify(); drop(stream); - Ok(buf) + let res = buf.try_into()?; + Ok(res) } }