Clean up tls-related feature flags

Jonas SchΓ€fer created

This provides a clear and consistent interface for selecting TLS-related
features on both (xmpp and tokio-xmpp) crates. All feature checks have
been revamped. All working combinations have been tested (including a
connectivity test + --all-features docs build) using:

```
set -xeuo pipefail
features=(aws_lc_rs ring ktls,aws_lc_rs ktls,ring aws_lc_rs,ring aws_lc_rs,ring,ktls native-tls rustls-any-backend)
export RUSTFLAGS=" -D warnings"
cargo test --no-default-features
cargo test
for feature in ${features[@]}; do
  echo ">>> BUILDING with $feature" 2>&1
  # Running code or building examples cannot succeed with rustls-any-backend.
  features="starttls,$feature"
  if [ "$feature" != 'rustls-any-backend' ]; then
    if ! cargo test --no-default-features --features="$features"; then
      echo ">>> BUILD FAILED for tls feature set: $features" >&2
      exit 1
    fi
    set +e
    timeout -sINT -p -k 2 3 cargo run --no-default-features --features="$features" --example keep_connection -- test@hub.sotecware.net "$(pass xmpp/test@hub.sotecware.net)"
    status="$?"
    set -e
    if [ $status -ne 0 ]; then
      echo ">>> keep_connection did not shut down cleanly! (status: $status)" >&2
      exit 1
    fi
  else
    if ! cargo build --no-default-features --features="$feature"; then
      echo ">>> BUILD FAILED for tls feature set: $features" >&2
      exit 1
    fi
  fi
done
RUSTDOCFLAGS="--cfg docsrs" RUSTFLAGS="--cfg xmpprs_doc_build" cargo +nightly doc -Zrustdoc-map --all-features
```

Change summary

tokio-xmpp/Cargo.toml                   | 52 +++++++++++++----
tokio-xmpp/ChangeLog                    | 16 +++--
tokio-xmpp/examples/contact_addr.rs     | 19 +++---
tokio-xmpp/examples/download_avatars.rs | 19 +++---
tokio-xmpp/examples/echo_bot.rs         | 19 +++---
tokio-xmpp/examples/echo_component.rs   | 19 +++---
tokio-xmpp/examples/echo_server.rs      | 19 +++---
tokio-xmpp/examples/keep_connection.rs  | 28 +++++----
tokio-xmpp/src/connect/starttls.rs      | 40 +++++++------
tokio-xmpp/src/lib.rs                   | 80 +++++++++++++++++++++++---
xmpp/Cargo.toml                         | 19 ++++-
xmpp/ChangeLog                          | 15 ++--
xmpp/examples/hello_bot.rs              | 19 +++---
xmpp/src/builder.rs                     |  4 
xmpp/src/lib.rs                         | 40 +++++++++++++
15 files changed, 273 insertions(+), 135 deletions(-)

Detailed changes

tokio-xmpp/Cargo.toml πŸ”—

@@ -15,7 +15,7 @@ edition = "2021"
 bytes = "1"
 futures = "0.3"
 log = "0.4"
-tokio = { version = "1", features = ["net", "rt", "rt-multi-thread", "macros"] }
+tokio = { version = "1", features = ["net", "rt", "rt-multi-thread", "macros", "io-util"] }
 tokio-stream = { version = "0.1", features = ["sync"] }
 webpki-roots = { version = "0.26", optional = true }
 rustls-native-certs = { version = "0.7", optional = true }
@@ -40,21 +40,47 @@ ktls = { version = "6", optional = true }
 env_logger = { version = "0.11", default-features = false, features = ["auto-color", "humantime"] }
 # this is needed for echo-component example
 tokio = { version = "1", features = ["signal", "test-util"] }
-tokio-xmpp = { path = ".", default-features = false, features = ["insecure-tcp"]}
+tokio-xmpp = { path = ".", default-features = false, features = ["rustls-native-certs"]}
+
+[[example]]
+name = "contact_addr"
+required-features = ["starttls"]
+
+[[example]]
+name = "download_avatars"
+required-features = ["starttls"]
+
+[[example]]
+name = "echo_bot"
+required-features = ["starttls"]
+
+[[example]]
+name = "echo_component"
+required-features = ["starttls", "insecure-tcp"]
+
+[[example]]
+name = "echo_server"
+required-features = ["starttls"]
+
+[[example]]
+name = "keep_connection"
+required-features = ["starttls"]
 
 [features]
-default = ["starttls-rust", "rustls-native-certs", "tls-rust-aws_lc_rs"]
+default = ["starttls", "aws_lc_rs", "rustls-native-certs"]
 starttls = ["dns"]
-tls-rust = ["tokio-rustls"]
-tls-rust-aws_lc_rs = ["tokio-rustls?/aws_lc_rs"]
-tls-rust-ring = ["tokio-rustls?/ring"]
-tls-rust-ktls = ["tls-rust", "ktls"]
-tls-rust-native-certs = ["tls-rust", "rustls-native-certs"]
-tls-rust-webpki-roots = ["tls-rust", "webpki-roots"]
-tls-native = ["tokio-native-tls", "native-tls"]
-starttls-native = ["starttls", "tls-native"]
-starttls-rust = ["starttls", "tls-rust"]
-insecure-tcp = ["tokio/io-util"]
+
+aws_lc_rs = ["rustls-any-backend", "tokio-rustls/aws_lc_rs"]
+ring = ["rustls-any-backend", "tokio-rustls/ring"]
+native-tls = ["dep:tokio-native-tls", "dep:native-tls"]
+ktls = ["rustls-any-backend", "dep:ktls"]
+
+rustls-any-backend = ["dep:tokio-rustls"]
+
+rustls-native-certs = ["dep:rustls-native-certs"]
+webpki-roots = ["dep:webpki-roots"]
+
+insecure-tcp = []
 syntax-highlighting = ["syntect"]
 # Enable serde support in jid crate
 serde = [ "xmpp-parsers/serde" ]

tokio-xmpp/ChangeLog πŸ”—

@@ -25,13 +25,15 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
       - `Component` is now gated behind `insecure-tcp` feature flag
       - `XMPPStream` and `XmppCodec` were removed in favour of the newly
         implemented `tokio_xmpp::xmlstream module.
-      - The `starttls-rust`, `tls-rust` and `tls-rust-ktls` feature flags do
-        not automatically enable a `rustls` crypto provider anymore. This is
-        to avoid conflict between two crypto providers and to avoid linking
-        unnecessary code. The `aws_lc_rs` crypto provider is still built by
-        default, however, applications which use `tokio_xmpp` without default
-        features will have to adapt their feature flags to explicitly enable
-        the `tls-rust-aws_lc_rs` or `tls-rust-ring` features. (!581)
+      - The TLS-related feature flags have been completely reworked to make
+        them easier to use. The `tls-rust-*`, `tls-native` and `starttls-*`
+        feature flags have been removed in favour of more concise flag names
+        identifying the TLS backends directly (`aws_lc_rs`, `ring`,
+        `native-tls`). The `starttls` feature is now independent of the
+        specific backend (but a backend still needs to be enabled for
+        compilation to succeed).
+
+        Please refer to the crate docs for details. (!581)
     * Added:
       - Support for sending IQ requests while tracking their responses in a
         Future.

tokio-xmpp/examples/contact_addr.rs πŸ”—

@@ -2,10 +2,7 @@ use futures::stream::StreamExt;
 use std::env::args;
 use std::process::exit;
 use std::str::FromStr;
-#[cfg(all(
-    feature = "tls-rust",
-    any(feature = "tls-rust-aws_lc_rs", feature = "tls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_xmpp::rustls;
 use tokio_xmpp::{Client, IqRequest, IqResponse};
 use xmpp_parsers::{
@@ -15,20 +12,22 @@ use xmpp_parsers::{
     server_info::ServerInfo,
 };
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() {
     env_logger::init();
 
-    #[cfg(all(feature = "tls-rust", feature = "tls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "tls-rust",
-        feature = "tls-rust-ring",
-        not(feature = "tls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");

tokio-xmpp/examples/download_avatars.rs πŸ”—

@@ -4,10 +4,7 @@ use std::fs::{create_dir_all, File};
 use std::io::{self, Write};
 use std::process::exit;
 use std::str::FromStr;
-#[cfg(all(
-    feature = "tls-rust",
-    any(feature = "tls-rust-aws_lc_rs", feature = "tls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_xmpp::rustls;
 use tokio_xmpp::{Client, Stanza};
 use xmpp_parsers::{
@@ -27,20 +24,22 @@ use xmpp_parsers::{
     stanza_error::{DefinedCondition, ErrorType, StanzaError},
 };
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() {
     env_logger::init();
 
-    #[cfg(all(feature = "tls-rust", feature = "tls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "tls-rust",
-        feature = "tls-rust-ring",
-        not(feature = "tls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");

tokio-xmpp/examples/echo_bot.rs πŸ”—

@@ -2,30 +2,29 @@ use futures::stream::StreamExt;
 use std::env::args;
 use std::process::exit;
 use std::str::FromStr;
-#[cfg(all(
-    feature = "tls-rust",
-    any(feature = "tls-rust-aws_lc_rs", feature = "tls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_xmpp::rustls;
 use tokio_xmpp::Client;
 use xmpp_parsers::jid::{BareJid, Jid};
 use xmpp_parsers::message::{Lang, Message, MessageType};
 use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() {
     env_logger::init();
 
-    #[cfg(all(feature = "tls-rust", feature = "tls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "tls-rust",
-        feature = "tls-rust-ring",
-        not(feature = "tls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");

tokio-xmpp/examples/echo_component.rs πŸ”—

@@ -6,27 +6,26 @@ use xmpp_parsers::jid::Jid;
 use xmpp_parsers::message::{Lang, Message, MessageType};
 use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
 
-#[cfg(all(
-    feature = "tls-rust",
-    any(feature = "tls-rust-aws_lc_rs", feature = "tls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_xmpp::rustls;
 use tokio_xmpp::{connect::DnsConfig, Component};
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() {
     env_logger::init();
 
-    #[cfg(all(feature = "tls-rust", feature = "tls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "tls-rust",
-        feature = "tls-rust-ring",
-        not(feature = "tls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");

tokio-xmpp/examples/echo_server.rs πŸ”—

@@ -1,10 +1,7 @@
 use futures::{SinkExt, StreamExt};
 use tokio::{self, io, net::TcpSocket};
 
-#[cfg(all(
-    feature = "tls-rust",
-    any(feature = "tls-rust-aws_lc_rs", feature = "tls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_xmpp::rustls;
 use tokio_xmpp::{
     minidom::Element,
@@ -12,18 +9,20 @@ use tokio_xmpp::{
     xmlstream::{accept_stream, StreamHeader, Timeouts},
 };
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() -> Result<(), io::Error> {
-    #[cfg(all(feature = "tls-rust", feature = "tls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "tls-rust",
-        feature = "tls-rust-ring",
-        not(feature = "tls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");

tokio-xmpp/examples/keep_connection.rs πŸ”—

@@ -19,10 +19,7 @@ use rand::{thread_rng, Rng};
 
 use futures::StreamExt;
 
-#[cfg(all(
-    feature = "tls-rust",
-    any(feature = "tls-rust-aws_lc_rs", feature = "tls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_xmpp::rustls;
 use tokio_xmpp::{
     connect::{DnsConfig, StartTlsServerConnector},
@@ -35,20 +32,22 @@ use tokio_xmpp::{
     xmlstream::Timeouts,
 };
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() {
     env_logger::init();
 
-    #[cfg(all(feature = "tls-rust", feature = "tls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "tls-rust",
-        feature = "tls-rust-ring",
-        not(feature = "tls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
@@ -93,8 +92,13 @@ async fn main() {
                 iq.to = Some(domain.clone());
                 stream.send(Box::new(iq.into())).await;
             }
-            ev = stream.next() => {
-                log::info!("{:?}", ev);
+            ev = stream.next() => match ev {
+                Some(ev) => {
+                    log::info!("{:?}", ev);
+                }
+                None => {
+                    panic!("stream terminated unexpectedly!");
+                }
             }
         }
     }

tokio-xmpp/src/connect/starttls.rs πŸ”—

@@ -2,18 +2,22 @@
 
 use alloc::borrow::Cow;
 use core::{error::Error as StdError, fmt};
-#[cfg(feature = "tls-native")]
+#[cfg(feature = "native-tls")]
 use native_tls::Error as TlsError;
 use std::io;
 use std::os::fd::AsRawFd;
-#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+#[cfg(feature = "rustls-any-backend")]
 use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
-#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+// Note: feature = "rustls-any-backend" and feature = "native-tls" are
+// mutually exclusive during normal compiles, but we allow it for rustdoc
+// builds. Thus, we have to make sure that the compilation still succeeds in
+// such a case.
+#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
 use tokio_rustls::rustls::Error as TlsError;
 
 use futures::{sink::SinkExt, stream::StreamExt};
 
-#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
 use {
     alloc::sync::Arc,
     tokio_rustls::{
@@ -24,16 +28,16 @@ use {
 };
 
 #[cfg(all(
-    feature = "tls-rust",
-    not(feature = "tls-native"),
-    not(feature = "tls-rust-ktls")
+    feature = "rustls-any-backend",
+    not(feature = "ktls"),
+    not(feature = "native-tls")
 ))]
 use tokio_rustls::client::TlsStream;
 
-#[cfg(all(feature = "tls-rust-ktls", not(feature = "tls-native")))]
+#[cfg(all(feature = "ktls", not(feature = "native-tls")))]
 type TlsStream<S> = ktls::KtlsStream<S>;
 
-#[cfg(feature = "tls-native")]
+#[cfg(feature = "native-tls")]
 use {
     native_tls::TlsConnector as NativeTlsConnector,
     tokio_native_tls::{TlsConnector, TlsStream},
@@ -123,7 +127,7 @@ impl ServerConnector for StartTlsServerConnector {
     }
 }
 
-#[cfg(feature = "tls-native")]
+#[cfg(feature = "native-tls")]
 async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
     xmpp_stream: XmppStream<BufStream<S>>,
     domain: &str,
@@ -140,7 +144,7 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
     Ok((tls_stream, ChannelBinding::None))
 }
 
-#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
 async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
     xmpp_stream: XmppStream<BufStream<S>>,
     domain: &str,
@@ -160,7 +164,7 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
     let mut config = ClientConfig::builder()
         .with_root_certificates(root_store)
         .with_no_client_auth();
-    #[cfg(feature = "tls-rust-ktls")]
+    #[cfg(feature = "ktls")]
     let stream = {
         config.enable_secret_extraction = true;
         ktls::CorkStream::new(stream)
@@ -184,7 +188,7 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
         _ => ChannelBinding::None,
     };
 
-    #[cfg(feature = "tls-rust-ktls")]
+    #[cfg(feature = "ktls")]
     let tls_stream = ktls::config_ktls_client(tls_stream)
         .await
         .map_err(StartTlsError::KtlsError)?;
@@ -228,10 +232,10 @@ pub async fn starttls<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
 pub enum StartTlsError {
     /// TLS error
     Tls(TlsError),
-    #[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+    #[cfg(feature = "rustls-any-backend")]
     /// DNS name parsing error
     DnsNameError(InvalidDnsNameError),
-    #[cfg(feature = "tls-rust-ktls")]
+    #[cfg(feature = "ktls")]
     /// Error while setting up kernel TLS
     KtlsError(ktls::Error),
 }
@@ -242,9 +246,9 @@ impl fmt::Display for StartTlsError {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         match self {
             Self::Tls(e) => write!(fmt, "TLS error: {}", e),
-            #[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+            #[cfg(feature = "rustls-any-backend")]
             Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
-            #[cfg(feature = "tls-rust-ktls")]
+            #[cfg(feature = "ktls")]
             Self::KtlsError(e) => write!(fmt, "Kernel TLS error: {}", e),
         }
     }
@@ -258,7 +262,7 @@ impl From<TlsError> for StartTlsError {
     }
 }
 
-#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
+#[cfg(feature = "rustls-any-backend")]
 impl From<InvalidDnsNameError> for StartTlsError {
     fn from(e: InvalidDnsNameError) -> Self {
         Self::DnsNameError(e)

tokio-xmpp/src/lib.rs πŸ”—

@@ -22,6 +22,50 @@
 //! - [ ] Websockets
 //! - [ ] BOSH
 //!
+//! # Cargo features
+//!
+//! ## TLS backends
+//!
+//! - `aws_lc_rs` (default) enables rustls with the `aws_lc_rs` backend.
+//! - `ring` enables rustls with the `ring` backend`.
+//! - `rustls-any-backend` enables rustls, but without enabling a backend. It
+//!   is the application's responsibility to ensure that a backend is enabled
+//!   and installed.
+//! - `ktls` enables the use of ktls.
+//!   **Important:** Currently, connections will fail if the `tls` kernel
+//!   module is not available. There is no fallback to non-ktls connections!
+//! - `native-tls` enables the system-native TLS library (commonly
+//!   libssl/OpenSSL).
+//!
+//! **Note:** It is not allowed to mix rustls-based TLS backends with
+//! `tls-native`. Attempting to do so will result in a compilation error.
+//!
+//! **Note:** The `ktls` feature requires at least one `rustls` backend to be
+//! enabled (`aws_lc_rs` or `ring`).
+//!
+//! **Note:** When enabling not exactly one rustls backend, it is the
+//! application's responsibility to make sure that a default crypto provider is
+//! installed in `rustls`. Otherwise, all TLS connections will fail.
+//!
+//! ## Certificate validation
+//!
+//! When using `native-tls`, the system's native certificate store is used.
+//! Otherwise, you need to pick one of the following to ensure that TLS
+//! connections will succeed:
+//!
+//! - `rustls-native-certs` (default): Uses [rustls-native-certs](https://crates.io/crates/rustls-native-certs).
+//! - `webpki-roots`: Uses [webpki-roots](https://crates.io/crates/webpki-roots).
+//!
+//! ## Other features
+//!
+//! - `starttls` (default): Enables support for `<starttls/>`. Required as per
+//!   RFC 6120.
+//! - `insecure-tcp`: Allow the use of insecure TCP connections to connect to
+//!   XMPP servers. Required for XMPP components, but disabled by default to
+//!   prevent accidental use.
+//! - `serde`: Enable the `serde` feature in `xmpp-parsers`.
+//! - `component`: Enable component support (implies `insecure-tcp`).
+//!
 //! # More information
 //!
 //! You can find more information on our website [xmpp.rs](https://xmpp.rs/) or by joining our chatroom [chat@xmpp.rs](xmpp:chat@xmpp.rs?join).
@@ -29,21 +73,35 @@
 #![deny(unsafe_code, missing_docs, bare_trait_objects)]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
 
-#[cfg(all(
-    not(xmpprs_doc_build),
-    not(doc),
-    feature = "tls-native",
-    feature = "tls-rust"
-))]
-compile_error!("Both tls-native and tls-rust features can't be enabled at the same time.");
+macro_rules! fail_native_with_any {
+    ($($feature:literal),+) => {
+        $(
+            #[cfg(all(
+                not(xmpprs_doc_build),
+                not(doc),
+                feature = "native-tls",
+                feature = $feature,
+            ))]
+            compile_error!(
+                concat!(
+                    "native-tls cannot be mixed with the ",
+                    $feature,
+                    " feature. Pick one or the other."
+                )
+            );
+        )+
+    }
+}
+
+fail_native_with_any!("ring", "aws_lc_rs", "ktls", "rustls-any-backend");
 
 #[cfg(all(
     feature = "starttls",
-    not(feature = "tls-native"),
-    not(feature = "tls-rust")
+    not(feature = "native-tls"),
+    not(any(feature = "rustls-any-backend"))
 ))]
 compile_error!(
-    "when starttls feature enabled one of tls-native and tls-rust features must be enabled."
+    "When the starttls feature is enabled, either native-tls or any of the rustls (aws_lc_rs, ring, or rustls-any-backend) features must be enabled."
 );
 
 extern crate alloc;
@@ -51,7 +109,7 @@ extern crate alloc;
 pub use parsers::{jid, minidom};
 pub use xmpp_parsers as parsers;
 
-#[cfg(feature = "tls-rust")]
+#[cfg(any(feature = "ring", feature = "aws_lc_rs", feature = "ktls"))]
 pub use tokio_rustls::rustls;
 
 mod client;

xmpp/Cargo.toml πŸ”—

@@ -31,11 +31,20 @@ name = "hello_bot"
 required-features = ["avatars"]
 
 [features]
-default = ["avatars", "starttls-rust"]
-starttls-native = ["tokio-xmpp/starttls", "tokio-xmpp/tls-native", "reqwest/native-tls"]
-starttls-rust = ["tokio-xmpp/starttls", "tokio-xmpp/tls-rust", "reqwest/rustls-tls-no-provider", "tokio-xmpp/tls-rust-webpki-roots"]
-starttls-rust-aws_lc_rs = ["tokio-xmpp/tls-rust-aws_lc_rs", "starttls-rust"]
-starttls-rust-ring = ["tokio-xmpp/tls-rust-ring", "starttls-rust"]
+default = ["avatars", "aws_lc_rs", "starttls", "rustls-native-certs"]
+
+aws_lc_rs = ["rustls-any-backend", "tokio-xmpp/aws_lc_rs", "reqwest/rustls-tls-no-provider"]
+ring = ["rustls-any-backend", "tokio-xmpp/ring", "reqwest/rustls-tls-no-provider"]
+native-tls = ["tokio-xmpp/native-tls", "reqwest/native-tls"]
+ktls = ["rustls-any-backend", "tokio-xmpp/ktls", "reqwest/rustls-tls"]
+
+rustls-any-backend = ["tokio-xmpp/rustls-any-backend"]
+
+rustls-native-certs = ["tokio-xmpp/rustls-native-certs"]
+webpki-roots = ["tokio-xmpp/webpki-roots"]
+
+starttls = ["tokio-xmpp/starttls"]
+
 avatars = []
 escape-hatch = []
 syntax-highlighting = [ "tokio-xmpp/syntax-highlighting" ]

xmpp/ChangeLog πŸ”—

@@ -15,13 +15,14 @@ XXXX-YY-ZZ [ RELEASER <admin@localhost> ]
       - Agent::send_room_private_message now takes RoomPrivateMessageSettings (!487)
       - Event now exposes Option<MessageId> for incoming messages, and MessageId
         for incoming message corrections; type alias Id has been removed (!504)
-      - The `starttls-rust` feature flag does not automatically enable a
-        `rustls` crypto provider anymore. This is to avoid conflict between
-        two crypto providers and to avoid linking unnecessary code. The
-        `aws_lc_rs` crypto provider is still built by default, however,
-        applications which use `xmpp` without default features will have to
-        adapt their feature flags to explicitly enable the
-        `starttls-rust-aws_lc_rs` or `starttls-rust-ring` features. (!581)
+      - The TLS-related feature flags have been completely reworked to make
+        them easier to use. The `starttls-*` feature flags have been removed
+        in favour of more concise flag names identifying the TLS backends
+        directly (`aws_lc_rs`, `ring`, `native-tls`). The `starttls` feature
+        is now independent of the specific backend (but a backend still needs
+        to be enabled for compilation to succeed).
+
+        Please refer to the crate docs for details. (!581)
     * Added:
       - Agent::send_room_message takes RoomMessageSettings argument (!483)
       - Agent::send_raw_message takes RawMessageSettings for any message type (!487)

xmpp/examples/hello_bot.rs πŸ”—

@@ -4,10 +4,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-#[cfg(all(
-    feature = "starttls-rust",
-    any(feature = "starttls-rust-aws_lc_rs", feature = "starttls-rust-ring")
-))]
+#[cfg(feature = "rustls-any-backend")]
 use xmpp::tokio_xmpp::rustls;
 use xmpp::{
     jid::BareJid,
@@ -20,20 +17,22 @@ use tokio::signal::ctrl_c;
 use std::env::args;
 use std::str::FromStr;
 
+#[cfg(all(
+    feature = "rustls-any-backend",
+    not(any(feature = "aws_lc_rs", feature = "ring"))
+))]
+compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
+
 #[tokio::main]
 async fn main() -> Result<(), Option<()>> {
     env_logger::init();
 
-    #[cfg(all(feature = "starttls-rust", feature = "starttls-rust-aws_lc_rs"))]
+    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
     rustls::crypto::aws_lc_rs::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");
 
-    #[cfg(all(
-        feature = "starttls-rust",
-        feature = "starttls-rust-ring",
-        not(feature = "starttls-rust-aws_lc_rs")
-    ))]
+    #[cfg(all(feature = "ring"))]
     rustls::crypto::ring::default_provider()
         .install_default()
         .expect("failed to install rustls crypto provider");

xmpp/src/builder.rs πŸ”—

@@ -4,7 +4,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-#[cfg(any(feature = "starttls-rust", feature = "starttls-native"))]
+#[cfg(feature = "starttls")]
 use crate::tokio_xmpp::connect::{DnsConfig, StartTlsServerConnector};
 use core::str::FromStr;
 
@@ -52,7 +52,7 @@ pub struct ClientBuilder<'a, C: ServerConnector> {
     timeouts: Timeouts,
 }
 
-#[cfg(any(feature = "starttls-rust", feature = "starttls-native"))]
+#[cfg(feature = "starttls")]
 impl ClientBuilder<'_, StartTlsServerConnector> {
     pub fn new<'a>(jid: BareJid, password: &'a str) -> ClientBuilder<'a, StartTlsServerConnector> {
         Self::new_with_connector(

xmpp/src/lib.rs πŸ”—

@@ -3,6 +3,46 @@
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//! # Cargo features
+//!
+//! ## TLS backends
+//!
+//! - `aws_lc_rs` (default) enables rustls with the `aws_lc_rs` backend.
+//! - `ring` enables rustls with the `ring` backend`.
+//! - `rustls-any-backend` enables rustls, but without enabling a backend. It
+//!   is the application's responsibility to ensure that a backend is enabled
+//!   and installed.
+//! - `ktls` enables the use of ktls.
+//!   **Important:** Currently, connections will fail if the `tls` kernel
+//!   module is not available. There is no fallback to non-ktls connections!
+//! - `native-tls` enables the system-native TLS library (commonly
+//!   libssl/OpenSSL).
+//!
+//! **Note:** It is not allowed to mix rustls-based TLS backends with
+//! `tls-native`. Attempting to do so will result in a compilation error.
+//!
+//! **Note:** The `ktls` feature requires at least one `rustls` backend to be
+//! enabled (`aws_lc_rs` or `ring`).
+//!
+//! **Note:** When enabling not exactly one rustls backend, it is the
+//! application's responsibility to make sure that a default crypto provider is
+//! installed in `rustls`. Otherwise, all TLS connections will fail.
+//!
+//! ## Certificate validation
+//!
+//! When using `native-tls`, the system's native certificate store is used.
+//! Otherwise, you need to pick one of the following to ensure that TLS
+//! connections will succeed:
+//!
+//! - `rustls-native-certs` (default): Uses [rustls-native-certs](https://crates.io/crates/rustls-native-certs).
+//! - `webpki-roots`: Uses [webpki-roots](https://crates.io/crates/webpki-roots).
+//!
+//! ## Other features
+//!
+//! - `starttls` (default): Enables support for `<starttls/>`. Required as per
+//!   RFC 6120.
+//! - `avatars` (default): Enables support for avatars.
+//! - `serde`: Enable the `serde` feature in `tokio-xmpp`.
 
 #![deny(bare_trait_objects)]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]