Compare commits

..

13 Commits

Author SHA1 Message Date
Paolo Barbolini
c1bf5dfda1 Prepare 0.11.3 (#929) 2024-01-02 18:45:34 +01:00
Paolo Barbolini
1c1fef8055 Drop once_cell dependency in favor of OnceLock from std (#928) 2024-01-02 11:53:47 +01:00
Paolo Barbolini
1540f16015 Upgrade rustls to v0.22 (#921) 2024-01-02 11:41:16 +01:00
Tobias Bieniek
330daa1173 transport/smtp: Implement Debug trait (#925) 2023-12-17 09:20:51 +01:00
Tobias Bieniek
47f2fe0750 transport/file: Derive Clone impls (#924) 2023-12-16 21:30:57 +01:00
Paolo Barbolini
8b6cee30ee Prepare 0.11.2 (#919) 2023-11-23 09:49:21 +01:00
Paolo Barbolini
62c16e90ef Bump idna to v0.5 (#918) 2023-11-23 08:23:25 +00:00
Paolo Barbolini
e0494a5f9d Bump boringssl crates to v4 (#915) 2023-11-19 11:49:43 +01:00
Paolo Barbolini
8c3bffa728 Bump MSRV to 1.70 (#916) 2023-11-19 11:42:49 +01:00
Paolo Barbolini
47eda90433 Prepare 0.11.1 (#910) 2023-10-24 23:47:49 +02:00
Paolo Barbolini
46ea8c48ac Ignore rustls deprecation warning 2023-10-24 22:55:16 +02:00
Paolo Barbolini
5f7063fdc3 Fix accidental disabling of webpki-roots setup (#909) 2023-10-24 22:54:22 +02:00
Paolo Barbolini
61c1f6bc6f Fix date in changelog 2023-10-15 17:22:48 +02:00
11 changed files with 146 additions and 91 deletions

View File

@@ -75,8 +75,8 @@ jobs:
rust: stable rust: stable
- name: beta - name: beta
rust: beta rust: beta
- name: 1.65.0 - name: '1.70'
rust: 1.65.0 rust: '1.70'
steps: steps:
- name: Checkout - name: Checkout

View File

@@ -1,5 +1,48 @@
<a name="v0.11.3"></a>
### v0.11.3 (2024-01-02)
#### Features
* Derive `Clone` for `FileTransport` and `AsyncFileTransport` ([#924])
* Derive `Debug` for `SmtpTransport` ([#925])
#### Misc
* Upgrade `rustls` to v0.22 ([#921])
* Drop once_cell dependency in favor of OnceLock from std ([#928])
[#921]: https://github.com/lettre/lettre/pull/921
[#924]: https://github.com/lettre/lettre/pull/924
[#925]: https://github.com/lettre/lettre/pull/925
[#928]: https://github.com/lettre/lettre/pull/928
<a name="v0.11.2"></a>
### v0.11.2 (2023-11-23)
#### Upgrade notes
* MSRV is now 1.70 ([#916])
#### Misc
* Bump `idna` to v0.5 ([#918])
* Bump `boring` and `tokio-boring` to v4 ([#915])
[#915]: https://github.com/lettre/lettre/pull/915
[#916]: https://github.com/lettre/lettre/pull/916
[#918]: https://github.com/lettre/lettre/pull/918
<a name="v0.11.1"></a>
### v0.11.1 (2023-10-24)
#### Bug fixes
* Fix `webpki-roots` certificate store setup ([#909])
[#909]: https://github.com/lettre/lettre/pull/909
<a name="v0.11.0"></a> <a name="v0.11.0"></a>
### v0.11.0 (2023-08-15) ### v0.11.0 (2023-10-15)
While this release technically contains breaking changes, we expect most projects While this release technically contains breaking changes, we expect most projects
to be able to upgrade by only bumping the version in `Cargo.toml`. to be able to upgrade by only bumping the version in `Cargo.toml`.

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lettre" name = "lettre"
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge) # remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
version = "0.11.0" version = "0.11.3"
description = "Email client" description = "Email client"
readme = "README.md" readme = "README.md"
homepage = "https://lettre.rs" homepage = "https://lettre.rs"
@@ -11,7 +11,7 @@ authors = ["Alexis Mousset <contact@amousset.me>", "Paolo Barbolini <paolo@paolo
categories = ["email", "network-programming"] categories = ["email", "network-programming"]
keywords = ["email", "smtp", "mailer", "message", "sendmail"] keywords = ["email", "smtp", "mailer", "message", "sendmail"]
edition = "2021" edition = "2021"
rust-version = "1.65" rust-version = "1.70"
[badges] [badges]
is-it-maintained-issue-resolution = { repository = "lettre/lettre" } is-it-maintained-issue-resolution = { repository = "lettre/lettre" }
@@ -20,8 +20,7 @@ maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
chumsky = "0.9" chumsky = "0.9"
idna = "0.4" idna = "0.5"
once_cell = { version = "1", optional = true }
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # feature tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # feature
# builder # builder
@@ -45,11 +44,11 @@ url = { version = "2.4", optional = true }
## tls ## tls
native-tls = { version = "0.2.5", optional = true } # feature native-tls = { version = "0.2.5", optional = true } # feature
rustls = { version = "0.21", features = ["dangerous_configuration"], optional = true } rustls = { version = "0.22.1", optional = true }
rustls-pemfile = { version = "1", optional = true } rustls-pemfile = { version = "2", optional = true }
rustls-native-certs = { version = "0.6.2", optional = true } rustls-native-certs = { version = "0.7", optional = true }
webpki-roots = { version = "0.25", optional = true } webpki-roots = { version = "0.26", optional = true }
boring = { version = "3", optional = true } boring = { version = "4", optional = true }
# async # async
futures-io = { version = "0.3.7", optional = true } futures-io = { version = "0.3.7", optional = true }
@@ -59,13 +58,13 @@ async-trait = { version = "0.1", optional = true }
## async-std ## async-std
async-std = { version = "1.8", optional = true } async-std = { version = "1.8", optional = true }
#async-native-tls = { version = "0.3.3", optional = true } #async-native-tls = { version = "0.3.3", optional = true }
futures-rustls = { version = "0.24", optional = true } futures-rustls = { version = "0.25", optional = true }
## tokio ## tokio
tokio1_crate = { package = "tokio", version = "1", optional = true } tokio1_crate = { package = "tokio", version = "1", optional = true }
tokio1_native_tls_crate = { package = "tokio-native-tls", version = "0.3", optional = true } tokio1_native_tls_crate = { package = "tokio-native-tls", version = "0.3", optional = true }
tokio1_rustls = { package = "tokio-rustls", version = "0.24", optional = true } tokio1_rustls = { package = "tokio-rustls", version = "0.25", optional = true }
tokio1_boring = { package = "tokio-boring", version = "3", optional = true } tokio1_boring = { package = "tokio-boring", version = "4", optional = true }
## dkim ## dkim
sha2 = { version = "0.10", optional = true, features = ["oid"] } sha2 = { version = "0.10", optional = true, features = ["oid"] }
@@ -104,7 +103,7 @@ mime03 = ["dep:mime"]
file-transport = ["dep:uuid", "tokio1_crate?/fs", "tokio1_crate?/io-util"] file-transport = ["dep:uuid", "tokio1_crate?/fs", "tokio1_crate?/io-util"]
file-transport-envelope = ["serde", "dep:serde_json", "file-transport"] file-transport-envelope = ["serde", "dep:serde_json", "file-transport"]
sendmail-transport = ["tokio1_crate?/process", "tokio1_crate?/io-util", "async-std?/unstable"] sendmail-transport = ["tokio1_crate?/process", "tokio1_crate?/io-util", "async-std?/unstable"]
smtp-transport = ["dep:base64", "dep:nom", "dep:socket2", "dep:once_cell", "dep:url", "tokio1_crate?/rt", "tokio1_crate?/time", "tokio1_crate?/net"] smtp-transport = ["dep:base64", "dep:nom", "dep:socket2", "dep:url", "tokio1_crate?/rt", "tokio1_crate?/time", "tokio1_crate?/net"]
pool = ["dep:futures-util"] pool = ["dep:futures-util"]

View File

@@ -28,8 +28,8 @@
</div> </div>
<div align="center"> <div align="center">
<a href="https://deps.rs/crate/lettre/0.11.0"> <a href="https://deps.rs/crate/lettre/0.11.3">
<img src="https://deps.rs/crate/lettre/0.11.0/status.svg" <img src="https://deps.rs/crate/lettre/0.11.3/status.svg"
alt="dependency status" /> alt="dependency status" />
</a> </a>
</div> </div>
@@ -53,12 +53,12 @@ Lettre does not provide (for now):
## Supported Rust Versions ## Supported Rust Versions
Lettre supports all Rust versions released in the last 6 months. At the time of writing Lettre supports all Rust versions released in the last 6 months. At the time of writing
the minimum supported Rust version is 1.65, but this could change at any time either from the minimum supported Rust version is 1.70, but this could change at any time either from
one of our dependencies bumping their MSRV or by a new patch release of lettre. one of our dependencies bumping their MSRV or by a new patch release of lettre.
## Example ## Example
This library requires Rust 1.65 or newer. This library requires Rust 1.70 or newer.
To use this library, add the following to your `Cargo.toml`: To use this library, add the following to your `Cargo.toml`:
```toml ```toml

View File

@@ -6,7 +6,7 @@
//! * Secure defaults //! * Secure defaults
//! * Async support //! * Async support
//! //!
//! Lettre requires Rust 1.65 or newer. //! Lettre requires Rust 1.70 or newer.
//! //!
//! ## Features //! ## Features
//! //!
@@ -109,7 +109,7 @@
//! [mime 0.3]: https://docs.rs/mime/0.3 //! [mime 0.3]: https://docs.rs/mime/0.3
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376 //! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.0")] #![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.3")]
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")] #![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")] #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]

View File

@@ -157,7 +157,7 @@ mod error;
type Id = String; type Id = String;
/// Writes the content and the envelope information to a file /// Writes the content and the envelope information to a file
#[derive(Debug)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(docsrs, doc(cfg(feature = "file-transport")))] #[cfg_attr(docsrs, doc(cfg(feature = "file-transport")))]
pub struct FileTransport { pub struct FileTransport {
@@ -167,7 +167,7 @@ pub struct FileTransport {
} }
/// Asynchronously writes the content and the envelope information to a file /// Asynchronously writes the content and the envelope information to a file
#[derive(Debug)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))]
#[cfg(any(feature = "async-std1", feature = "tokio1"))] #[cfg(any(feature = "async-std1", feature = "tokio1"))]

View File

@@ -16,6 +16,8 @@ use futures_io::{
}; };
#[cfg(feature = "async-std1-rustls-tls")] #[cfg(feature = "async-std1-rustls-tls")]
use futures_rustls::client::TlsStream as AsyncStd1RustlsTlsStream; use futures_rustls::client::TlsStream as AsyncStd1RustlsTlsStream;
#[cfg(any(feature = "tokio1-rustls-tls", feature = "async-std1-rustls-tls"))]
use rustls::pki_types::ServerName;
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
use tokio1_boring::SslStream as Tokio1SslStream; use tokio1_boring::SslStream as Tokio1SslStream;
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
@@ -350,7 +352,6 @@ impl AsyncNetworkStream {
#[cfg(feature = "tokio1-rustls-tls")] #[cfg(feature = "tokio1-rustls-tls")]
return { return {
use rustls::ServerName;
use tokio1_rustls::TlsConnector; use tokio1_rustls::TlsConnector;
let domain = ServerName::try_from(domain.as_str()) let domain = ServerName::try_from(domain.as_str())
@@ -358,7 +359,7 @@ impl AsyncNetworkStream {
let connector = TlsConnector::from(config); let connector = TlsConnector::from(config);
let stream = connector let stream = connector
.connect(domain, tcp_stream) .connect(domain.to_owned(), tcp_stream)
.await .await
.map_err(error::connection)?; .map_err(error::connection)?;
Ok(InnerAsyncNetworkStream::Tokio1RustlsTls(stream)) Ok(InnerAsyncNetworkStream::Tokio1RustlsTls(stream))
@@ -424,14 +425,13 @@ impl AsyncNetworkStream {
#[cfg(feature = "async-std1-rustls-tls")] #[cfg(feature = "async-std1-rustls-tls")]
return { return {
use futures_rustls::TlsConnector; use futures_rustls::TlsConnector;
use rustls::ServerName;
let domain = ServerName::try_from(domain.as_str()) let domain = ServerName::try_from(domain.as_str())
.map_err(|_| error::connection("domain isn't a valid DNS name"))?; .map_err(|_| error::connection("domain isn't a valid DNS name"))?;
let connector = TlsConnector::from(config); let connector = TlsConnector::from(config);
let stream = connector let stream = connector
.connect(domain, tcp_stream) .connect(domain.to_owned(), tcp_stream)
.await .await
.map_err(error::connection)?; .map_err(error::connection)?;
Ok(InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream)) Ok(InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream))
@@ -486,8 +486,7 @@ impl AsyncNetworkStream {
.unwrap() .unwrap()
.first() .first()
.unwrap() .unwrap()
.clone() .to_vec()),
.0),
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(stream) => Ok(stream InnerAsyncNetworkStream::Tokio1BoringTls(stream) => Ok(stream
.ssl() .ssl()
@@ -509,8 +508,7 @@ impl AsyncNetworkStream {
.unwrap() .unwrap()
.first() .first()
.unwrap() .unwrap()
.clone() .to_vec()),
.0),
InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"), InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
} }
} }

View File

@@ -12,7 +12,7 @@ use boring::ssl::SslStream;
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
use native_tls::TlsStream; use native_tls::TlsStream;
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
use rustls::{ClientConnection, ServerName, StreamOwned}; use rustls::{pki_types::ServerName, ClientConnection, StreamOwned};
use socket2::{Domain, Protocol, Type}; use socket2::{Domain, Protocol, Type};
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))] #[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
@@ -189,7 +189,7 @@ impl NetworkStream {
InnerTlsParameters::RustlsTls(connector) => { InnerTlsParameters::RustlsTls(connector) => {
let domain = ServerName::try_from(tls_parameters.domain()) let domain = ServerName::try_from(tls_parameters.domain())
.map_err(|_| error::connection("domain isn't a valid DNS name"))?; .map_err(|_| error::connection("domain isn't a valid DNS name"))?;
let connection = ClientConnection::new(Arc::clone(connector), domain) let connection = ClientConnection::new(Arc::clone(connector), domain.to_owned())
.map_err(error::connection)?; .map_err(error::connection)?;
let stream = StreamOwned::new(connection, tcp_stream); let stream = StreamOwned::new(connection, tcp_stream);
InnerNetworkStream::RustlsTls(stream) InnerNetworkStream::RustlsTls(stream)
@@ -241,8 +241,7 @@ impl NetworkStream {
.unwrap() .unwrap()
.first() .first()
.unwrap() .unwrap()
.clone() .to_vec()),
.0),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => Ok(stream InnerNetworkStream::BoringTls(stream) => Ok(stream
.ssl() .ssl()

View File

@@ -1,6 +1,6 @@
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
use std::{sync::Arc, time::SystemTime}; use std::{io, sync::Arc};
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
use boring::{ use boring::{
@@ -11,8 +11,10 @@ use boring::{
use native_tls::{Protocol, TlsConnector}; use native_tls::{Protocol, TlsConnector};
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
use rustls::{ use rustls::{
client::{ServerCertVerified, ServerCertVerifier, WebPkiVerifier}, client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
ClientConfig, Error as TlsError, RootCertStore, ServerName, crypto::{verify_tls12_signature, verify_tls13_signature},
pki_types::{CertificateDer, ServerName, UnixTime},
ClientConfig, DigitallySignedStruct, Error as TlsError, RootCertStore, SignatureScheme,
}; };
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))] #[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
@@ -115,7 +117,7 @@ pub enum CertificateStore {
/// Use a hardcoded set of Mozilla roots via the `webpki-roots` crate. /// Use a hardcoded set of Mozilla roots via the `webpki-roots` crate.
/// ///
/// This option is only available in the rustls backend. /// This option is only available in the rustls backend.
#[cfg(feature = "webpki-roots")] #[cfg(feature = "rustls-tls")]
WebpkiRoots, WebpkiRoots,
/// Don't use any system certificates. /// Don't use any system certificates.
None, None,
@@ -337,8 +339,6 @@ impl TlsParametersBuilder {
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
pub fn build_rustls(self) -> Result<TlsParameters, Error> { pub fn build_rustls(self) -> Result<TlsParameters, Error> {
let tls = ClientConfig::builder();
let just_version3 = &[&rustls::version::TLS13]; let just_version3 = &[&rustls::version::TLS13];
let supported_versions = match self.min_tls_version { let supported_versions = match self.min_tls_version {
TlsVersion::Tlsv10 => { TlsVersion::Tlsv10 => {
@@ -351,58 +351,38 @@ impl TlsParametersBuilder {
TlsVersion::Tlsv13 => just_version3, TlsVersion::Tlsv13 => just_version3,
}; };
let tls = tls let tls = ClientConfig::builder_with_protocol_versions(supported_versions);
.with_safe_default_cipher_suites()
.with_safe_default_kx_groups()
.with_protocol_versions(supported_versions)
.map_err(error::tls)?;
let tls = if self.accept_invalid_certs { let tls = if self.accept_invalid_certs {
tls.with_custom_certificate_verifier(Arc::new(InvalidCertsVerifier {})) tls.dangerous()
.with_custom_certificate_verifier(Arc::new(InvalidCertsVerifier {}))
} else { } else {
let mut root_cert_store = RootCertStore::empty(); let mut root_cert_store = RootCertStore::empty();
#[cfg(feature = "rustls-native-certs")] #[cfg(feature = "rustls-native-certs")]
fn load_native_roots(store: &mut RootCertStore) -> Result<(), Error> { fn load_native_roots(store: &mut RootCertStore) -> Result<(), Error> {
let native_certs = rustls_native_certs::load_native_certs().map_err(error::tls)?; let native_certs = rustls_native_certs::load_native_certs().map_err(error::tls)?;
let mut valid_count = 0; let (added, ignored) = store.add_parsable_certificates(native_certs);
let mut invalid_count = 0;
for cert in native_certs {
match store.add(&rustls::Certificate(cert.0)) {
Ok(_) => valid_count += 1,
Err(err) => {
#[cfg(feature = "tracing")]
tracing::debug!("certificate parsing failed: {:?}", err);
invalid_count += 1;
}
}
}
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
tracing::debug!( tracing::debug!(
"loaded platform certs with {valid_count} valid and {invalid_count} invalid certs" "loaded platform certs with {added} valid and {ignored} ignored (invalid) certs"
); );
Ok(()) Ok(())
} }
#[cfg(feature = "webpki-roots")] #[cfg(feature = "rustls-tls")]
fn load_webpki_roots(store: &mut RootCertStore) { fn load_webpki_roots(store: &mut RootCertStore) {
store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
}));
} }
match self.cert_store { match self.cert_store {
CertificateStore::Default => { CertificateStore::Default => {
#[cfg(feature = "rustls-native-certs")] #[cfg(feature = "rustls-native-certs")]
load_native_roots(&mut root_cert_store)?; load_native_roots(&mut root_cert_store)?;
#[cfg(all(not(feature = "rustls-native-certs"), feature = "webpki-roots"))] #[cfg(not(feature = "rustls-native-certs"))]
load_webpki_roots(&mut root_cert_store); load_webpki_roots(&mut root_cert_store);
} }
#[cfg(feature = "webpki-roots")] #[cfg(feature = "rustls-tls")]
CertificateStore::WebpkiRoots => { CertificateStore::WebpkiRoots => {
load_webpki_roots(&mut root_cert_store); load_webpki_roots(&mut root_cert_store);
} }
@@ -410,14 +390,11 @@ impl TlsParametersBuilder {
} }
for cert in self.root_certs { for cert in self.root_certs {
for rustls_cert in cert.rustls { for rustls_cert in cert.rustls {
root_cert_store.add(&rustls_cert).map_err(error::tls)?; root_cert_store.add(rustls_cert).map_err(error::tls)?;
} }
} }
tls.with_custom_certificate_verifier(Arc::new(WebPkiVerifier::new( tls.with_root_certificates(root_cert_store)
root_cert_store,
None,
)))
}; };
let tls = tls.with_no_client_auth(); let tls = tls.with_no_client_auth();
@@ -491,7 +468,7 @@ pub struct Certificate {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
native_tls: native_tls::Certificate, native_tls: native_tls::Certificate,
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
rustls: Vec<rustls::Certificate>, rustls: Vec<CertificateDer<'static>>,
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
boring_tls: boring::x509::X509, boring_tls: boring::x509::X509,
} }
@@ -510,7 +487,7 @@ impl Certificate {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
native_tls: native_tls_cert, native_tls: native_tls_cert,
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
rustls: vec![rustls::Certificate(der)], rustls: vec![der.into()],
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
boring_tls: boring_tls_cert, boring_tls: boring_tls_cert,
}) })
@@ -530,10 +507,8 @@ impl Certificate {
let mut pem = Cursor::new(pem); let mut pem = Cursor::new(pem);
rustls_pemfile::certs(&mut pem) rustls_pemfile::certs(&mut pem)
.collect::<io::Result<Vec<_>>>()
.map_err(|_| error::tls("invalid certificates"))? .map_err(|_| error::tls("invalid certificates"))?
.into_iter()
.map(rustls::Certificate)
.collect::<Vec<_>>()
}; };
Ok(Self { Ok(Self {
@@ -554,19 +529,53 @@ impl Debug for Certificate {
} }
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
#[derive(Debug)]
struct InvalidCertsVerifier; struct InvalidCertsVerifier;
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
impl ServerCertVerifier for InvalidCertsVerifier { impl ServerCertVerifier for InvalidCertsVerifier {
fn verify_server_cert( fn verify_server_cert(
&self, &self,
_end_entity: &rustls::Certificate, _end_entity: &CertificateDer<'_>,
_intermediates: &[rustls::Certificate], _intermediates: &[CertificateDer<'_>],
_server_name: &ServerName, _server_name: &ServerName<'_>,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8], _ocsp_response: &[u8],
_now: SystemTime, _now: UnixTime,
) -> Result<ServerCertVerified, TlsError> { ) -> Result<ServerCertVerified, TlsError> {
Ok(ServerCertVerified::assertion()) Ok(ServerCertVerified::assertion())
} }
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
verify_tls12_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
verify_tls13_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
} }

View File

@@ -2,7 +2,7 @@ use std::{
fmt::{self, Debug}, fmt::{self, Debug},
mem, mem,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::Arc, sync::{Arc, OnceLock},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@@ -10,7 +10,6 @@ use futures_util::{
lock::Mutex, lock::Mutex,
stream::{self, StreamExt}, stream::{self, StreamExt},
}; };
use once_cell::sync::OnceCell;
use super::{ use super::{
super::{client::AsyncSmtpConnection, Error}, super::{client::AsyncSmtpConnection, Error},
@@ -22,7 +21,7 @@ pub struct Pool<E: Executor> {
config: PoolConfig, config: PoolConfig,
connections: Mutex<Vec<ParkedConnection>>, connections: Mutex<Vec<ParkedConnection>>,
client: AsyncSmtpClient<E>, client: AsyncSmtpClient<E>,
handle: OnceCell<E::Handle>, handle: OnceLock<E::Handle>,
} }
struct ParkedConnection { struct ParkedConnection {
@@ -41,7 +40,7 @@ impl<E: Executor> Pool<E> {
config, config,
connections: Mutex::new(Vec::new()), connections: Mutex::new(Vec::new()),
client, client,
handle: OnceCell::new(), handle: OnceLock::new(),
}); });
{ {

View File

@@ -1,6 +1,6 @@
#[cfg(feature = "pool")] #[cfg(feature = "pool")]
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::{fmt::Debug, time::Duration};
#[cfg(feature = "pool")] #[cfg(feature = "pool")]
use super::pool::sync_impl::Pool; use super::pool::sync_impl::Pool;
@@ -38,6 +38,14 @@ impl Transport for SmtpTransport {
} }
} }
impl Debug for SmtpTransport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut builder = f.debug_struct("SmtpTransport");
builder.field("inner", &self.inner);
builder.finish()
}
}
impl SmtpTransport { impl SmtpTransport {
/// Simple and secure transport, using TLS connections to communicate with the SMTP server /// Simple and secure transport, using TLS connections to communicate with the SMTP server
/// ///