Compare commits

..

18 Commits

Author SHA1 Message Date
Paolo Barbolini
1e22bcd527 Prepare v0.11.14 (#1056) 2025-02-23 10:06:28 +01:00
Paolo Barbolini
75716ca269 feat: make crypto and TLS certificate verify backends opt-in (#1054) 2025-02-23 09:32:47 +01:00
Paolo Barbolini
8a6f1dab0e deprecate: having made AsyncNetworkStream public (#1059) 2025-02-22 21:22:30 +01:00
Paolo Barbolini
621853e2e3 chore(license): bump my copyright year (#1057) 2025-02-22 20:27:07 +01:00
Paolo Barbolini
5e4cb2d1b5 fix: use the same rustls crypto provider everywhere (#1055) 2025-02-22 19:14:28 +01:00
Paolo Barbolini
b4abd40698 style: fix rustls-native-tls warnings when tracing is disabled (#1053) 2025-02-22 17:39:39 +00:00
Paolo Barbolini
2d1ccda2ef style(clippy): ban direct use of std::time::SystemTime::now (#1043) 2025-02-22 08:32:38 +00:00
Paolo Barbolini
54934e1492 build(deps): drop direct dependency on rustls-pki-types (#1051) 2025-02-22 08:07:23 +00:00
Paolo Barbolini
cfa29743a8 refactor: replace rustls-pemfile with rustls-pki-types (#1050) 2025-02-22 08:59:14 +01:00
Paolo Barbolini
4a4a96d805 refactor: remove artifact from web-time refactor (#1049) 2025-02-22 07:44:56 +00:00
Paolo Barbolini
f0b8052a52 build(deps): upgrade nom to v8 (#1048) 2025-02-22 08:36:56 +01:00
Paolo Barbolini
655cd8a140 style: cleanup Cargo.toml (#1047) 2025-02-22 08:30:01 +01:00
Paolo Barbolini
dabc88a053 Prepare v0.11.13 (#1044) 2025-02-17 11:48:42 +01:00
Paolo Barbolini
9cdefcea09 refactor: simplify handling of WASM web-time (#1042) 2025-02-17 09:05:22 +00:00
Abid Omar
5748af4c98 feat: add WASM support via web-time (#1037)
Support WASM environments by using web-time.
This was tested on a Cloudflare worker environment.
2025-02-17 09:53:19 +01:00
André Cruz
3e9b1876d9 feat: add method to obtain TLS result (#1039)
Some TLS toolkits export a result that can be checked afterwards even if
the TLS negotation returned successfully. This can be used for example
if you disabled certificate checks by default, but then want to check
the outcome.

Currently this is only supported on boring TLS.
2025-02-17 09:51:45 +01:00
Popax21
795bedae76 fix: synchronous pool shutdowns being arbitrarily delayed (#1041)
Previously, the connection pool thread did not drop its upgraded `Arc` pool reference while sleeping until the next idle duration check. This causes a drop of the `SmtpTransport` to not shut down any connections until said thread wakes up again (since it still holds a reference to the pool), which can take up to 60s with default settings. In practice, this means that connections will most likely not be properly closed before the program exists, (since the `SmtpTransport` is most likely dropped when the program shuts down) which violates the SMTP specification which states that:
> The sender MUST NOT intentionally close the channel until it sends a QUIT command, and it SHOULD wait until it receives the reply (even if there was an error response to a command).
2025-02-07 08:29:06 +01:00
dependabot[bot]
891dd521ab build(deps): bump openssl from 0.10.68 to 0.10.70 (#1038)
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.68 to 0.10.70.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.68...openssl-v0.10.70)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 20:51:45 +01:00
27 changed files with 667 additions and 272 deletions

View File

@@ -57,12 +57,12 @@ jobs:
- name: Setup cache
uses: Swatinem/rust-cache@v2
- name: Install cargo hack
run: cargo install cargo-hack --debug
- name: Check with cargo hack
run: cargo hack check --feature-powerset --depth 3
run: cargo hack check --feature-powerset --depth 3 --at-least-one-of aws-lc-rs,ring --at-least-one-of rustls-native-certs,webpki-roots
test:
name: test / ${{ matrix.name }}
@@ -90,7 +90,7 @@ jobs:
- name: Setup cache
uses: Swatinem/rust-cache@v2
- name: Install postfix
- name: Install postfix
run: |
DEBIAN_FRONTEND=noninteractive sudo apt-get update
DEBIAN_FRONTEND=noninteractive sudo apt-get -y install postfix
@@ -119,10 +119,10 @@ jobs:
run: cargo test
- name: Test with all features (-native-tls)
run: cargo test --no-default-features --features async-std1,async-std1-rustls-tls,boring-tls,builder,dkim,file-transport,file-transport-envelope,hostname,mime03,pool,rustls-native-certs,rustls-tls,sendmail-transport,smtp-transport,tokio1,tokio1-boring-tls,tokio1-rustls-tls,tracing
run: cargo test --no-default-features --features async-std1,async-std1-rustls,aws-lc-rs,rustls-native-certs,boring-tls,builder,dkim,file-transport,file-transport-envelope,hostname,mime03,pool,rustls-native-certs,rustls,sendmail-transport,smtp-transport,tokio1,tokio1-boring-tls,tokio1-rustls,tracing
- name: Test with all features (-boring-tls)
run: cargo test --no-default-features --features async-std1,async-std1-rustls-tls,builder,dkim,file-transport,file-transport-envelope,hostname,mime03,native-tls,pool,rustls-native-certs,rustls-tls,sendmail-transport,smtp-transport,tokio1,tokio1-native-tls,tokio1-rustls-tls,tracing
run: cargo test --no-default-features --features async-std1,async-std1-rustls,aws-lc-rs,rustls-native-certs,builder,dkim,file-transport,file-transport-envelope,hostname,mime03,native-tls,pool,rustls-native-certs,rustls,sendmail-transport,smtp-transport,tokio1,tokio1-native-tls,tokio1-rustls,tracing
# coverage:
# name: Coverage

View File

@@ -1,3 +1,69 @@
<a name="v0.11.14"></a>
### v0.11.14 (2025-02-23)
This release deprecates the `rustls-tls`, `tokio1-rustls-tls` and `async-std1-rustls-tls`
features, which will be removed in lettre v0.12.
rustls users should start migrating to the `rustls`, `tokio1-rustls` and
`async-std1-rustls` features. Unlike the deprecated _*rustls-tls_ features,
which automatically enabled the `ring` and `webpki-roots` backends, the new
features do not. To complete the migration, users must either enable the
`aws-lc-rs` or the `ring` feature. Additionally, those who rely on `webpki-roots`
for TLS certificate verification must now explicitly enable its feature.
Users of `rustls-native-certs` do not need to enable `webpki-roots`.
Find out more about the new features via the [lettre rustls docs].
#### Features
* Make it possible to use different `rustls` crypto providers and TLS verifiers ([#1054])
#### Bug fixes
* Use the same `rustls` crypto provider everywhere ([#1055])
#### Misc
* Deprecate `AsyncNetworkStream` being public ([#1059])
* Upgrade `nom` to v8 ([#1048])
* Drop `rustls-pemfile` in favor of `rustls-pki-types` APIs ([#1050])
* Ban direct use of `std::time::SystemTime::now` via clippy ([#1043])
* Drop direct dependency on `rustls-pki-types` ([#1051])
* Remove artifact from `web-time` refactor ([#1049])
* Fix warnings with `rustls-native-certs` when `tracing` is disabled ([#1053])
* Bump license year ([#1057])
* Cleanup `Cargo.toml` style ([#1047])
[lettre rustls docs]: https://docs.rs/lettre/0.11.14/lettre/index.html#smtp-over-tls-via-the-rustls-crate
[#1043]: https://github.com/lettre/lettre/pull/1043
[#1047]: https://github.com/lettre/lettre/pull/1047
[#1048]: https://github.com/lettre/lettre/pull/1048
[#1049]: https://github.com/lettre/lettre/pull/1049
[#1050]: https://github.com/lettre/lettre/pull/1050
[#1051]: https://github.com/lettre/lettre/pull/1051
[#1053]: https://github.com/lettre/lettre/pull/1053
[#1054]: https://github.com/lettre/lettre/pull/1054
[#1055]: https://github.com/lettre/lettre/pull/1055
[#1057]: https://github.com/lettre/lettre/pull/1057
[#1059]: https://github.com/lettre/lettre/pull/1059
<a name="v0.11.13"></a>
### v0.11.13 (2025-02-17)
#### Features
* Add WASM support ([#1037], [#1042])
* Add method to get the TLS verify result with BoringSSL ([#1039])
#### Bug fixes
* Synchronous pool shutdowns being arbitrarily delayed ([#1041])
[#1037]: https://github.com/lettre/lettre/pull/1037
[#1039]: https://github.com/lettre/lettre/pull/1039
[#1041]: https://github.com/lettre/lettre/pull/1041
[#1042]: https://github.com/lettre/lettre/pull/1042
<a name="v0.11.12"></a>
### v0.11.12 (2025-02-02)

173
Cargo.lock generated
View File

@@ -241,6 +241,47 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "aws-lc-fips-sys"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29003a681b2b9465c1139bfb726da452a841a8b025f35953f3bce71139f10b21"
dependencies = [
"bindgen 0.69.5",
"cc",
"cmake",
"dunce",
"fs_extra",
"paste",
"regex",
]
[[package]]
name = "aws-lc-rs"
version = "1.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd755adf9707cf671e31d944a189be3deaaeee11c8bc1d669bb8022ac90fbd0"
dependencies = [
"aws-lc-fips-sys",
"aws-lc-sys",
"paste",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9dd2e03ee80ca2822dd6ea431163d2ef259f2066a4d6ccaca6d9dcb386aa43"
dependencies = [
"bindgen 0.69.5",
"cc",
"cmake",
"dunce",
"fs_extra",
"paste",
]
[[package]]
name = "backtrace"
version = "0.3.74"
@@ -268,6 +309,29 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bindgen"
version = "0.69.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools 0.10.5",
"lazy_static",
"lazycell",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 2.0.89",
"which",
]
[[package]]
name = "bindgen"
version = "0.70.1"
@@ -335,7 +399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777cf31ea10f7eb87d46b30848f8d7acbd65cb4a25cd51bee1808ef601de0416"
dependencies = [
"autocfg",
"bindgen",
"bindgen 0.70.1",
"cmake",
"fs_extra",
"fslock",
@@ -371,6 +435,8 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
dependencies = [
"jobserver",
"libc",
"shlex",
]
@@ -380,7 +446,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
"nom 7.1.3",
]
[[package]]
@@ -664,6 +730,12 @@ dependencies = [
"syn 2.0.89",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "ed25519"
version = "2.2.3"
@@ -969,6 +1041,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "hostname"
version = "0.4.0"
@@ -1160,6 +1241,15 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.72"
@@ -1187,9 +1277,15 @@ dependencies = [
"spin",
]
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lettre"
version = "0.11.12"
version = "0.11.14"
dependencies = [
"async-std",
"async-trait",
@@ -1211,15 +1307,13 @@ dependencies = [
"maud",
"mime",
"native-tls",
"nom",
"nom 8.0.0",
"percent-encoding",
"pretty_assertions",
"quoted_printable",
"rsa",
"rustls",
"rustls-native-certs",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"sha2",
@@ -1233,6 +1327,7 @@ dependencies = [
"url",
"uuid",
"walkdir",
"web-time",
"webpki-roots",
]
@@ -1367,6 +1462,15 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -1447,9 +1551,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "openssl"
version = "0.10.68"
version = "0.10.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
dependencies = [
"bitflags",
"cfg-if",
@@ -1479,9 +1583,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.104"
version = "0.9.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
dependencies = [
"cc",
"libc",
@@ -1501,6 +1605,12 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@@ -1628,6 +1738,16 @@ dependencies = [
"yansi",
]
[[package]]
name = "prettyplease"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [
"proc-macro2",
"syn 2.0.89",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@@ -1837,6 +1957,7 @@ version = "0.23.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
@@ -1858,15 +1979,6 @@ dependencies = [
"security-framework 3.0.1",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.10.0"
@@ -1879,6 +1991,7 @@ version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
@@ -2442,6 +2555,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.26.7"
@@ -2451,6 +2574,18 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "winapi"
version = "0.3.9"

View File

@@ -1,7 +1,7 @@
[package]
name = "lettre"
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
version = "0.11.12"
version = "0.11.14"
description = "Email client"
readme = "README.md"
homepage = "https://lettre.rs"
@@ -19,9 +19,12 @@ is-it-maintained-open-issues = { repository = "lettre/lettre" }
maintenance = { status = "actively-developed" }
[dependencies]
email_address = { version = "0.2.1", default-features = false }
chumsky = "0.9"
idna = "1"
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # feature
## tracing support
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true }
# builder
httpdate = { version = "1", optional = true }
@@ -33,11 +36,11 @@ email-encoding = { version = "0.3", optional = true }
# file transport
uuid = { version = "1", features = ["v4"], optional = true }
serde = { version = "1", optional = true, features = ["derive"] }
serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true }
# smtp-transport
nom = { version = "7", optional = true }
nom = { version = "8", optional = true }
hostname = { version = "0.4", optional = true } # feature
socket2 = { version = "0.5.1", optional = true }
url = { version = "2.4", optional = true }
@@ -45,10 +48,8 @@ percent-encoding = { version = "2.3", optional = true }
## tls
native-tls = { version = "0.2.9", optional = true } # feature
rustls = { version = "0.23.5", default-features = false, features = ["ring", "logging", "std", "tls12"], optional = true }
rustls-pemfile = { version = "2", optional = true }
rustls = { version = "0.23.5", default-features = false, features = ["logging", "std", "tls12"], optional = true }
rustls-native-certs = { version = "0.8", optional = true }
rustls-pki-types = { version = "1.7", optional = true }
webpki-roots = { version = "0.26", optional = true }
boring = { version = "4", optional = true }
@@ -59,21 +60,21 @@ async-trait = { version = "0.1", optional = true }
## async-std
async-std = { version = "1.8", optional = true }
futures-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true }
futures-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12"], optional = true }
## tokio
tokio1_crate = { package = "tokio", version = "1", optional = true }
tokio1_native_tls_crate = { package = "tokio-native-tls", version = "0.3", optional = true }
tokio1_rustls = { package = "tokio-rustls", version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true }
tokio1_rustls = { package = "tokio-rustls", version = "0.26", default-features = false, features = ["logging", "tls12"], optional = true }
tokio1_boring = { package = "tokio-boring", version = "4", optional = true }
## dkim
sha2 = { version = "0.10", optional = true, features = ["oid"] }
sha2 = { version = "0.10", features = ["oid"], optional = true }
rsa = { version = "0.9", optional = true }
ed25519-dalek = { version = "2", optional = true }
# email formats
email_address = { version = "0.2.1", default-features = false }
## web-time for wasm support
web-time = { version = "1.1.0", optional = true }
[dev-dependencies]
pretty_assertions = "1"
@@ -108,20 +109,33 @@ smtp-transport = ["dep:base64", "dep:nom", "dep:socket2", "dep:url", "dep:percen
pool = ["dep:futures-util"]
rustls-tls = ["dep:webpki-roots", "dep:rustls", "dep:rustls-pemfile", "dep:rustls-pki-types"]
rustls = ["dep:rustls"]
aws-lc-rs = ["rustls?/aws-lc-rs"]
fips = ["aws-lc-rs", "rustls?/fips"]
ring = ["rustls?/ring"]
webpki-roots = ["dep:webpki-roots"]
# deprecated
rustls-tls = ["webpki-roots", "rustls", "ring"]
boring-tls = ["dep:boring"]
# async
async-std1 = ["dep:async-std", "dep:async-trait", "dep:futures-io", "dep:futures-util"]
async-std1-rustls-tls = ["async-std1", "rustls-tls", "dep:futures-rustls"]
async-std1-rustls = ["async-std1", "rustls", "dep:futures-rustls"]
# deprecated
async-std1-rustls-tls = ["async-std1-rustls", "rustls-tls"]
tokio1 = ["dep:tokio1_crate", "dep:async-trait", "dep:futures-io", "dep:futures-util"]
tokio1-native-tls = ["tokio1", "native-tls", "dep:tokio1_native_tls_crate"]
tokio1-rustls-tls = ["tokio1", "rustls-tls", "dep:tokio1_rustls"]
tokio1-rustls = ["tokio1", "rustls", "dep:tokio1_rustls"]
# deprecated
tokio1-rustls-tls = ["tokio1-rustls", "rustls-tls"]
tokio1-boring-tls = ["tokio1", "boring-tls", "dep:tokio1_boring"]
dkim = ["dep:base64", "dep:sha2", "dep:rsa", "dep:ed25519-dalek"]
# wasm support
web = ["dep:web-time"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(lettre_ignore_tls_mismatch)'] }

View File

@@ -1,5 +1,5 @@
Copyright (c) 2014-2024 Alexis Mousset <contact@amousset.me>
Copyright (c) 2019-2024 Paolo Barbolini <paolo@paolo565.org>
Copyright (c) 2019-2025 Paolo Barbolini <paolo@paolo565.org>
Copyright (c) 2018 K. <kayo@illumium.org>
Permission is hereby granted, free of charge, to any

View File

@@ -28,8 +28,8 @@
</div>
<div align="center">
<a href="https://deps.rs/crate/lettre/0.11.12">
<img src="https://deps.rs/crate/lettre/0.11.12/status.svg"
<a href="https://deps.rs/crate/lettre/0.11.14">
<img src="https://deps.rs/crate/lettre/0.11.14/status.svg"
alt="dependency status" />
</a>
</div>

3
clippy.toml Normal file
View File

@@ -0,0 +1,3 @@
disallowed-methods = [
{ "path" = "std::time::SystemTime::now", reason = "does not work on WASM environments", replacement = "crate::time::now" }
]

View File

@@ -133,7 +133,7 @@ impl Executor for Tokio1Executor {
) -> Result<AsyncSmtpConnection, Error> {
#[allow(clippy::match_single_binding)]
let tls_parameters = match tls {
#[cfg(any(feature = "tokio1-native-tls", feature = "tokio1-rustls-tls"))]
#[cfg(any(feature = "tokio1-native-tls", feature = "tokio1-rustls"))]
Tls::Wrapper(tls_parameters) => Some(tls_parameters.clone()),
_ => None,
};
@@ -147,7 +147,7 @@ impl Executor for Tokio1Executor {
)
.await?;
#[cfg(any(feature = "tokio1-native-tls", feature = "tokio1-rustls-tls"))]
#[cfg(any(feature = "tokio1-native-tls", feature = "tokio1-rustls"))]
match tls {
Tls::Opportunistic(tls_parameters) => {
if conn.can_starttls() {
@@ -230,7 +230,7 @@ impl Executor for AsyncStd1Executor {
) -> Result<AsyncSmtpConnection, Error> {
#[allow(clippy::match_single_binding)]
let tls_parameters = match tls {
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
Tls::Wrapper(tls_parameters) => Some(tls_parameters.clone()),
_ => None,
};
@@ -243,7 +243,7 @@ impl Executor for AsyncStd1Executor {
)
.await?;
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
match tls {
Tls::Opportunistic(tls_parameters) => {
if conn.can_starttls() {

View File

@@ -64,13 +64,46 @@
//!
//! #### SMTP over TLS via the rustls crate
//!
//! _Secure SMTP connections using TLS from the `rustls-tls` crate_
//! _Secure SMTP connections using TLS from the `rustls` crate_
//!
//! Rustls uses [ring] as the cryptography implementation. As a result, [not all Rust's targets are supported][ring-support].
//! * **rustls**: TLS support for the synchronous version of the API
//! * **tokio1-rustls**: TLS support for the `tokio1` async version of the API
//! * **async-std1-rustls**: TLS support for the `async-std1` async version of the API
//!
//! * **rustls-tls**: TLS support for the synchronous version of the API
//! * **tokio1-rustls-tls**: TLS support for the `tokio1` async version of the API
//! * **async-std1-rustls-tls**: TLS support for the `async-std1` async version of the API
//! ##### rustls crypto backends
//!
//! _The crypto implementation to use with rustls_
//!
//! When the `rustls` feature is enabled, one of the following crypto backends MUST also
//! be enabled.
//!
//! * **aws-lc-rs**: use [AWS-LC] (via [`aws-lc-rs`]) as the `rustls` crypto backend
//! * **ring**: use [`ring`] as the `rustls` crypto backend
//!
//! When enabling `aws-lc-rs`, the `fips` feature can also be enabled to have
//! rustls use the FIPS certified module of AWS-LC.
//!
//! `aws-lc-rs` may require cmake on some platforms to compile.
//! `fips` always requires cmake and the Go compiler to compile.
//!
//! ##### rustls certificate verification backend
//!
//! _The TLS certificate verification backend to use with rustls_
//!
//! When the `rustls` feature is enabled, one of the following verification backends
//! MUST also be enabled.
//!
//! * **rustls-native-certs**: verify TLS certificates using the platform's native certificate store (see [`rustls-native-certs`])
//! * **webpki-roots**: verify TLS certificates against Mozilla's root certificates (see [`webpki-roots`])
//!
//! For the `rustls-native-certs` backend to work correctly, the following packages
//! will need to be installed in order for the build stage and the compiled program
//! to run properly.
//!
//! | Distro | Build-time packages | Runtime packages |
//! | ------------ | -------------------------- | ---------------------------- |
//! | Debian | none | `ca-certificates` |
//! | Alpine Linux | none | `ca-certificates` |
//!
//! ### Sendmail transport
//!
@@ -107,6 +140,7 @@
//! * **tracing**: Logging using the `tracing` crate
//! * **mime03**: Allow creating a [`ContentType`] from an existing [mime 0.3] `Mime` struct
//! * **dkim**: Add support for signing email with DKIM
//! * **web**: WebAssembly support using the `web-time` crate for time operations
//!
//! [`SMTP`]: crate::transport::smtp
//! [`sendmail`]: crate::transport::sendmail
@@ -114,14 +148,17 @@
//! [`ContentType`]: crate::message::header::ContentType
//! [tokio]: https://docs.rs/tokio/1
//! [async-std]: https://docs.rs/async-std/1
//! [ring]: https://github.com/briansmith/ring#ring
//! [ring-support]: https://github.com/briansmith/ring#online-automated-testing
//! [AWS-LC]: https://github.com/aws/aws-lc
//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs
//! [`ring`]: https://crates.io/crates/ring
//! [`rustls-native-certs`]: https://crates.io/crates/rustls-native-certs
//! [`webpki-roots`]: https://crates.io/crates/webpki-roots
//! [Tokio 1.x]: https://docs.rs/tokio/1
//! [async-std 1.x]: https://docs.rs/async-std/1
//! [mime 0.3]: https://docs.rs/mime/0.3
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.12")]
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.14")]
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
#![forbid(unsafe_code)]
@@ -162,6 +199,22 @@
#[cfg(not(lettre_ignore_tls_mismatch))]
mod compiletime_checks {
#[cfg(all(feature = "rustls", not(feature = "aws-lc-rs"), not(feature = "ring")))]
compile_error!(
"feature `rustls` also requires either the `aws-lc-rs` or the `ring` feature to
be enabled"
);
#[cfg(all(
feature = "rustls",
not(feature = "rustls-native-certs"),
not(feature = "webpki-roots")
))]
compile_error!(
"feature `rustls` also requires either the `rustls-native-certs` or the `webpki-roots` feature to
be enabled"
);
#[cfg(all(feature = "native-tls", feature = "boring-tls"))]
compile_error!("feature \"native-tls\" and feature \"boring-tls\" cannot be enabled at the same time, otherwise
the executable will fail to link.");
@@ -172,16 +225,12 @@ mod compiletime_checks {
not(feature = "tokio1-native-tls")
))]
compile_error!("Lettre is being built with the `tokio1` and the `native-tls` features, but the `tokio1-native-tls` feature hasn't been turned on.
If you were trying to opt into `rustls-tls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
If you were trying to opt into `rustls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
#[cfg(all(
feature = "tokio1",
feature = "rustls-tls",
not(feature = "tokio1-rustls-tls")
))]
compile_error!("Lettre is being built with the `tokio1` and the `rustls-tls` features, but the `tokio1-rustls-tls` feature hasn't been turned on.
If you'd like to use `native-tls` make sure that the `rustls-tls` feature hasn't been enabled by mistake.
#[cfg(all(feature = "tokio1", feature = "rustls", not(feature = "tokio1-rustls")))]
compile_error!("Lettre is being built with the `tokio1` and the `rustls` features, but the `tokio1-rustls` feature hasn't been turned on.
If you'd like to use `native-tls` make sure that the `rustls` feature hasn't been enabled by mistake.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
#[cfg(all(
@@ -190,22 +239,22 @@ mod compiletime_checks {
not(feature = "tokio1-boring-tls")
))]
compile_error!("Lettre is being built with the `tokio1` and the `boring-tls` features, but the `tokio1-boring-tls` feature hasn't been turned on.
If you'd like to use `boring-tls` make sure that the `rustls-tls` feature hasn't been enabled by mistake.
If you'd like to use `boring-tls` make sure that the `rustls` feature hasn't been enabled by mistake.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
#[cfg(all(feature = "async-std1", feature = "native-tls",))]
#[cfg(all(feature = "async-std1", feature = "native-tls"))]
compile_error!("Lettre is being built with the `async-std1` and the `native-tls` features, but the async-std integration doesn't support native-tls yet.
If you'd like to work on the issue please take a look at https://github.com/lettre/lettre/issues/576.
If you were trying to opt into `rustls-tls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
If you were trying to opt into `rustls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
#[cfg(all(
feature = "async-std1",
feature = "rustls-tls",
not(feature = "async-std1-rustls-tls")
feature = "rustls",
not(feature = "async-std1-rustls")
))]
compile_error!("Lettre is being built with the `async-std1` and the `rustls-tls` features, but the `async-std1-rustls-tls` feature hasn't been turned on.
If you'd like to use `native-tls` make sure that the `rustls-tls` hasn't been enabled by mistake.
compile_error!("Lettre is being built with the `async-std1` and the `rustls` features, but the `async-std1-rustls` feature hasn't been turned on.
If you'd like to use `native-tls` make sure that the `rustls` hasn't been enabled by mistake.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
}
@@ -218,6 +267,9 @@ mod executor;
#[cfg(feature = "builder")]
#[cfg_attr(docsrs, doc(cfg(feature = "builder")))]
pub mod message;
#[cfg(feature = "rustls")]
mod rustls_crypto;
mod time;
pub mod transport;
use std::error::Error as StdError;

View File

@@ -346,7 +346,7 @@ fn dkim_canonicalize_headers<'a>(
/// Sign with Dkim a message by adding Dkim-Signature header created with configuration expressed by
/// `dkim_config`
pub fn dkim_sign(message: &mut Message, dkim_config: &DkimConfig) {
dkim_sign_fixed_time(message, dkim_config, SystemTime::now());
dkim_sign_fixed_time(message, dkim_config, crate::time::now());
}
fn dkim_sign_fixed_time(message: &mut Message, dkim_config: &DkimConfig, timestamp: SystemTime) {

View File

@@ -21,7 +21,7 @@ impl Date {
///
/// Shortcut for `Date::new(SystemTime::now())`
pub fn now() -> Self {
Self::new(SystemTime::now())
Self::new(crate::time::now())
}
}

View File

@@ -277,7 +277,7 @@ impl MessageBuilder {
/// Shortcut for `self.date(SystemTime::now())`, it is automatically inserted
/// if no date has been provided.
pub fn date_now(self) -> Self {
self.date(SystemTime::now())
self.date(crate::time::now())
}
/// Set or add mailbox to `ReplyTo` header

14
src/rustls_crypto.rs Normal file
View File

@@ -0,0 +1,14 @@
use std::sync::Arc;
use rustls::crypto::CryptoProvider;
pub(crate) fn crypto_provider() -> Arc<CryptoProvider> {
CryptoProvider::get_default().cloned().unwrap_or_else(|| {
#[cfg(feature = "aws-lc-rs")]
let provider = rustls::crypto::aws_lc_rs::default_provider();
#[cfg(not(feature = "aws-lc-rs"))]
let provider = rustls::crypto::ring::default_provider();
Arc::new(provider)
})
}

26
src/time.rs Normal file
View File

@@ -0,0 +1,26 @@
use std::time::SystemTime;
#[cfg(feature = "web")]
pub(crate) fn now() -> SystemTime {
fn to_std_systemtime(time: web_time::SystemTime) -> std::time::SystemTime {
let duration = time
.duration_since(web_time::SystemTime::UNIX_EPOCH)
.unwrap();
SystemTime::UNIX_EPOCH + duration
}
// FIXME: change to:
// #[allow(
// clippy::disallowed_methods,
// reason = "`web-time` aliases `std::time::SystemTime::now` on non-WASM platforms"
// )]
#[allow(clippy::disallowed_methods)]
to_std_systemtime(web_time::SystemTime::now())
}
#[cfg(not(feature = "web"))]
pub(crate) fn now() -> SystemTime {
// FIXME: change to #[expect(clippy::disallowed_methods, reason = "the `web` feature is disabled")]
#[allow(clippy::disallowed_methods)]
SystemTime::now()
}

View File

@@ -14,8 +14,8 @@ use super::pool::async_impl::Pool;
use super::PoolConfig;
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
))]
use super::Tls;
use super::{
@@ -111,15 +111,15 @@ where
/// to validate TLS certificates.
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
)))
)]
pub fn relay(relay: &str) -> Result<AsyncSmtpTransportBuilder, Error> {
@@ -145,15 +145,15 @@ where
/// or emails will be sent to the server, protecting from downgrade attacks.
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
)))
)]
pub fn starttls_relay(relay: &str) -> Result<AsyncSmtpTransportBuilder, Error> {
@@ -288,10 +288,10 @@ where
/// # Ok(())
/// # }
/// ```
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn from_url(connection_url: &str) -> Result<AsyncSmtpTransportBuilder, Error> {
super::connection_url::from_connection_url(connection_url)
@@ -416,15 +416,15 @@ impl AsyncSmtpTransportBuilder {
/// lead to hard to debug IO errors coming from the TLS library.
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "async-std1-rustls-tls"
feature = "tokio1-rustls",
feature = "async-std1-rustls"
)))
)]
pub fn tls(mut self, tls: Tls) -> Self {

View File

@@ -6,7 +6,8 @@ use futures_util::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use super::async_net::AsyncTokioStream;
#[cfg(feature = "tracing")]
use super::escape_crlf;
use super::{AsyncNetworkStream, ClientCodec, TlsParameters};
#[allow(deprecated)]
use super::{async_net::AsyncNetworkStream, ClientCodec, TlsParameters};
use crate::{
transport::smtp::{
authentication::{Credentials, Mechanism},
@@ -35,6 +36,7 @@ macro_rules! try_smtp (
pub struct AsyncSmtpConnection {
/// TCP stream between client and server
/// Value is None before connection
#[allow(deprecated)]
stream: BufReader<AsyncNetworkStream>,
/// Panic state
panic: bool,
@@ -56,6 +58,7 @@ impl AsyncSmtpConnection {
stream: Box<dyn AsyncTokioStream>,
hello_name: &ClientId,
) -> Result<AsyncSmtpConnection, Error> {
#[allow(deprecated)]
let stream = AsyncNetworkStream::use_existing_tokio1(stream);
Self::connect_impl(stream, hello_name).await
}
@@ -98,6 +101,7 @@ impl AsyncSmtpConnection {
tls_parameters: Option<TlsParameters>,
local_address: Option<IpAddr>,
) -> Result<AsyncSmtpConnection, Error> {
#[allow(deprecated)]
let stream =
AsyncNetworkStream::connect_tokio1(server, timeout, tls_parameters, local_address)
.await?;
@@ -114,10 +118,12 @@ impl AsyncSmtpConnection {
hello_name: &ClientId,
tls_parameters: Option<TlsParameters>,
) -> Result<AsyncSmtpConnection, Error> {
#[allow(deprecated)]
let stream = AsyncNetworkStream::connect_asyncstd1(server, timeout, tls_parameters).await?;
Self::connect_impl(stream, hello_name).await
}
#[allow(deprecated)]
async fn connect_impl(
stream: AsyncNetworkStream,
hello_name: &ClientId,
@@ -245,6 +251,7 @@ impl AsyncSmtpConnection {
}
/// Sets the underlying stream
#[allow(deprecated)]
pub fn set_stream(&mut self, stream: AsyncNetworkStream) {
self.stream = BufReader::new(stream);
}
@@ -368,13 +375,29 @@ impl AsyncSmtpConnection {
}
/// The X509 certificate of the server (DER encoded)
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> {
self.stream.get_ref().peer_certificate()
}
/// Currently this is only avaialable when using Boring TLS and
/// returns the result of the verification of the TLS certificate
/// presented by the peer, if any. Only the last error encountered
/// during verification is presented.
/// It can be useful when you don't want to fail outright the TLS
/// negotiation, for example when a self-signed certificate is
/// encountered, but still want to record metrics or log the fact.
/// When using DANE verification, the PKI root of trust moves from
/// the CAs to DNS, so self-signed certificates are permitted as long
/// as the TLSA records match the leaf or issuer certificates.
/// It cannot be called on non Boring TLS streams.
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
self.stream.get_ref().tls_verify_result()
}
/// All the X509 certificates of the chain (DER encoded)
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "rustls", feature = "boring-tls"))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
self.stream.get_ref().certificate_chain()
}

View File

@@ -12,9 +12,9 @@ use futures_io::{
AsyncRead as FuturesAsyncRead, AsyncWrite as FuturesAsyncWrite, Error as IoError, ErrorKind,
Result as IoResult,
};
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
use futures_rustls::client::TlsStream as AsyncStd1RustlsTlsStream;
#[cfg(any(feature = "tokio1-rustls-tls", feature = "async-std1-rustls-tls"))]
#[cfg(any(feature = "tokio1-rustls", feature = "async-std1-rustls"))]
use rustls::pki_types::ServerName;
#[cfg(feature = "tokio1-boring-tls")]
use tokio1_boring::SslStream as Tokio1SslStream;
@@ -27,14 +27,14 @@ use tokio1_crate::net::{
};
#[cfg(feature = "tokio1-native-tls")]
use tokio1_native_tls_crate::TlsStream as Tokio1TlsStream;
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
use tokio1_rustls::client::TlsStream as Tokio1RustlsTlsStream;
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "tokio1-rustls",
feature = "tokio1-boring-tls",
feature = "async-std1-rustls-tls"
feature = "async-std1-rustls"
))]
use super::InnerTlsParameters;
use super::TlsParameters;
@@ -44,6 +44,10 @@ use crate::transport::smtp::{error, Error};
/// A network stream
#[derive(Debug)]
#[deprecated(
since = "0.11.14",
note = "This struct was not meant to be made public"
)]
pub struct AsyncNetworkStream {
inner: InnerAsyncNetworkStream,
}
@@ -74,7 +78,7 @@ enum InnerAsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")]
Tokio1NativeTls(Tokio1TlsStream<Box<dyn AsyncTokioStream>>),
/// Encrypted Tokio 1.x TCP stream
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
Tokio1RustlsTls(Tokio1RustlsTlsStream<Box<dyn AsyncTokioStream>>),
/// Encrypted Tokio 1.x TCP stream
#[cfg(feature = "tokio1-boring-tls")]
@@ -83,12 +87,13 @@ enum InnerAsyncNetworkStream {
#[cfg(feature = "async-std1")]
AsyncStd1Tcp(AsyncStd1TcpStream),
/// Encrypted Tokio 1.x TCP stream
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
AsyncStd1RustlsTls(AsyncStd1RustlsTlsStream<AsyncStd1TcpStream>),
/// Can't be built
None,
}
#[allow(deprecated)]
impl AsyncNetworkStream {
fn new(inner: InnerAsyncNetworkStream) -> Self {
if let InnerAsyncNetworkStream::None = inner {
@@ -107,13 +112,13 @@ impl AsyncNetworkStream {
InnerAsyncNetworkStream::Tokio1NativeTls(s) => {
s.get_ref().get_ref().get_ref().peer_addr()
}
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(s) => s.get_ref().0.peer_addr(),
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => s.get_ref().peer_addr(),
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => s.peer_addr(),
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => s.get_ref().0.peer_addr(),
InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
@@ -253,18 +258,18 @@ impl AsyncNetworkStream {
feature = "tokio1",
not(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "tokio1-rustls",
feature = "tokio1-boring-tls"
))
))]
InnerAsyncNetworkStream::Tokio1Tcp(_) => {
let _ = tls_parameters;
panic!("Trying to upgrade an AsyncNetworkStream without having enabled either the tokio1-native-tls or the tokio1-rustls-tls feature");
panic!("Trying to upgrade an AsyncNetworkStream without having enabled either the tokio1-native-tls or the tokio1-rustls feature");
}
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "tokio1-rustls",
feature = "tokio1-boring-tls"
))]
InnerAsyncNetworkStream::Tokio1Tcp(_) => {
@@ -279,13 +284,13 @@ impl AsyncNetworkStream {
.map_err(error::connection)?;
Ok(())
}
#[cfg(all(feature = "async-std1", not(feature = "async-std1-rustls-tls")))]
#[cfg(all(feature = "async-std1", not(feature = "async-std1-rustls")))]
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
let _ = tls_parameters;
panic!("Trying to upgrade an AsyncNetworkStream without having enabled the async-std1-rustls-tls feature");
panic!("Trying to upgrade an AsyncNetworkStream without having enabled the async-std1-rustls feature");
}
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
// get owned TcpStream
let tcp_stream = mem::replace(&mut self.inner, InnerAsyncNetworkStream::None);
@@ -305,7 +310,7 @@ impl AsyncNetworkStream {
#[allow(unused_variables)]
#[cfg(any(
feature = "tokio1-native-tls",
feature = "tokio1-rustls-tls",
feature = "tokio1-rustls",
feature = "tokio1-boring-tls"
))]
async fn upgrade_tokio1_tls(
@@ -332,12 +337,12 @@ impl AsyncNetworkStream {
Ok(InnerAsyncNetworkStream::Tokio1NativeTls(stream))
};
}
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerTlsParameters::RustlsTls(config) => {
#[cfg(not(feature = "tokio1-rustls-tls"))]
panic!("built without the tokio1-rustls-tls feature");
#[cfg(not(feature = "tokio1-rustls"))]
panic!("built without the tokio1-rustls feature");
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
return {
use tokio1_rustls::TlsConnector;
@@ -372,7 +377,7 @@ impl AsyncNetworkStream {
}
#[allow(unused_variables)]
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
async fn upgrade_asyncstd1_tls(
tcp_stream: AsyncStd1TcpStream,
mut tls_parameters: TlsParameters,
@@ -384,12 +389,12 @@ impl AsyncNetworkStream {
InnerTlsParameters::NativeTls(connector) => {
panic!("native-tls isn't supported with async-std yet. See https://github.com/lettre/lettre/pull/531#issuecomment-757893531");
}
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerTlsParameters::RustlsTls(config) => {
#[cfg(not(feature = "async-std1-rustls-tls"))]
panic!("built without the async-std1-rustls-tls feature");
#[cfg(not(feature = "async-std1-rustls"))]
panic!("built without the async-std1-rustls feature");
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
return {
use futures_rustls::TlsConnector;
@@ -417,18 +422,42 @@ impl AsyncNetworkStream {
InnerAsyncNetworkStream::Tokio1Tcp(_) => false,
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => true,
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(_) => true,
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(_) => true,
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => false,
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => true,
InnerAsyncNetworkStream::None => false,
}
}
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
match &self.inner {
#[cfg(feature = "tokio1")]
InnerAsyncNetworkStream::Tokio1Tcp(_) => {
Err(error::client("Connection is not encrypted"))
}
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(stream) => {
stream.ssl().verify_result().map_err(error::tls)
}
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
Err(error::client("Connection is not encrypted"))
}
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => panic!("Unsupported"),
InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
}
}
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
match &self.inner {
#[cfg(feature = "tokio1")]
@@ -437,7 +466,7 @@ impl AsyncNetworkStream {
}
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(stream) => Ok(stream
.get_ref()
.1
@@ -458,7 +487,7 @@ impl AsyncNetworkStream {
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
Err(error::client("Connection is not encrypted"))
}
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream) => Ok(stream
.get_ref()
.1
@@ -485,7 +514,7 @@ impl AsyncNetworkStream {
.unwrap()
.to_der()
.map_err(error::tls)?),
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(stream) => Ok(stream
.get_ref()
.1
@@ -505,7 +534,7 @@ impl AsyncNetworkStream {
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
Err(error::client("Connection is not encrypted"))
}
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream) => Ok(stream
.get_ref()
.1
@@ -519,6 +548,7 @@ impl AsyncNetworkStream {
}
}
#[allow(deprecated)]
impl FuturesAsyncRead for AsyncNetworkStream {
fn poll_read(
mut self: Pin<&mut Self>,
@@ -544,7 +574,7 @@ impl FuturesAsyncRead for AsyncNetworkStream {
Poll::Pending => Poll::Pending,
}
}
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(s) => {
let mut b = Tokio1ReadBuf::new(buf);
match Pin::new(s).poll_read(cx, &mut b) {
@@ -564,7 +594,7 @@ impl FuturesAsyncRead for AsyncNetworkStream {
}
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_read(cx, buf),
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_read(cx, buf),
InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
@@ -574,6 +604,7 @@ impl FuturesAsyncRead for AsyncNetworkStream {
}
}
#[allow(deprecated)]
impl FuturesAsyncWrite for AsyncNetworkStream {
fn poll_write(
mut self: Pin<&mut Self>,
@@ -585,13 +616,13 @@ impl FuturesAsyncWrite for AsyncNetworkStream {
InnerAsyncNetworkStream::Tokio1Tcp(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_write(cx, buf),
InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
@@ -606,13 +637,13 @@ impl FuturesAsyncWrite for AsyncNetworkStream {
InnerAsyncNetworkStream::Tokio1Tcp(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_flush(cx),
InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
@@ -627,13 +658,13 @@ impl FuturesAsyncWrite for AsyncNetworkStream {
InnerAsyncNetworkStream::Tokio1Tcp(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "tokio1-rustls-tls")]
#[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_close(cx),
#[cfg(feature = "async-std1-rustls-tls")]
#[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_close(cx),
InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");

View File

@@ -143,7 +143,7 @@ impl SmtpConnection {
hello_name: &ClientId,
) -> Result<(), Error> {
if self.server_info.supports_feature(Extension::StartTls) {
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
{
try_smtp!(self.command(Starttls), self);
self.stream.get_mut().upgrade_tls(tls_parameters)?;
@@ -153,11 +153,7 @@ impl SmtpConnection {
try_smtp!(self.ehlo(hello_name), self);
Ok(())
}
#[cfg(not(any(
feature = "native-tls",
feature = "rustls-tls",
feature = "boring-tls"
)))]
#[cfg(not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))]
// This should never happen as `Tls` can only be created
// when a TLS library is enabled
unreachable!("TLS support required but not supported");
@@ -303,13 +299,29 @@ impl SmtpConnection {
}
/// The X509 certificate of the server (DER encoded)
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> {
self.stream.get_ref().peer_certificate()
}
/// Currently this is only avaialable when using Boring TLS and
/// returns the result of the verification of the TLS certificate
/// presented by the peer, if any. Only the last error encountered
/// during verification is presented.
/// It can be useful when you don't want to fail outright the TLS
/// negotiation, for example when a self-signed certificate is
/// encountered, but still want to record metrics or log the fact.
/// When using DANE verification, the PKI root of trust moves from
/// the CAs to DNS, so self-signed certificates are permitted as long
/// as the TLSA records match the leaf or issuer certificates.
/// It cannot be called on non Boring TLS streams.
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
self.stream.get_ref().tls_verify_result()
}
/// All the X509 certificates of the chain (DER encoded)
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "rustls", feature = "boring-tls"))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
self.stream.get_ref().certificate_chain()
}

View File

@@ -28,13 +28,14 @@ use std::fmt::Debug;
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
pub use self::async_connection::AsyncSmtpConnection;
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
#[allow(deprecated)]
pub use self::async_net::AsyncNetworkStream;
#[cfg(feature = "tokio1")]
pub use self::async_net::AsyncTokioStream;
use self::net::NetworkStream;
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub(super) use self::tls::InnerTlsParameters;
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub use self::tls::TlsVersion;
pub use self::{
connection::SmtpConnection,

View File

@@ -1,4 +1,4 @@
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
use std::sync::Arc;
use std::{
io::{self, Read, Write},
@@ -11,11 +11,11 @@ use std::{
use boring::ssl::SslStream;
#[cfg(feature = "native-tls")]
use native_tls::TlsStream;
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
use rustls::{pki_types::ServerName, ClientConnection, StreamOwned};
use socket2::{Domain, Protocol, Type};
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
use super::InnerTlsParameters;
use super::TlsParameters;
use crate::transport::smtp::{error, Error};
@@ -36,7 +36,7 @@ enum InnerNetworkStream {
#[cfg(feature = "native-tls")]
NativeTls(TlsStream<TcpStream>),
/// Encrypted TCP stream
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
RustlsTls(StreamOwned<ClientConnection, TcpStream>),
#[cfg(feature = "boring-tls")]
BoringTls(SslStream<TcpStream>),
@@ -59,7 +59,7 @@ impl NetworkStream {
InnerNetworkStream::Tcp(s) => s.peer_addr(),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.get_ref().peer_addr(),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(s) => s.get_ref().peer_addr(),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.get_ref().peer_addr(),
@@ -79,7 +79,7 @@ impl NetworkStream {
InnerNetworkStream::Tcp(s) => s.shutdown(how),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.get_ref().shutdown(how),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(s) => s.get_ref().shutdown(how),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.get_ref().shutdown(how),
@@ -146,17 +146,13 @@ impl NetworkStream {
pub fn upgrade_tls(&mut self, tls_parameters: &TlsParameters) -> Result<(), Error> {
match &self.inner {
#[cfg(not(any(
feature = "native-tls",
feature = "rustls-tls",
feature = "boring-tls"
)))]
#[cfg(not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))]
InnerNetworkStream::Tcp(_) => {
let _ = tls_parameters;
panic!("Trying to upgrade an NetworkStream without having enabled either the native-tls or the rustls-tls feature");
panic!("Trying to upgrade an NetworkStream without having enabled either the `native-tls` or the `rustls` feature");
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
InnerNetworkStream::Tcp(_) => {
// get owned TcpStream
let tcp_stream = mem::replace(&mut self.inner, InnerNetworkStream::None);
@@ -171,7 +167,7 @@ impl NetworkStream {
}
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
fn upgrade_tls_impl(
tcp_stream: TcpStream,
tls_parameters: &TlsParameters,
@@ -184,7 +180,7 @@ impl NetworkStream {
.map_err(error::connection)?;
InnerNetworkStream::NativeTls(stream)
}
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerTlsParameters::RustlsTls(connector) => {
let domain = ServerName::try_from(tls_parameters.domain())
.map_err(|_| error::connection("domain isn't a valid DNS name"))?;
@@ -211,7 +207,7 @@ impl NetworkStream {
InnerNetworkStream::Tcp(_) => false,
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => true,
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(_) => true,
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(_) => true,
@@ -222,13 +218,29 @@ impl NetworkStream {
}
}
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(_) => panic!("Unsupported"),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => {
stream.ssl().verify_result().map_err(error::tls)
}
InnerNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
}
}
#[cfg(any(feature = "rustls", feature = "boring-tls"))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(stream) => Ok(stream
.conn
.peer_certificates()
@@ -248,7 +260,7 @@ impl NetworkStream {
}
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> {
match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
@@ -259,7 +271,7 @@ impl NetworkStream {
.unwrap()
.to_der()
.map_err(error::tls)?),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(stream) => Ok(stream
.conn
.peer_certificates()
@@ -283,7 +295,7 @@ impl NetworkStream {
InnerNetworkStream::Tcp(stream) => stream.set_read_timeout(duration),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(stream) => stream.get_ref().set_read_timeout(duration),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(stream) => stream.get_ref().set_read_timeout(duration),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => stream.get_ref().set_read_timeout(duration),
@@ -301,7 +313,7 @@ impl NetworkStream {
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(stream) => stream.get_ref().set_write_timeout(duration),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(stream) => stream.get_ref().set_write_timeout(duration),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => stream.get_ref().set_write_timeout(duration),
@@ -319,7 +331,7 @@ impl Read for NetworkStream {
InnerNetworkStream::Tcp(s) => s.read(buf),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.read(buf),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(s) => s.read(buf),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.read(buf),
@@ -337,7 +349,7 @@ impl Write for NetworkStream {
InnerNetworkStream::Tcp(s) => s.write(buf),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.write(buf),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(s) => s.write(buf),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.write(buf),
@@ -353,7 +365,7 @@ impl Write for NetworkStream {
InnerNetworkStream::Tcp(s) => s.flush(),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.flush(),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
InnerNetworkStream::RustlsTls(s) => s.flush(),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.flush(),

View File

@@ -1,6 +1,6 @@
use std::fmt::{self, Debug};
#[cfg(feature = "rustls-tls")]
use std::{io, sync::Arc};
#[cfg(feature = "rustls")]
use std::sync::Arc;
#[cfg(feature = "boring-tls")]
use boring::{
@@ -10,23 +10,22 @@ use boring::{
};
#[cfg(feature = "native-tls")]
use native_tls::{Protocol, TlsConnector};
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
use rustls::{
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
crypto::WebPkiSupportedAlgorithms,
crypto::{verify_tls12_signature, verify_tls13_signature},
pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime},
crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider},
pki_types::{self, pem::PemObject, CertificateDer, PrivateKeyDer, ServerName, UnixTime},
server::ParsedCertificate,
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", feature = "boring-tls"))]
use crate::transport::smtp::{error, Error};
/// TLS protocol versions.
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub enum TlsVersion {
/// TLS 1.0
///
@@ -86,10 +85,10 @@ pub enum Tls {
///
/// Warning: A malicious intermediary could intercept the `STARTTLS` flag,
/// causing lettre to believe the server only supports plaintext connections.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
Opportunistic(TlsParameters),
/// Begin with a plaintext connection and require `STARTTLS` for security.
@@ -101,10 +100,10 @@ pub enum Tls {
/// Unlike [`Tls::Opportunistic`], this option is secure against MITM attacks.
/// For optimal security and performance, consider using [`Tls::Wrapper`] instead,
/// as it requires fewer roundtrips to establish a secure connection.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
Required(TlsParameters),
/// Establish a connection wrapped in TLS from the start.
@@ -114,10 +113,10 @@ pub enum Tls {
/// transmitting any sensitive data.
///
/// This is the fastest and most secure option for establishing a connection.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
Wrapper(TlsParameters),
}
@@ -126,11 +125,11 @@ impl Debug for Tls {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::None => f.pad("None"),
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
Self::Opportunistic(_) => f.pad("Opportunistic"),
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
Self::Required(_) => f.pad("Required"),
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
Self::Wrapper(_) => f.pad("Wrapper"),
}
}
@@ -154,7 +153,7 @@ pub enum CertificateStore {
/// Use a hardcoded set of Mozilla roots via the `webpki-roots` crate.
///
/// This option is only available in the rustls backend.
#[cfg(feature = "rustls-tls")]
#[cfg(all(feature = "rustls", feature = "webpki-roots"))]
WebpkiRoots,
/// Don't use any system certificates.
None,
@@ -179,7 +178,7 @@ pub struct TlsParametersBuilder {
identity: Option<Identity>,
accept_invalid_hostnames: bool,
accept_invalid_certs: bool,
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
min_tls_version: TlsVersion,
}
@@ -193,7 +192,7 @@ impl TlsParametersBuilder {
identity: None,
accept_invalid_hostnames: false,
accept_invalid_certs: false,
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
min_tls_version: TlsVersion::Tlsv12,
}
}
@@ -231,10 +230,10 @@ impl TlsParametersBuilder {
/// including those from other sites, are trusted.
///
/// This method introduces significant vulnerabilities to man-in-the-middle attacks.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn dangerous_accept_invalid_hostnames(mut self, accept_invalid_hostnames: bool) -> Self {
self.accept_invalid_hostnames = accept_invalid_hostnames;
@@ -244,7 +243,7 @@ impl TlsParametersBuilder {
/// Controls which minimum TLS version is allowed
///
/// Defaults to [`Tlsv12`][TlsVersion::Tlsv12].
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub fn set_min_tls_version(mut self, min_tls_version: TlsVersion) -> Self {
self.min_tls_version = min_tls_version;
self
@@ -273,17 +272,17 @@ impl TlsParametersBuilder {
/// Creates a new `TlsParameters` using native-tls, boring-tls or rustls
/// depending on which one is available
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn build(self) -> Result<TlsParameters, Error> {
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
return self.build_rustls();
#[cfg(all(not(feature = "rustls-tls"), feature = "native-tls"))]
#[cfg(all(not(feature = "rustls"), feature = "native-tls"))]
return self.build_native();
#[cfg(all(not(feature = "rustls-tls"), feature = "boring-tls"))]
#[cfg(all(not(feature = "rustls"), feature = "boring-tls"))]
return self.build_boring();
}
@@ -397,8 +396,8 @@ impl TlsParametersBuilder {
}
/// Creates a new `TlsParameters` using rustls with the provided configuration
#[cfg(feature = "rustls-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub fn build_rustls(self) -> Result<TlsParameters, Error> {
let just_version3 = &[&rustls::version::TLS13];
let supported_versions = match self.min_tls_version {
@@ -412,14 +411,12 @@ impl TlsParametersBuilder {
TlsVersion::Tlsv13 => just_version3,
};
let tls = ClientConfig::builder_with_protocol_versions(supported_versions);
let provider = rustls::crypto::CryptoProvider::get_default()
.cloned()
.unwrap_or_else(|| Arc::new(rustls::crypto::ring::default_provider()));
let crypto_provider = crate::rustls_crypto::crypto_provider();
let tls = ClientConfig::builder_with_provider(Arc::clone(&crypto_provider))
.with_protocol_versions(supported_versions)
.map_err(error::tls)?;
// Build TLS config
let signature_algorithms = provider.signature_verification_algorithms;
let mut root_cert_store = RootCertStore::empty();
#[cfg(feature = "rustls-native-certs")]
@@ -433,9 +430,11 @@ impl TlsParametersBuilder {
tracing::debug!(
"loaded platform certs with {errors_len} failing to load, {added} valid and {ignored} ignored (invalid) certs"
);
#[cfg(not(feature = "tracing"))]
let _ = (errors_len, added, ignored);
}
#[cfg(feature = "rustls-tls")]
#[cfg(all(feature = "rustls", feature = "webpki-roots"))]
fn load_webpki_roots(store: &mut RootCertStore) {
store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
}
@@ -444,10 +443,10 @@ impl TlsParametersBuilder {
CertificateStore::Default => {
#[cfg(feature = "rustls-native-certs")]
load_native_roots(&mut root_cert_store);
#[cfg(not(feature = "rustls-native-certs"))]
#[cfg(all(not(feature = "rustls-native-certs"), feature = "webpki-roots"))]
load_webpki_roots(&mut root_cert_store);
}
#[cfg(feature = "rustls-tls")]
#[cfg(all(feature = "rustls", feature = "webpki-roots"))]
CertificateStore::WebpkiRoots => {
load_webpki_roots(&mut root_cert_store);
}
@@ -464,7 +463,7 @@ impl TlsParametersBuilder {
ignore_invalid_hostnames: self.accept_invalid_hostnames,
ignore_invalid_certs: self.accept_invalid_certs,
roots: root_cert_store,
signature_algorithms,
crypto_provider,
};
tls.dangerous()
.with_custom_certificate_verifier(Arc::new(verifier))
@@ -494,7 +493,7 @@ impl TlsParametersBuilder {
pub enum InnerTlsParameters {
#[cfg(feature = "native-tls")]
NativeTls(TlsConnector),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
RustlsTls(Arc<ClientConfig>),
#[cfg(feature = "boring-tls")]
BoringTls(SslConnector),
@@ -503,10 +502,10 @@ pub enum InnerTlsParameters {
impl TlsParameters {
/// Creates a new `TlsParameters` using native-tls or rustls
/// depending on which one is available
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn new(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build()
@@ -525,8 +524,8 @@ impl TlsParameters {
}
/// Creates a new `TlsParameters` using rustls
#[cfg(feature = "rustls-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub fn new_rustls(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_rustls()
}
@@ -549,13 +548,13 @@ impl TlsParameters {
pub struct Certificate {
#[cfg(feature = "native-tls")]
native_tls: native_tls::Certificate,
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
rustls: Vec<CertificateDer<'static>>,
#[cfg(feature = "boring-tls")]
boring_tls: boring::x509::X509,
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
impl Certificate {
/// Create a `Certificate` from a DER encoded certificate
pub fn from_der(der: Vec<u8>) -> Result<Self, Error> {
@@ -568,7 +567,7 @@ impl Certificate {
Ok(Self {
#[cfg(feature = "native-tls")]
native_tls: native_tls_cert,
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
rustls: vec![der.into()],
#[cfg(feature = "boring-tls")]
boring_tls: boring_tls_cert,
@@ -583,20 +582,17 @@ impl Certificate {
#[cfg(feature = "boring-tls")]
let boring_tls_cert = boring::x509::X509::from_pem(pem).map_err(error::tls)?;
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
let rustls_cert = {
use std::io::Cursor;
let mut pem = Cursor::new(pem);
rustls_pemfile::certs(&mut pem)
.collect::<io::Result<Vec<_>>>()
CertificateDer::pem_slice_iter(pem)
.collect::<Result<Vec<_>, pki_types::pem::Error>>()
.map_err(|_| error::tls("invalid certificates"))?
};
Ok(Self {
#[cfg(feature = "native-tls")]
native_tls: native_tls_cert,
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
rustls: rustls_cert,
#[cfg(feature = "boring-tls")]
boring_tls: boring_tls_cert,
@@ -615,7 +611,7 @@ impl Debug for Certificate {
pub struct Identity {
#[cfg(feature = "native-tls")]
native_tls: native_tls::Identity,
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
rustls_tls: (Vec<CertificateDer<'static>>, PrivateKeyDer<'static>),
#[cfg(feature = "boring-tls")]
boring_tls: (boring::x509::X509, PKey<boring::pkey::Private>),
@@ -632,7 +628,7 @@ impl Clone for Identity {
Identity {
#[cfg(feature = "native-tls")]
native_tls: self.native_tls.clone(),
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
rustls_tls: (self.rustls_tls.0.clone(), self.rustls_tls.1.clone_key()),
#[cfg(feature = "boring-tls")]
boring_tls: (self.boring_tls.0.clone(), self.boring_tls.1.clone()),
@@ -640,13 +636,13 @@ impl Clone for Identity {
}
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
impl Identity {
pub fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
Ok(Self {
#[cfg(feature = "native-tls")]
native_tls: Identity::from_pem_native_tls(pem, key)?,
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
rustls_tls: Identity::from_pem_rustls_tls(pem, key)?,
#[cfg(feature = "boring-tls")]
boring_tls: Identity::from_pem_boring_tls(pem, key)?,
@@ -658,14 +654,19 @@ impl Identity {
native_tls::Identity::from_pkcs8(pem, key).map_err(error::tls)
}
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
fn from_pem_rustls_tls(
pem: &[u8],
mut key: &[u8],
key: &[u8],
) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Error> {
let key = rustls_pemfile::private_key(&mut key)
.map_err(error::tls)?
.ok_or_else(|| error::tls("no private key found"))?;
let key = match PrivateKeyDer::from_pem_slice(key) {
Ok(key) => key,
Err(pki_types::pem::Error::NoItemsFound) => {
return Err(error::tls("no private key found"))
}
Err(err) => return Err(error::tls(err)),
};
Ok((vec![pem.to_owned().into()], key))
}
@@ -680,16 +681,16 @@ impl Identity {
}
}
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
#[derive(Debug)]
struct InvalidCertsVerifier {
ignore_invalid_hostnames: bool,
ignore_invalid_certs: bool,
roots: RootCertStore,
signature_algorithms: WebPkiSupportedAlgorithms,
crypto_provider: Arc<CryptoProvider>,
}
#[cfg(feature = "rustls-tls")]
#[cfg(feature = "rustls")]
impl ServerCertVerifier for InvalidCertsVerifier {
fn verify_server_cert(
&self,
@@ -707,7 +708,7 @@ impl ServerCertVerifier for InvalidCertsVerifier {
&self.roots,
intermediates,
now,
self.signature_algorithms.all,
self.crypto_provider.signature_verification_algorithms.all,
)?;
}
@@ -727,7 +728,7 @@ impl ServerCertVerifier for InvalidCertsVerifier {
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
&self.crypto_provider.signature_verification_algorithms,
)
}
@@ -741,12 +742,12 @@ impl ServerCertVerifier for InvalidCertsVerifier {
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
&self.crypto_provider.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
rustls::crypto::ring::default_provider()
self.crypto_provider
.signature_verification_algorithms
.supported_schemes()
}

View File

@@ -2,7 +2,7 @@ use std::borrow::Cow;
use url::Url;
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
use super::client::{Tls, TlsParameters};
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
use super::AsyncSmtpTransportBuilder;
@@ -82,19 +82,19 @@ pub(crate) fn from_connection_url<B: TransportBuilder>(connection_url: &str) ->
("smtp", None) => {
builder = builder.port(connection_url.port().unwrap_or(SMTP_PORT));
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
("smtp", Some("required")) => {
builder = builder
.port(connection_url.port().unwrap_or(SUBMISSION_PORT))
.tls(Tls::Required(TlsParameters::new(host.into())?));
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
("smtp", Some("opportunistic")) => {
builder = builder
.port(connection_url.port().unwrap_or(SUBMISSION_PORT))
.tls(Tls::Opportunistic(TlsParameters::new(host.into())?));
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
("smtps", _) => {
builder = builder
.port(connection_url.port().unwrap_or(SUBMISSIONS_PORT))

View File

@@ -68,10 +68,10 @@ impl Error {
}
/// Returns true if the error is from TLS
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn is_tls(&self) -> bool {
matches!(self.inner.kind, Kind::Tls)
@@ -107,9 +107,9 @@ pub(crate) enum Kind {
/// TLS error
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
Tls,
}
@@ -134,7 +134,7 @@ impl fmt::Display for Error {
Kind::Client => f.write_str("internal client error")?,
Kind::Network => f.write_str("network error")?,
Kind::Connection => f.write_str("Connection error")?,
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
Kind::Tls => f.write_str("tls error")?,
Kind::Transient(code) => {
write!(f, "transient error ({code})")?;
@@ -185,7 +185,7 @@ pub(crate) fn connection<E: Into<BoxError>>(e: E) -> Error {
Error::new(Kind::Connection, Some(e))
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub(crate) fn tls<E: Into<BoxError>>(e: E) -> Error {
Error::new(Kind::Tls, Some(e))
}

View File

@@ -32,7 +32,7 @@
//! do the following:
//!
//! ```rust,no_run
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls-tls")))]
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
//! use lettre::{
//! message::header::ContentType,
@@ -73,7 +73,7 @@
//! For more information take a look at [`SmtpTransport::from_url`] or [`AsyncSmtpTransport::from_url`].
//!
//! ```rust,no_run
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls-tls")))]
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
//! use lettre::{
//! message::header::ContentType,
@@ -101,7 +101,7 @@
//! #### Advanced configuration with custom TLS settings
//!
//! ```rust,no_run
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls-tls")))]
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
//! use std::fs;
//!
@@ -146,7 +146,7 @@
//! In a webserver context it may go about this:
//!
//! ```rust,no_run
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls-tls")))]
//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
//! # fn test() {
//! use lettre::{
//! message::header::ContentType,
@@ -199,7 +199,7 @@ pub use self::{
error::Error,
transport::{SmtpTransport, SmtpTransportBuilder},
};
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
use crate::transport::smtp::client::TlsParameters;
use crate::transport::smtp::{
authentication::{Credentials, Mechanism, DEFAULT_MECHANISMS},

View File

@@ -109,6 +109,7 @@ impl Pool {
}
}
drop(pool);
thread::sleep(idle_timeout);
}
})

View File

@@ -12,8 +12,8 @@ use nom::{
bytes::streaming::{tag, take_until},
combinator::{complete, map},
multi::many0,
sequence::{preceded, tuple},
IResult,
sequence::preceded,
IResult, Parser,
};
use crate::transport::smtp::{error, Error};
@@ -221,7 +221,8 @@ fn parse_severity(i: &str) -> IResult<&str, Severity> {
map(tag("3"), |_| Severity::PositiveIntermediate),
map(tag("4"), |_| Severity::TransientNegativeCompletion),
map(tag("5"), |_| Severity::PermanentNegativeCompletion),
))(i)
))
.parse(i)
}
fn parse_category(i: &str) -> IResult<&str, Category> {
@@ -232,7 +233,8 @@ fn parse_category(i: &str) -> IResult<&str, Category> {
map(tag("3"), |_| Category::Unspecified3),
map(tag("4"), |_| Category::Unspecified4),
map(tag("5"), |_| Category::MailSystem),
))(i)
))
.parse(i)
}
fn parse_detail(i: &str) -> IResult<&str, Detail> {
@@ -247,18 +249,20 @@ fn parse_detail(i: &str) -> IResult<&str, Detail> {
map(tag("7"), |_| Detail::Seven),
map(tag("8"), |_| Detail::Eight),
map(tag("9"), |_| Detail::Nine),
))(i)
))
.parse(i)
}
pub(crate) fn parse_response(i: &str) -> IResult<&str, Response> {
let (i, lines) = many0(tuple((
let (i, lines) = many0((
parse_code,
preceded(tag("-"), take_until("\r\n")),
tag("\r\n"),
)))(i)?;
))
.parse(i)?;
let (i, (last_code, last_line)) =
tuple((parse_code, preceded(tag(" "), take_until("\r\n"))))(i)?;
let (i, _) = complete(tag("\r\n"))(i)?;
(parse_code, preceded(tag(" "), take_until("\r\n"))).parse(i)?;
let (i, _) = complete(tag("\r\n")).parse(i)?;
// Check that all codes are equal.
if !lines.iter().all(|&(code, _, _)| code == last_code) {

View File

@@ -7,7 +7,7 @@ use super::pool::sync_impl::Pool;
#[cfg(feature = "pool")]
use super::PoolConfig;
use super::{ClientId, Credentials, Error, Mechanism, Response, SmtpConnection, SmtpInfo};
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
use super::{Tls, TlsParameters, SUBMISSIONS_PORT, SUBMISSION_PORT};
use crate::{address::Envelope, Transport};
@@ -77,10 +77,10 @@ impl SmtpTransport {
///
/// Creates an encrypted transport over submissions port, using the provided domain
/// to validate TLS certificates.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn relay(relay: &str) -> Result<SmtpTransportBuilder, Error> {
let tls_parameters = TlsParameters::new(relay.into())?;
@@ -101,10 +101,10 @@ impl SmtpTransport {
///
/// An error is returned if the connection can't be upgraded. No credentials
/// or emails will be sent to the server, protecting from downgrade attacks.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn starttls_relay(relay: &str) -> Result<SmtpTransportBuilder, Error> {
let tls_parameters = TlsParameters::new(relay.into())?;
@@ -230,10 +230,10 @@ impl SmtpTransport {
/// # Ok(())
/// # }
/// ```
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn from_url(connection_url: &str) -> Result<SmtpTransportBuilder, Error> {
super::connection_url::from_connection_url(connection_url)
@@ -333,10 +333,10 @@ impl SmtpTransportBuilder {
///
/// Using the wrong [`Tls`] and [`Self::port`] combination may
/// lead to hard to debug IO errors coming from the TLS library.
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn tls(mut self, tls: Tls) -> Self {
self.info.tls = tls;
@@ -380,7 +380,7 @@ impl SmtpClient {
pub fn connection(&self) -> Result<SmtpConnection, Error> {
#[allow(clippy::match_single_binding)]
let tls_parameters = match &self.info.tls {
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
Tls::Wrapper(tls_parameters) => Some(tls_parameters),
_ => None,
};
@@ -394,7 +394,7 @@ impl SmtpClient {
None,
)?;
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
match &self.info.tls {
Tls::Opportunistic(tls_parameters) => {
if conn.can_starttls() {