Only expose one error type in crate root

This commit is contained in:
xmppftw xmppftw 2024-08-04 17:32:12 +02:00
commit 84511b54a0
6 changed files with 40 additions and 55 deletions

View file

@ -7,6 +7,10 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
which was not used and has been completely removed (!418)
- `ProtocolError` and `AuthError` are no longer exported in crate root;
access them from `error` module (!423)
- `connect::ServerConnector` no longer has `Error` associated type, the methods return tokio_xmpp::Error directly,
where Connection variant contains any error type that implements connect::ServerConnectorError (!421)
- `starttls::Error` no longer has `TokioXMPP` variant ; only tokio_xmpp::Error can contain starttls::Error,
not the other way around (!421)
Version 4.0.0:
2024-07-26 Maxime “pep” Buquet <pep@bouah.net>

View file

@ -5,6 +5,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
use xmpp_parsers::jid::Jid;
use crate::xmpp_stream::XMPPStream;
use crate::Error;
/// trait returned wrapped in XMPPStream by ServerConnector
pub trait AsyncReadAndWrite: AsyncRead + AsyncWrite + Unpin + Send {}
@ -17,19 +18,17 @@ pub trait ServerConnectorError: std::error::Error + Sync + Send {}
pub trait ServerConnector: Clone + core::fmt::Debug + Send + Unpin + 'static {
/// The type of Stream this ServerConnector produces
type Stream: AsyncReadAndWrite;
/// Error type to return
type Error: ServerConnectorError;
/// This must return the connection ready to login, ie if starttls is involved, after TLS has been started, and then after the <stream headers are exchanged
fn connect(
&self,
jid: &Jid,
ns: &str,
) -> impl std::future::Future<Output = Result<XMPPStream<Self::Stream>, Self::Error>> + Send;
) -> impl std::future::Future<Output = Result<XMPPStream<Self::Stream>, Error>> + Send;
/// Return channel binding data if available
/// do not fail if channel binding is simply unavailable, just return Ok(None)
/// this should only be called after the TLS handshake is finished
fn channel_binding(_stream: &Self::Stream) -> Result<ChannelBinding, Self::Error> {
fn channel_binding(_stream: &Self::Stream) -> Result<ChannelBinding, Error> {
Ok(ChannelBinding::None)
}
}

View file

@ -10,6 +10,8 @@ use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
use tokio_rustls::rustls::Error as TlsError;
use super::ServerConnectorError;
/// StartTLS ServerConnector Error
#[derive(Debug)]
pub enum Error {
@ -25,41 +27,34 @@ pub enum Error {
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
/// DNS name parsing error
DnsNameError(InvalidDnsNameError),
/// tokio-xmpp error
TokioXMPP(crate::error::Error),
}
impl ServerConnectorError for Error {}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Dns(e) => write!(fmt, "{:?}", e),
Error::Resolve(e) => write!(fmt, "{:?}", e),
Error::Idna => write!(fmt, "IDNA error"),
Error::Tls(e) => write!(fmt, "TLS error: {}", e),
Self::Dns(e) => write!(fmt, "{:?}", e),
Self::Resolve(e) => write!(fmt, "{:?}", e),
Self::Idna => write!(fmt, "IDNA error"),
Self::Tls(e) => write!(fmt, "TLS error: {}", e),
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
Error::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
Error::TokioXMPP(e) => write!(fmt, "TokioXMPP error: {}", e),
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
}
}
}
impl StdError for Error {}
impl From<crate::error::Error> for Error {
fn from(e: crate::error::Error) -> Self {
Error::TokioXMPP(e)
}
}
impl From<TlsError> for Error {
fn from(e: TlsError) -> Self {
Error::Tls(e)
Self::Tls(e)
}
}
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
impl From<InvalidDnsNameError> for Error {
fn from(e: InvalidDnsNameError) -> Self {
Error::DnsNameError(e)
Self::DnsNameError(e)
}
}

View file

@ -1,4 +1,5 @@
use super::error::Error;
use super::error::Error as StartTlsError;
use crate::Error;
use futures::{future::select_ok, FutureExt};
use hickory_resolver::{
config::LookupIpStrategy, name_server::TokioConnectionProvider, IntoName, TokioAsyncResolver,
@ -8,23 +9,21 @@ use std::net::SocketAddr;
use tokio::net::TcpStream;
pub async fn connect_to_host(domain: &str, port: u16) -> Result<TcpStream, Error> {
let ascii_domain = idna::domain_to_ascii(&domain).map_err(|_| Error::Idna)?;
let ascii_domain = idna::domain_to_ascii(&domain).map_err(|_| StartTlsError::Idna)?;
if let Ok(ip) = ascii_domain.parse() {
return Ok(TcpStream::connect(&SocketAddr::new(ip, port))
.await
.map_err(|e| Error::from(crate::Error::Io(e)))?);
return Ok(TcpStream::connect(&SocketAddr::new(ip, port)).await?);
}
let (config, mut options) =
hickory_resolver::system_conf::read_system_conf().map_err(Error::Resolve)?;
hickory_resolver::system_conf::read_system_conf().map_err(StartTlsError::Resolve)?;
options.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
let resolver = TokioAsyncResolver::new(config, options, TokioConnectionProvider::default());
let ips = resolver
.lookup_ip(ascii_domain)
.await
.map_err(Error::Resolve)?;
.map_err(StartTlsError::Resolve)?;
// Happy Eyeballs: connect to all records in parallel, return the
// first to succeed
select_ok(
@ -33,7 +32,7 @@ pub async fn connect_to_host(domain: &str, port: u16) -> Result<TcpStream, Error
)
.await
.map(|(result, _)| result)
.map_err(|_| crate::Error::Disconnected.into())
.map_err(|_| crate::Error::Disconnected)
}
pub async fn connect_with_srv(
@ -41,20 +40,18 @@ pub async fn connect_with_srv(
srv: &str,
fallback_port: u16,
) -> Result<TcpStream, Error> {
let ascii_domain = idna::domain_to_ascii(&domain).map_err(|_| Error::Idna)?;
let ascii_domain = idna::domain_to_ascii(&domain).map_err(|_| StartTlsError::Idna)?;
if let Ok(ip) = ascii_domain.parse() {
debug!("Attempting connection to {ip}:{fallback_port}");
return Ok(TcpStream::connect(&SocketAddr::new(ip, fallback_port))
.await
.map_err(|e| Error::from(crate::Error::Io(e)))?);
return Ok(TcpStream::connect(&SocketAddr::new(ip, fallback_port)).await?);
}
let resolver = TokioAsyncResolver::tokio_from_system_conf().map_err(Error::Resolve)?;
let resolver = TokioAsyncResolver::tokio_from_system_conf().map_err(StartTlsError::Resolve)?;
let srv_domain = format!("{}.{}.", srv, ascii_domain)
.into_name()
.map_err(Error::Dns)?;
.map_err(StartTlsError::Dns)?;
let srv_records = resolver.srv_lookup(srv_domain.clone()).await.ok();
match srv_records {

View file

@ -28,10 +28,11 @@ use tokio::{
use xmpp_parsers::{jid::Jid, ns};
use crate::error::ProtocolError;
use crate::Error;
use crate::{connect::ServerConnector, xmpp_codec::Packet, AsyncClient, SimpleClient};
use crate::{connect::ServerConnectorError, xmpp_stream::XMPPStream};
use self::error::Error;
use self::error::Error as StartTlsError;
use self::happy_eyeballs::{connect_to_host, connect_with_srv};
mod client;
@ -58,11 +59,8 @@ pub enum ServerConfig {
},
}
impl ServerConnectorError for Error {}
impl ServerConnector for ServerConfig {
type Stream = TlsStream<TcpStream>;
type Error = Error;
async fn connect(&self, jid: &Jid, ns: &str) -> Result<XMPPStream<Self::Stream>, Error> {
// TCP connection
let tcp_stream = match self {
@ -100,11 +98,9 @@ impl ServerConnector for ServerConfig {
// 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,
)?;
let data = connection
.export_keying_material(data, b"EXPORTER-Channel-Binding", None)
.map_err(|e| StartTlsError::Tls(e))?;
ChannelBinding::TlsExporter(data)
}
_ => ChannelBinding::None,
@ -121,7 +117,8 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
let stream = xmpp_stream.into_inner();
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
.connect(&domain, stream)
.await?;
.await
.map_err(|e| StartTlsError::Tls(e))?;
Ok(tls_stream)
}
@ -130,7 +127,7 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
xmpp_stream: XMPPStream<S>,
) -> Result<TlsStream<S>, Error> {
let domain = xmpp_stream.jid.domain().to_string();
let domain = ServerName::try_from(domain)?;
let domain = ServerName::try_from(domain).map_err(|e| StartTlsError::DnsNameError(e))?;
let stream = xmpp_stream.into_inner();
let root_store = RootCertStore {
roots: webpki_roots::TLS_SERVER_ROOTS.into(),

View file

@ -4,13 +4,9 @@ use std::sync::Arc;
use tokio::net::TcpStream;
use crate::{
connect::{ServerConnector, ServerConnectorError},
xmpp_stream::XMPPStream,
Component,
};
use crate::{connect::ServerConnector, xmpp_stream::XMPPStream, Component};
use self::error::Error;
use crate::Error;
mod component;
pub mod error;
@ -31,16 +27,13 @@ impl TcpServerConnector {
}
}
impl ServerConnectorError for Error {}
impl ServerConnector for TcpServerConnector {
type Stream = TcpStream;
type Error = Error;
async fn connect(
&self,
jid: &xmpp_parsers::jid::Jid,
ns: &str,
) -> Result<XMPPStream<Self::Stream>, Self::Error> {
) -> Result<XMPPStream<Self::Stream>, Error> {
let stream = TcpStream::connect(&*self.0)
.await
.map_err(|e| crate::Error::Io(e))?;