Add dns feature for DNS stuff (not just in starttls)

This commit is contained in:
xmppftw xmppftw 2024-08-05 15:09:59 +02:00
commit 97698b4d1e
8 changed files with 145 additions and 96 deletions

View file

@ -1,6 +1,5 @@
//! StartTLS ServerConnector Error
use hickory_resolver::{error::ResolveError, proto::error::ProtoError};
#[cfg(feature = "tls-native")]
use native_tls::Error as TlsError;
use std::error::Error as StdError;
@ -15,13 +14,6 @@ use super::ServerConnectorError;
/// StartTLS ServerConnector Error
#[derive(Debug)]
pub enum Error {
/// DNS protocol error
Dns(ProtoError),
/// DNS resolution error
Resolve(ResolveError),
/// DNS label conversion error, no details available from module
/// `idna`
Idna,
/// TLS error
Tls(TlsError),
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
@ -34,9 +26,6 @@ impl ServerConnectorError for Error {}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
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")))]
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),

View file

@ -1,75 +0,0 @@
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,
};
use log::debug;
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(|_| StartTlsError::Idna)?;
if let Ok(ip) = ascii_domain.parse() {
return Ok(TcpStream::connect(&SocketAddr::new(ip, port)).await?);
}
let (config, mut options) =
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(StartTlsError::Resolve)?;
// Happy Eyeballs: connect to all records in parallel, return the
// first to succeed
select_ok(
ips.into_iter()
.map(|ip| TcpStream::connect(SocketAddr::new(ip, port)).boxed()),
)
.await
.map(|(result, _)| result)
.map_err(|_| crate::Error::Disconnected)
}
pub async fn connect_with_srv(
domain: &str,
srv: &str,
fallback_port: u16,
) -> Result<TcpStream, Error> {
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?);
}
let resolver = TokioAsyncResolver::tokio_from_system_conf().map_err(StartTlsError::Resolve)?;
let srv_domain = format!("{}.{}.", srv, ascii_domain)
.into_name()
.map_err(StartTlsError::Dns)?;
let srv_records = resolver.srv_lookup(srv_domain.clone()).await.ok();
match srv_records {
Some(lookup) => {
// TODO: sort lookup records by priority/weight
for srv in lookup.iter() {
debug!("Attempting connection to {srv_domain} {srv}");
match connect_to_host(&srv.target().to_ascii(), srv.port()).await {
Ok(stream) => return Ok(stream),
Err(_) => {}
}
}
Err(crate::Error::Disconnected.into())
}
None => {
// SRV lookup error, retry with hostname
debug!("Attempting connection to {domain}:{fallback_port}");
connect_to_host(domain, fallback_port).await
}
}
}

View file

@ -27,16 +27,17 @@ 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 crate::{
connect::{ServerConnector, ServerConnectorError, Tcp},
error::{Error, ProtocolError},
xmpp_codec::Packet,
xmpp_stream::XMPPStream,
AsyncClient, SimpleClient,
};
use self::error::Error as StartTlsError;
use self::happy_eyeballs::{connect_to_host, connect_with_srv};
pub mod error;
mod happy_eyeballs;
/// AsyncClient that connects over StartTls
pub type StartTlsAsyncClient = AsyncClient<ServerConfig>;
@ -64,9 +65,9 @@ impl ServerConnector for ServerConfig {
// TCP connection
let tcp_stream = match self {
ServerConfig::UseSrv => {
connect_with_srv(jid.domain().as_str(), "_xmpp-client._tcp", 5222).await?
Tcp::resolve_with_srv(jid.domain().as_str(), "_xmpp-client._tcp", 5222).await?
}
ServerConfig::Manual { host, port } => connect_to_host(host.as_str(), *port).await?,
ServerConfig::Manual { host, port } => Tcp::resolve(host.as_str(), *port).await?,
};
// Unencryped XMPPStream