tokio-xmpp: remove code duplicates for direct tls and starttls connectors
This commit is contained in:
parent
c674c14597
commit
e3b167687f
5 changed files with 189 additions and 297 deletions
|
|
@ -35,7 +35,7 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
|
||||||
|
|
||||||
Please refer to the crate docs for details. (!581)
|
Please refer to the crate docs for details. (!581)
|
||||||
* Added:
|
* Added:
|
||||||
- Add new directTLS connection method to the `Client`. (Placeholder for PR number)
|
- Add new `direct-tls` connection method to the `Client`. (!585)
|
||||||
- Support for sending IQ requests while tracking their responses in a
|
- Support for sending IQ requests while tracking their responses in a
|
||||||
Future.
|
Future.
|
||||||
- `rustls` is now re-exported if it is enabled, to allow applications to
|
- `rustls` is now re-exported if it is enabled, to allow applications to
|
||||||
|
|
|
||||||
|
|
@ -7,50 +7,15 @@
|
||||||
//! `direct_tls::ServerConfig` provides a `ServerConnector` for direct TLS connections
|
//! `direct_tls::ServerConfig` provides a `ServerConnector` for direct TLS connections
|
||||||
|
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use core::{error::Error as StdError, fmt};
|
|
||||||
#[cfg(feature = "native-tls")]
|
|
||||||
use native_tls::Error as TlsError;
|
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
|
|
||||||
use {
|
|
||||||
alloc::sync::Arc,
|
|
||||||
tokio_rustls::{
|
|
||||||
rustls::pki_types::ServerName,
|
|
||||||
rustls::{ClientConfig, RootCertStore},
|
|
||||||
TlsConnector,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(
|
|
||||||
feature = "rustls-any-backend",
|
|
||||||
not(feature = "ktls"),
|
|
||||||
not(feature = "native-tls")
|
|
||||||
))]
|
|
||||||
use tokio_rustls::client::TlsStream;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "ktls", not(feature = "native-tls")))]
|
|
||||||
type TlsStream<S> = ktls::KtlsStream<S>;
|
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
|
||||||
use {
|
|
||||||
native_tls::TlsConnector as NativeTlsConnector,
|
|
||||||
tokio_native_tls::{TlsConnector, TlsStream},
|
|
||||||
};
|
|
||||||
|
|
||||||
use sasl::common::ChannelBinding;
|
use sasl::common::ChannelBinding;
|
||||||
use tokio::{io::BufStream, net::TcpStream};
|
use tokio::{io::BufStream, net::TcpStream};
|
||||||
use xmpp_parsers::jid::Jid;
|
use xmpp_parsers::jid::Jid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connect::{DnsConfig, ServerConnector, ServerConnectorError},
|
connect::{
|
||||||
|
tls_common::{establish_tls_connection, TlsConnectorError, TlsStream},
|
||||||
|
DnsConfig, ServerConnector,
|
||||||
|
},
|
||||||
error::Error,
|
error::Error,
|
||||||
xmlstream::{initiate_stream, PendingFeaturesRecv, StreamHeader, Timeouts},
|
xmlstream::{initiate_stream, PendingFeaturesRecv, StreamHeader, Timeouts},
|
||||||
};
|
};
|
||||||
|
|
@ -78,7 +43,7 @@ impl ServerConnector for DirectTlsServerConnector {
|
||||||
|
|
||||||
// Immediately establish TLS connection
|
// Immediately establish TLS connection
|
||||||
let (tls_stream, channel_binding) =
|
let (tls_stream, channel_binding) =
|
||||||
establish_tls(tcp_stream, jid.domain().as_str()).await?;
|
establish_tls_connection(tcp_stream, jid.domain().as_str()).await?;
|
||||||
|
|
||||||
// Establish XMPP stream over TLS
|
// Establish XMPP stream over TLS
|
||||||
Ok((
|
Ok((
|
||||||
|
|
@ -100,110 +65,5 @@ impl ServerConnector for DirectTlsServerConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
/// Direct TLS ServerConnector Error - now just an alias to the common error type
|
||||||
async fn establish_tls(
|
pub type DirectTlsError = TlsConnectorError;
|
||||||
tcp_stream: TcpStream,
|
|
||||||
domain: &str,
|
|
||||||
) -> Result<(TlsStream<TcpStream>, ChannelBinding), Error> {
|
|
||||||
let domain = domain.to_owned();
|
|
||||||
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
|
|
||||||
.connect(&domain, tcp_stream)
|
|
||||||
.await
|
|
||||||
.map_err(|e| DirectTlsError::Tls(e))?;
|
|
||||||
log::warn!(
|
|
||||||
"tls-native doesn't support channel binding, please use tls-rust if you want this feature!"
|
|
||||||
);
|
|
||||||
Ok((tls_stream, ChannelBinding::None))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
|
|
||||||
async fn establish_tls(
|
|
||||||
tcp_stream: TcpStream,
|
|
||||||
domain: &str,
|
|
||||||
) -> Result<(TlsStream<TcpStream>, ChannelBinding), Error> {
|
|
||||||
let domain = ServerName::try_from(domain.to_owned()).map_err(DirectTlsError::DnsNameError)?;
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
#[cfg(feature = "webpki-roots")]
|
|
||||||
{
|
|
||||||
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
|
||||||
}
|
|
||||||
#[cfg(feature = "rustls-native-certs")]
|
|
||||||
{
|
|
||||||
root_store.add_parsable_certificates(rustls_native_certs::load_native_certs()?);
|
|
||||||
}
|
|
||||||
#[allow(unused_mut, reason = "This config is mutable when using ktls")]
|
|
||||||
let mut config = ClientConfig::builder()
|
|
||||||
.with_root_certificates(root_store)
|
|
||||||
.with_no_client_auth();
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
let tcp_stream = {
|
|
||||||
config.enable_secret_extraction = true;
|
|
||||||
ktls::CorkStream::new(tcp_stream)
|
|
||||||
};
|
|
||||||
let tls_stream = TlsConnector::from(Arc::new(config))
|
|
||||||
.connect(domain, tcp_stream)
|
|
||||||
.await
|
|
||||||
.map_err(crate::Error::Io)?;
|
|
||||||
|
|
||||||
// Extract the channel-binding information before we hand the stream over to ktls.
|
|
||||||
let (_, connection) = tls_stream.get_ref();
|
|
||||||
let channel_binding = match connection.protocol_version() {
|
|
||||||
// TODO: Add support for TLS 1.2 and earlier.
|
|
||||||
Some(tokio_rustls::rustls::ProtocolVersion::TLSv1_3) => {
|
|
||||||
let data = vec![0u8; 32];
|
|
||||||
let data = connection
|
|
||||||
.export_keying_material(data, b"EXPORTER-Channel-Binding", None)
|
|
||||||
.map_err(DirectTlsError::Tls)?;
|
|
||||||
ChannelBinding::TlsExporter(data)
|
|
||||||
}
|
|
||||||
_ => ChannelBinding::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
let tls_stream = ktls::config_ktls_client(tls_stream)
|
|
||||||
.await
|
|
||||||
.map_err(DirectTlsError::KtlsError)?;
|
|
||||||
Ok((tls_stream, channel_binding))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Direct TLS ServerConnector Error
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum DirectTlsError {
|
|
||||||
/// TLS error
|
|
||||||
Tls(TlsError),
|
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
/// DNS name parsing error
|
|
||||||
DnsNameError(InvalidDnsNameError),
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
/// Error while setting up kernel TLS
|
|
||||||
KtlsError(ktls::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerConnectorError for DirectTlsError {}
|
|
||||||
|
|
||||||
impl fmt::Display for DirectTlsError {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Tls(e) => write!(fmt, "TLS error: {}", e),
|
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
Self::KtlsError(e) => write!(fmt, "Kernel TLS error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for DirectTlsError {}
|
|
||||||
|
|
||||||
impl From<TlsError> for DirectTlsError {
|
|
||||||
fn from(e: TlsError) -> Self {
|
|
||||||
Self::Tls(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
impl From<InvalidDnsNameError> for DirectTlsError {
|
|
||||||
fn from(e: InvalidDnsNameError) -> Self {
|
|
||||||
Self::DnsNameError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ pub mod tcp;
|
||||||
#[cfg(feature = "insecure-tcp")]
|
#[cfg(feature = "insecure-tcp")]
|
||||||
pub use tcp::TcpServerConnector;
|
pub use tcp::TcpServerConnector;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "direct-tls", feature = "starttls"))]
|
||||||
|
pub mod tls_common;
|
||||||
|
|
||||||
mod dns;
|
mod dns;
|
||||||
pub use dns::DnsConfig;
|
pub use dns::DnsConfig;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,10 @@
|
||||||
//! `starttls::ServerConfig` provides a `ServerConnector` for starttls connections
|
//! `starttls::ServerConfig` provides a `ServerConnector` for starttls connections
|
||||||
|
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use core::{error::Error as StdError, fmt};
|
|
||||||
#[cfg(feature = "native-tls")]
|
|
||||||
use native_tls::Error as TlsError;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
|
|
||||||
// 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};
|
use futures::{sink::SinkExt, stream::StreamExt};
|
||||||
|
|
||||||
#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
|
|
||||||
use {
|
|
||||||
alloc::sync::Arc,
|
|
||||||
tokio_rustls::{
|
|
||||||
rustls::pki_types::ServerName,
|
|
||||||
rustls::{ClientConfig, RootCertStore},
|
|
||||||
TlsConnector,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(
|
|
||||||
feature = "rustls-any-backend",
|
|
||||||
not(feature = "ktls"),
|
|
||||||
not(feature = "native-tls")
|
|
||||||
))]
|
|
||||||
use tokio_rustls::client::TlsStream;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "ktls", not(feature = "native-tls")))]
|
|
||||||
type TlsStream<S> = ktls::KtlsStream<S>;
|
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
|
||||||
use {
|
|
||||||
native_tls::TlsConnector as NativeTlsConnector,
|
|
||||||
tokio_native_tls::{TlsConnector, TlsStream},
|
|
||||||
};
|
|
||||||
|
|
||||||
use sasl::common::ChannelBinding;
|
use sasl::common::ChannelBinding;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncRead, AsyncWrite, BufStream},
|
io::{AsyncRead, AsyncWrite, BufStream},
|
||||||
|
|
@ -54,7 +16,10 @@ use xmpp_parsers::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connect::{DnsConfig, ServerConnector, ServerConnectorError},
|
connect::{
|
||||||
|
tls_common::{establish_tls_connection, TlsConnectorError, TlsStream},
|
||||||
|
DnsConfig, ServerConnector,
|
||||||
|
},
|
||||||
error::{Error, ProtocolError},
|
error::{Error, ProtocolError},
|
||||||
xmlstream::{
|
xmlstream::{
|
||||||
initiate_stream, PendingFeaturesRecv, ReadError, StreamHeader, Timeouts, XmppStream,
|
initiate_stream, PendingFeaturesRecv, ReadError, StreamHeader, Timeouts, XmppStream,
|
||||||
|
|
@ -127,74 +92,6 @@ impl ServerConnector for StartTlsServerConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
|
||||||
async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
|
|
||||||
xmpp_stream: XmppStream<BufStream<S>>,
|
|
||||||
domain: &str,
|
|
||||||
) -> Result<(TlsStream<S>, ChannelBinding), Error> {
|
|
||||||
let domain = domain.to_owned();
|
|
||||||
let stream = xmpp_stream.into_inner().into_inner();
|
|
||||||
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
|
|
||||||
.connect(&domain, stream)
|
|
||||||
.await
|
|
||||||
.map_err(|e| StartTlsError::Tls(e))?;
|
|
||||||
log::warn!(
|
|
||||||
"tls-native doesn’t support channel binding, please use tls-rust if you want this feature!"
|
|
||||||
);
|
|
||||||
Ok((tls_stream, ChannelBinding::None))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,
|
|
||||||
) -> Result<(TlsStream<S>, ChannelBinding), Error> {
|
|
||||||
let domain = ServerName::try_from(domain.to_owned()).map_err(StartTlsError::DnsNameError)?;
|
|
||||||
let stream = xmpp_stream.into_inner().into_inner();
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
#[cfg(feature = "webpki-roots")]
|
|
||||||
{
|
|
||||||
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
|
||||||
}
|
|
||||||
#[cfg(feature = "rustls-native-certs")]
|
|
||||||
{
|
|
||||||
root_store.add_parsable_certificates(rustls_native_certs::load_native_certs()?);
|
|
||||||
}
|
|
||||||
#[allow(unused_mut, reason = "This config is mutable when using ktls")]
|
|
||||||
let mut config = ClientConfig::builder()
|
|
||||||
.with_root_certificates(root_store)
|
|
||||||
.with_no_client_auth();
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
let stream = {
|
|
||||||
config.enable_secret_extraction = true;
|
|
||||||
ktls::CorkStream::new(stream)
|
|
||||||
};
|
|
||||||
let tls_stream = TlsConnector::from(Arc::new(config))
|
|
||||||
.connect(domain, stream)
|
|
||||||
.await
|
|
||||||
.map_err(crate::Error::Io)?;
|
|
||||||
|
|
||||||
// Extract the channel-binding information before we hand the stream over to ktls.
|
|
||||||
let (_, connection) = tls_stream.get_ref();
|
|
||||||
let channel_binding = match connection.protocol_version() {
|
|
||||||
// TODO: Add support for TLS 1.2 and earlier.
|
|
||||||
Some(tokio_rustls::rustls::ProtocolVersion::TLSv1_3) => {
|
|
||||||
let data = vec![0u8; 32];
|
|
||||||
let data = connection
|
|
||||||
.export_keying_material(data, b"EXPORTER-Channel-Binding", None)
|
|
||||||
.map_err(StartTlsError::Tls)?;
|
|
||||||
ChannelBinding::TlsExporter(data)
|
|
||||||
}
|
|
||||||
_ => ChannelBinding::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
let tls_stream = ktls::config_ktls_client(tls_stream)
|
|
||||||
.await
|
|
||||||
.map_err(StartTlsError::KtlsError)?;
|
|
||||||
Ok((tls_stream, channel_binding))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs `<starttls/>` on an XmppStream and returns a binary
|
/// Performs `<starttls/>` on an XmppStream and returns a binary
|
||||||
/// TlsStream.
|
/// TlsStream.
|
||||||
pub async fn starttls<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
|
pub async fn starttls<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
|
||||||
|
|
@ -224,47 +121,9 @@ pub async fn starttls<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_tls_stream(stream, domain).await
|
let inner_stream = stream.into_inner().into_inner();
|
||||||
|
establish_tls_connection(inner_stream, domain).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// StartTLS ServerConnector Error
|
/// StartTLS ServerConnector Error - now just an alias to the common error type
|
||||||
#[derive(Debug)]
|
pub type StartTlsError = TlsConnectorError;
|
||||||
pub enum StartTlsError {
|
|
||||||
/// TLS error
|
|
||||||
Tls(TlsError),
|
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
/// DNS name parsing error
|
|
||||||
DnsNameError(InvalidDnsNameError),
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
/// Error while setting up kernel TLS
|
|
||||||
KtlsError(ktls::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerConnectorError for StartTlsError {}
|
|
||||||
|
|
||||||
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(feature = "rustls-any-backend")]
|
|
||||||
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
|
|
||||||
#[cfg(feature = "ktls")]
|
|
||||||
Self::KtlsError(e) => write!(fmt, "Kernel TLS error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for StartTlsError {}
|
|
||||||
|
|
||||||
impl From<TlsError> for StartTlsError {
|
|
||||||
fn from(e: TlsError) -> Self {
|
|
||||||
Self::Tls(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rustls-any-backend")]
|
|
||||||
impl From<InvalidDnsNameError> for StartTlsError {
|
|
||||||
fn from(e: InvalidDnsNameError) -> Self {
|
|
||||||
Self::DnsNameError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
170
tokio-xmpp/src/connect/tls_common.rs
Normal file
170
tokio-xmpp/src/connect/tls_common.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright (c) 2025 Saarko <saarko@tutanota.com>
|
||||||
|
//
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
//! Common TLS functionality shared between direct_tls and starttls modules
|
||||||
|
|
||||||
|
use core::{error::Error as StdError, fmt};
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
#[cfg(feature = "native-tls")]
|
||||||
|
use native_tls::Error as TlsError;
|
||||||
|
#[cfg(feature = "rustls-any-backend")]
|
||||||
|
use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
|
||||||
|
#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
|
||||||
|
use tokio_rustls::rustls::Error as TlsError;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
|
||||||
|
use {
|
||||||
|
alloc::sync::Arc,
|
||||||
|
tokio_rustls::{
|
||||||
|
rustls::pki_types::ServerName,
|
||||||
|
rustls::{ClientConfig, RootCertStore},
|
||||||
|
TlsConnector,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "rustls-any-backend",
|
||||||
|
not(feature = "ktls"),
|
||||||
|
not(feature = "native-tls")
|
||||||
|
))]
|
||||||
|
pub use tokio_rustls::client::TlsStream;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ktls", not(feature = "native-tls")))]
|
||||||
|
pub type TlsStream<S> = ktls::KtlsStream<S>;
|
||||||
|
|
||||||
|
#[cfg(feature = "native-tls")]
|
||||||
|
pub use tokio_native_tls::TlsStream;
|
||||||
|
|
||||||
|
#[cfg(feature = "native-tls")]
|
||||||
|
use {native_tls::TlsConnector as NativeTlsConnector, tokio_native_tls::TlsConnector};
|
||||||
|
|
||||||
|
use crate::{connect::ServerConnectorError, error::Error};
|
||||||
|
use sasl::common::ChannelBinding;
|
||||||
|
|
||||||
|
/// Common TLS error type used by both direct_tls and starttls
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TlsConnectorError {
|
||||||
|
/// TLS error
|
||||||
|
Tls(TlsError),
|
||||||
|
#[cfg(feature = "rustls-any-backend")]
|
||||||
|
/// DNS name parsing error
|
||||||
|
DnsNameError(InvalidDnsNameError),
|
||||||
|
#[cfg(feature = "ktls")]
|
||||||
|
/// Error while setting up kernel TLS
|
||||||
|
KtlsError(ktls::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerConnectorError for TlsConnectorError {}
|
||||||
|
|
||||||
|
impl fmt::Display for TlsConnectorError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Tls(e) => write!(fmt, "TLS error: {}", e),
|
||||||
|
#[cfg(feature = "rustls-any-backend")]
|
||||||
|
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
|
||||||
|
#[cfg(feature = "ktls")]
|
||||||
|
Self::KtlsError(e) => write!(fmt, "Kernel TLS error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdError for TlsConnectorError {}
|
||||||
|
|
||||||
|
impl From<TlsError> for TlsConnectorError {
|
||||||
|
fn from(e: TlsError) -> Self {
|
||||||
|
Self::Tls(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-any-backend")]
|
||||||
|
impl From<InvalidDnsNameError> for TlsConnectorError {
|
||||||
|
fn from(e: InvalidDnsNameError) -> Self {
|
||||||
|
Self::DnsNameError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establish TLS connection using native-tls
|
||||||
|
#[cfg(feature = "native-tls")]
|
||||||
|
pub async fn establish_tls_connection<S>(
|
||||||
|
stream: S,
|
||||||
|
domain: &str,
|
||||||
|
) -> Result<(TlsStream<S>, ChannelBinding), Error>
|
||||||
|
where
|
||||||
|
S: AsyncRead + AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
let domain = domain.to_owned();
|
||||||
|
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
|
||||||
|
.connect(&domain, stream)
|
||||||
|
.await
|
||||||
|
.map_err(|e| TlsConnectorError::Tls(e))?;
|
||||||
|
log::warn!(
|
||||||
|
"tls-native doesn't support channel binding, please use tls-rust if you want this feature!"
|
||||||
|
);
|
||||||
|
Ok((tls_stream, ChannelBinding::None))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establish TLS connection using rustls
|
||||||
|
#[cfg(all(feature = "rustls-any-backend", not(feature = "native-tls")))]
|
||||||
|
pub async fn establish_tls_connection<S>(
|
||||||
|
stream: S,
|
||||||
|
domain: &str,
|
||||||
|
) -> Result<(TlsStream<S>, ChannelBinding), Error>
|
||||||
|
where
|
||||||
|
S: AsyncRead + AsyncWrite + Unpin + AsRawFd,
|
||||||
|
{
|
||||||
|
let domain =
|
||||||
|
ServerName::try_from(domain.to_owned()).map_err(TlsConnectorError::DnsNameError)?;
|
||||||
|
let mut root_store = RootCertStore::empty();
|
||||||
|
|
||||||
|
#[cfg(feature = "webpki-roots")]
|
||||||
|
{
|
||||||
|
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-native-certs")]
|
||||||
|
{
|
||||||
|
root_store.add_parsable_certificates(rustls_native_certs::load_native_certs()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_mut, reason = "This config is mutable when using ktls")]
|
||||||
|
let mut config = ClientConfig::builder()
|
||||||
|
.with_root_certificates(root_store)
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
#[cfg(feature = "ktls")]
|
||||||
|
let stream = {
|
||||||
|
config.enable_secret_extraction = true;
|
||||||
|
ktls::CorkStream::new(stream)
|
||||||
|
};
|
||||||
|
|
||||||
|
let tls_stream = TlsConnector::from(Arc::new(config))
|
||||||
|
.connect(domain, stream)
|
||||||
|
.await
|
||||||
|
.map_err(crate::Error::Io)?;
|
||||||
|
|
||||||
|
// Extract the channel-binding information before we hand the stream over to ktls.
|
||||||
|
let (_, connection) = tls_stream.get_ref();
|
||||||
|
let channel_binding = match connection.protocol_version() {
|
||||||
|
// TODO: Add support for TLS 1.2 and earlier.
|
||||||
|
Some(tokio_rustls::rustls::ProtocolVersion::TLSv1_3) => {
|
||||||
|
let data = vec![0u8; 32];
|
||||||
|
let data = connection
|
||||||
|
.export_keying_material(data, b"EXPORTER-Channel-Binding", None)
|
||||||
|
.map_err(TlsConnectorError::Tls)?;
|
||||||
|
ChannelBinding::TlsExporter(data)
|
||||||
|
}
|
||||||
|
_ => ChannelBinding::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "ktls")]
|
||||||
|
let tls_stream = ktls::config_ktls_client(tls_stream)
|
||||||
|
.await
|
||||||
|
.map_err(TlsConnectorError::KtlsError)?;
|
||||||
|
|
||||||
|
Ok((tls_stream, channel_binding))
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue