1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::{convert::TryFrom, io, sync::Arc};
use rustls::{
client::{ServerCertVerified, ServerCertVerifier, WebPkiVerifier},
Certificate, ClientConfig, ClientConnection, OwnedTrustAnchor, RootCertStore, ServerName,
};
use tokio::net::TcpStream;
use tokio_rustls::{client::TlsStream, TlsConnector};
use crate::{Error, SmtpClient};
use super::AssertReply;
impl SmtpClient<TcpStream> {
pub async fn start_tls(
mut self,
tls_connector: &TlsConnector,
hostname: &str,
) -> crate::Result<SmtpClient<TlsStream<TcpStream>>> {
self.cmd(b"STARTTLS\r\n")
.await?
.assert_positive_completion()?;
self.into_tls(tls_connector, hostname).await
}
pub async fn into_tls(
self,
tls_connector: &TlsConnector,
hostname: &str,
) -> crate::Result<SmtpClient<TlsStream<TcpStream>>> {
tokio::time::timeout(self.timeout, async {
Ok(SmtpClient {
stream: tls_connector
.connect(
ServerName::try_from(hostname).map_err(|_| crate::Error::InvalidTLSName)?,
self.stream,
)
.await
.map_err(|err| {
let kind = err.kind();
if let Some(inner) = err.into_inner() {
match inner.downcast::<rustls::Error>() {
Ok(error) => Error::Tls(error),
Err(error) => Error::Io(io::Error::new(kind, error)),
}
} else {
Error::Io(io::Error::new(kind, "Unspecified"))
}
})?,
timeout: self.timeout,
})
})
.await
.map_err(|_| crate::Error::Timeout)?
}
}
impl SmtpClient<TlsStream<TcpStream>> {
pub fn tls_connection(&self) -> &ClientConnection {
self.stream.get_ref().1
}
}
pub fn build_tls_connector(allow_invalid_certs: bool) -> TlsConnector {
let config = ClientConfig::builder().with_safe_defaults();
let config = if !allow_invalid_certs {
let mut root_cert_store = RootCertStore::empty();
root_cert_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(
|ta| {
OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
},
));
config
.with_custom_certificate_verifier(Arc::new(WebPkiVerifier::new(root_cert_store, None)))
} else {
config.with_custom_certificate_verifier(Arc::new(DummyVerifier {}))
}
.with_no_client_auth();
TlsConnector::from(Arc::new(config))
}
#[doc(hidden)]
struct DummyVerifier;
impl ServerCertVerifier for DummyVerifier {
fn verify_server_cert(
&self,
_e: &Certificate,
_i: &[Certificate],
_sn: &ServerName,
_sc: &mut dyn Iterator<Item = &[u8]>,
_o: &[u8],
_n: std::time::SystemTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
}