Only expose one error type in crate root
This commit is contained in:
parent
34796c90d4
commit
84511b54a0
6 changed files with 40 additions and 55 deletions
|
|
@ -7,6 +7,10 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
|
||||||
which was not used and has been completely removed (!418)
|
which was not used and has been completely removed (!418)
|
||||||
- `ProtocolError` and `AuthError` are no longer exported in crate root;
|
- `ProtocolError` and `AuthError` are no longer exported in crate root;
|
||||||
access them from `error` module (!423)
|
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:
|
Version 4.0.0:
|
||||||
2024-07-26 Maxime “pep” Buquet <pep@bouah.net>
|
2024-07-26 Maxime “pep” Buquet <pep@bouah.net>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
use xmpp_parsers::jid::Jid;
|
use xmpp_parsers::jid::Jid;
|
||||||
|
|
||||||
use crate::xmpp_stream::XMPPStream;
|
use crate::xmpp_stream::XMPPStream;
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
/// trait returned wrapped in XMPPStream by ServerConnector
|
/// trait returned wrapped in XMPPStream by ServerConnector
|
||||||
pub trait AsyncReadAndWrite: AsyncRead + AsyncWrite + Unpin + Send {}
|
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 {
|
pub trait ServerConnector: Clone + core::fmt::Debug + Send + Unpin + 'static {
|
||||||
/// The type of Stream this ServerConnector produces
|
/// The type of Stream this ServerConnector produces
|
||||||
type Stream: AsyncReadAndWrite;
|
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
|
/// 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(
|
fn connect(
|
||||||
&self,
|
&self,
|
||||||
jid: &Jid,
|
jid: &Jid,
|
||||||
ns: &str,
|
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
|
/// Return channel binding data if available
|
||||||
/// do not fail if channel binding is simply unavailable, just return Ok(None)
|
/// do not fail if channel binding is simply unavailable, just return Ok(None)
|
||||||
/// this should only be called after the TLS handshake is finished
|
/// 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)
|
Ok(ChannelBinding::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
|
||||||
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
||||||
use tokio_rustls::rustls::Error as TlsError;
|
use tokio_rustls::rustls::Error as TlsError;
|
||||||
|
|
||||||
|
use super::ServerConnectorError;
|
||||||
|
|
||||||
/// StartTLS ServerConnector Error
|
/// StartTLS ServerConnector Error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|
@ -25,41 +27,34 @@ pub enum Error {
|
||||||
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
||||||
/// DNS name parsing error
|
/// DNS name parsing error
|
||||||
DnsNameError(InvalidDnsNameError),
|
DnsNameError(InvalidDnsNameError),
|
||||||
/// tokio-xmpp error
|
|
||||||
TokioXMPP(crate::error::Error),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ServerConnectorError for Error {}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::Dns(e) => write!(fmt, "{:?}", e),
|
Self::Dns(e) => write!(fmt, "{:?}", e),
|
||||||
Error::Resolve(e) => write!(fmt, "{:?}", e),
|
Self::Resolve(e) => write!(fmt, "{:?}", e),
|
||||||
Error::Idna => write!(fmt, "IDNA error"),
|
Self::Idna => write!(fmt, "IDNA error"),
|
||||||
Error::Tls(e) => write!(fmt, "TLS error: {}", e),
|
Self::Tls(e) => write!(fmt, "TLS error: {}", e),
|
||||||
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
||||||
Error::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
|
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
|
||||||
Error::TokioXMPP(e) => write!(fmt, "TokioXMPP error: {}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for Error {}
|
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 {
|
impl From<TlsError> for Error {
|
||||||
fn from(e: TlsError) -> Self {
|
fn from(e: TlsError) -> Self {
|
||||||
Error::Tls(e)
|
Self::Tls(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
||||||
impl From<InvalidDnsNameError> for Error {
|
impl From<InvalidDnsNameError> for Error {
|
||||||
fn from(e: InvalidDnsNameError) -> Self {
|
fn from(e: InvalidDnsNameError) -> Self {
|
||||||
Error::DnsNameError(e)
|
Self::DnsNameError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use super::error::Error;
|
use super::error::Error as StartTlsError;
|
||||||
|
use crate::Error;
|
||||||
use futures::{future::select_ok, FutureExt};
|
use futures::{future::select_ok, FutureExt};
|
||||||
use hickory_resolver::{
|
use hickory_resolver::{
|
||||||
config::LookupIpStrategy, name_server::TokioConnectionProvider, IntoName, TokioAsyncResolver,
|
config::LookupIpStrategy, name_server::TokioConnectionProvider, IntoName, TokioAsyncResolver,
|
||||||
|
|
@ -8,23 +9,21 @@ use std::net::SocketAddr;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
pub async fn connect_to_host(domain: &str, port: u16) -> Result<TcpStream, Error> {
|
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() {
|
if let Ok(ip) = ascii_domain.parse() {
|
||||||
return Ok(TcpStream::connect(&SocketAddr::new(ip, port))
|
return Ok(TcpStream::connect(&SocketAddr::new(ip, port)).await?);
|
||||||
.await
|
|
||||||
.map_err(|e| Error::from(crate::Error::Io(e)))?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (config, mut options) =
|
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;
|
options.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||||
let resolver = TokioAsyncResolver::new(config, options, TokioConnectionProvider::default());
|
let resolver = TokioAsyncResolver::new(config, options, TokioConnectionProvider::default());
|
||||||
|
|
||||||
let ips = resolver
|
let ips = resolver
|
||||||
.lookup_ip(ascii_domain)
|
.lookup_ip(ascii_domain)
|
||||||
.await
|
.await
|
||||||
.map_err(Error::Resolve)?;
|
.map_err(StartTlsError::Resolve)?;
|
||||||
// Happy Eyeballs: connect to all records in parallel, return the
|
// Happy Eyeballs: connect to all records in parallel, return the
|
||||||
// first to succeed
|
// first to succeed
|
||||||
select_ok(
|
select_ok(
|
||||||
|
|
@ -33,7 +32,7 @@ pub async fn connect_to_host(domain: &str, port: u16) -> Result<TcpStream, Error
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map(|(result, _)| result)
|
.map(|(result, _)| result)
|
||||||
.map_err(|_| crate::Error::Disconnected.into())
|
.map_err(|_| crate::Error::Disconnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect_with_srv(
|
pub async fn connect_with_srv(
|
||||||
|
|
@ -41,20 +40,18 @@ pub async fn connect_with_srv(
|
||||||
srv: &str,
|
srv: &str,
|
||||||
fallback_port: u16,
|
fallback_port: u16,
|
||||||
) -> Result<TcpStream, Error> {
|
) -> 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() {
|
if let Ok(ip) = ascii_domain.parse() {
|
||||||
debug!("Attempting connection to {ip}:{fallback_port}");
|
debug!("Attempting connection to {ip}:{fallback_port}");
|
||||||
return Ok(TcpStream::connect(&SocketAddr::new(ip, fallback_port))
|
return Ok(TcpStream::connect(&SocketAddr::new(ip, fallback_port)).await?);
|
||||||
.await
|
|
||||||
.map_err(|e| Error::from(crate::Error::Io(e)))?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
let srv_domain = format!("{}.{}.", srv, ascii_domain)
|
||||||
.into_name()
|
.into_name()
|
||||||
.map_err(Error::Dns)?;
|
.map_err(StartTlsError::Dns)?;
|
||||||
let srv_records = resolver.srv_lookup(srv_domain.clone()).await.ok();
|
let srv_records = resolver.srv_lookup(srv_domain.clone()).await.ok();
|
||||||
|
|
||||||
match srv_records {
|
match srv_records {
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,11 @@ use tokio::{
|
||||||
use xmpp_parsers::{jid::Jid, ns};
|
use xmpp_parsers::{jid::Jid, ns};
|
||||||
|
|
||||||
use crate::error::ProtocolError;
|
use crate::error::ProtocolError;
|
||||||
|
use crate::Error;
|
||||||
use crate::{connect::ServerConnector, xmpp_codec::Packet, AsyncClient, SimpleClient};
|
use crate::{connect::ServerConnector, xmpp_codec::Packet, AsyncClient, SimpleClient};
|
||||||
use crate::{connect::ServerConnectorError, xmpp_stream::XMPPStream};
|
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};
|
use self::happy_eyeballs::{connect_to_host, connect_with_srv};
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
|
|
@ -58,11 +59,8 @@ pub enum ServerConfig {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerConnectorError for Error {}
|
|
||||||
|
|
||||||
impl ServerConnector for ServerConfig {
|
impl ServerConnector for ServerConfig {
|
||||||
type Stream = TlsStream<TcpStream>;
|
type Stream = TlsStream<TcpStream>;
|
||||||
type Error = Error;
|
|
||||||
async fn connect(&self, jid: &Jid, ns: &str) -> Result<XMPPStream<Self::Stream>, Error> {
|
async fn connect(&self, jid: &Jid, ns: &str) -> Result<XMPPStream<Self::Stream>, Error> {
|
||||||
// TCP connection
|
// TCP connection
|
||||||
let tcp_stream = match self {
|
let tcp_stream = match self {
|
||||||
|
|
@ -100,11 +98,9 @@ impl ServerConnector for ServerConfig {
|
||||||
// TODO: Add support for TLS 1.2 and earlier.
|
// TODO: Add support for TLS 1.2 and earlier.
|
||||||
Some(tokio_rustls::rustls::ProtocolVersion::TLSv1_3) => {
|
Some(tokio_rustls::rustls::ProtocolVersion::TLSv1_3) => {
|
||||||
let data = vec![0u8; 32];
|
let data = vec![0u8; 32];
|
||||||
let data = connection.export_keying_material(
|
let data = connection
|
||||||
data,
|
.export_keying_material(data, b"EXPORTER-Channel-Binding", None)
|
||||||
b"EXPORTER-Channel-Binding",
|
.map_err(|e| StartTlsError::Tls(e))?;
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
ChannelBinding::TlsExporter(data)
|
ChannelBinding::TlsExporter(data)
|
||||||
}
|
}
|
||||||
_ => ChannelBinding::None,
|
_ => ChannelBinding::None,
|
||||||
|
|
@ -121,7 +117,8 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
|
||||||
let stream = xmpp_stream.into_inner();
|
let stream = xmpp_stream.into_inner();
|
||||||
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
|
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
|
||||||
.connect(&domain, stream)
|
.connect(&domain, stream)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(|e| StartTlsError::Tls(e))?;
|
||||||
Ok(tls_stream)
|
Ok(tls_stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +127,7 @@ async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
|
||||||
xmpp_stream: XMPPStream<S>,
|
xmpp_stream: XMPPStream<S>,
|
||||||
) -> Result<TlsStream<S>, Error> {
|
) -> Result<TlsStream<S>, Error> {
|
||||||
let domain = xmpp_stream.jid.domain().to_string();
|
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 stream = xmpp_stream.into_inner();
|
||||||
let root_store = RootCertStore {
|
let root_store = RootCertStore {
|
||||||
roots: webpki_roots::TLS_SERVER_ROOTS.into(),
|
roots: webpki_roots::TLS_SERVER_ROOTS.into(),
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,9 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use crate::{
|
use crate::{connect::ServerConnector, xmpp_stream::XMPPStream, Component};
|
||||||
connect::{ServerConnector, ServerConnectorError},
|
|
||||||
xmpp_stream::XMPPStream,
|
|
||||||
Component,
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::error::Error;
|
use crate::Error;
|
||||||
|
|
||||||
mod component;
|
mod component;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
@ -31,16 +27,13 @@ impl TcpServerConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerConnectorError for Error {}
|
|
||||||
|
|
||||||
impl ServerConnector for TcpServerConnector {
|
impl ServerConnector for TcpServerConnector {
|
||||||
type Stream = TcpStream;
|
type Stream = TcpStream;
|
||||||
type Error = Error;
|
|
||||||
async fn connect(
|
async fn connect(
|
||||||
&self,
|
&self,
|
||||||
jid: &xmpp_parsers::jid::Jid,
|
jid: &xmpp_parsers::jid::Jid,
|
||||||
ns: &str,
|
ns: &str,
|
||||||
) -> Result<XMPPStream<Self::Stream>, Self::Error> {
|
) -> Result<XMPPStream<Self::Stream>, Error> {
|
||||||
let stream = TcpStream::connect(&*self.0)
|
let stream = TcpStream::connect(&*self.0)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| crate::Error::Io(e))?;
|
.map_err(|e| crate::Error::Io(e))?;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue