132 lines
3.8 KiB
Rust
132 lines
3.8 KiB
Rust
//! Sends a Misfin [`Request`]
|
|
use {
|
|
crate::prelude::{CertificateStore, ClientCertificateStore, Mailuser, Request, Response},
|
|
rustls::{ClientConfig, ClientConnection, StreamOwned},
|
|
std::{
|
|
io::{self, Read, Write},
|
|
net::{TcpStream, ToSocketAddrs},
|
|
sync::Arc,
|
|
time::Duration,
|
|
},
|
|
};
|
|
|
|
pub use self::{error::Error, verifier::Verifier};
|
|
|
|
mod error;
|
|
mod verifier;
|
|
|
|
#[derive(Debug)]
|
|
/// Sends a piece of mail from the sending server to the receiving server
|
|
pub struct Sender<S, C>
|
|
where
|
|
S: CertificateStore,
|
|
C: ClientCertificateStore,
|
|
{
|
|
/// The address of the sender
|
|
pub sender: Mailuser,
|
|
/// The full message text to be sent
|
|
pub request: Request,
|
|
/// A [CertificateStore] for servers known to us
|
|
pub store: S,
|
|
/// A [ClientCertificateStore] for mailboxes which exist on this system
|
|
pub client_store: C,
|
|
}
|
|
|
|
impl<S, C> Sender<S, C>
|
|
where
|
|
S: CertificateStore + 'static,
|
|
C: ClientCertificateStore,
|
|
{
|
|
pub fn new(sender: &str, request_str: &str, store: S, client_store: C) -> Result<Self, Error> {
|
|
let sender = sender.parse()?;
|
|
let request: Request = request_str.parse()?;
|
|
Ok(Self {
|
|
sender,
|
|
request,
|
|
store,
|
|
client_store,
|
|
})
|
|
}
|
|
|
|
fn host_string(&self) -> String {
|
|
self.request.recipient.host.to_string()
|
|
}
|
|
|
|
pub fn send(self) -> Result<Response, Error> {
|
|
let dnsname = self
|
|
.host_string()
|
|
.as_str()
|
|
.try_into()
|
|
.map_err(|_| Error::DnsError)?;
|
|
let mut it = self.request.recipient.host.to_socket_addrs()?;
|
|
let client_cert = self.client_store.get_certificate(&self.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());
|
|
};
|
|
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(c) => {
|
|
//let rustls_cert = rustls::Certificate::read_bytes(&c.der)?;
|
|
let rustls_cert = rustls::Certificate(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);
|
|
stream.write_all(self.request.to_string().as_bytes())?;
|
|
let mut buf = Vec::with_capacity(1024);
|
|
let _res = stream.read_to_end(&mut buf);
|
|
stream.conn.send_close_notify();
|
|
drop(stream);
|
|
let res = buf.try_into()?;
|
|
Ok(res)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MultiSender<S, C>
|
|
where
|
|
S: CertificateStore,
|
|
C: ClientCertificateStore,
|
|
{
|
|
pub sender: Mailuser,
|
|
pub recipients: Vec<Mailuser>,
|
|
pub store: S,
|
|
pub client_store: C,
|
|
}
|
|
|
|
impl<S, C> MultiSender<S, C>
|
|
where
|
|
S: CertificateStore + 'static + Clone,
|
|
C: ClientCertificateStore + Clone,
|
|
{
|
|
pub fn new(
|
|
sender: &str,
|
|
recipients: &[&str],
|
|
store: S,
|
|
client_store: C,
|
|
) -> Result<Self, Error> {
|
|
let mut rec = vec![];
|
|
for r in recipients {
|
|
let r = r.parse()?;
|
|
rec.push(r);
|
|
}
|
|
Ok(Self {
|
|
sender: sender.parse()?,
|
|
recipients: rec,
|
|
store,
|
|
client_store,
|
|
})
|
|
}
|
|
|
|
pub fn send(&self, f: &dyn Fn(Result<Response, Error>)) -> Result<(), Error> {
|
|
todo!()
|
|
}
|
|
}
|