Add dns feature for DNS stuff (not just in starttls)
This commit is contained in:
parent
d706b318c3
commit
97698b4d1e
8 changed files with 145 additions and 96 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue