2021-01-22 17:33:50 -08:00
|
|
|
use std::{
|
|
|
|
future::Future,
|
|
|
|
io,
|
|
|
|
net::SocketAddr,
|
|
|
|
pin::Pin,
|
|
|
|
rc::Rc,
|
|
|
|
task::{Context, Poll},
|
|
|
|
vec::IntoIter,
|
|
|
|
};
|
|
|
|
|
|
|
|
use actix_rt::task::{spawn_blocking, JoinHandle};
|
2019-11-14 18:38:24 +06:00
|
|
|
use actix_service::{Service, ServiceFactory};
|
2021-01-22 17:33:50 -08:00
|
|
|
use futures_core::{future::LocalBoxFuture, ready};
|
2020-12-29 00:38:41 +00:00
|
|
|
use log::trace;
|
2018-09-10 19:16:46 -07:00
|
|
|
|
2020-12-29 00:38:41 +00:00
|
|
|
use super::connect::{Address, Connect};
|
|
|
|
use super::error::ConnectError;
|
2019-03-13 12:40:11 -07:00
|
|
|
|
2021-01-26 08:05:19 +00:00
|
|
|
/// DNS Resolver Service Factory
|
2021-01-22 17:33:50 -08:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct ResolverFactory {
|
|
|
|
resolver: Resolver,
|
2018-09-10 19:16:46 -07:00
|
|
|
}
|
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
impl ResolverFactory {
|
|
|
|
pub fn new(resolver: Resolver) -> Self {
|
|
|
|
Self { resolver }
|
2019-08-05 09:52:50 -07:00
|
|
|
}
|
2019-03-13 22:51:31 -07:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
pub fn service(&self) -> Resolver {
|
|
|
|
self.resolver.clone()
|
2019-03-13 12:40:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
impl<T: Address> ServiceFactory<Connect<T>> for ResolverFactory {
|
2019-03-13 15:37:12 -07:00
|
|
|
type Response = Connect<T>;
|
2019-03-13 12:40:11 -07:00
|
|
|
type Error = ConnectError;
|
2019-05-12 06:03:50 -07:00
|
|
|
type Config = ();
|
2021-01-22 17:33:50 -08:00
|
|
|
type Service = Resolver;
|
2019-03-13 12:40:11 -07:00
|
|
|
type InitError = ();
|
2021-01-22 17:33:50 -08:00
|
|
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
2019-03-13 12:40:11 -07:00
|
|
|
|
2019-12-02 21:27:48 +06:00
|
|
|
fn new_service(&self, _: ()) -> Self::Future {
|
2021-01-22 17:33:50 -08:00
|
|
|
let service = self.resolver.clone();
|
|
|
|
Box::pin(async { Ok(service) })
|
2019-03-13 12:40:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// DNS Resolver Service
|
2021-01-22 17:33:50 -08:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum Resolver {
|
|
|
|
Default,
|
|
|
|
Custom(Rc<dyn Resolve>),
|
2018-09-10 19:16:46 -07:00
|
|
|
}
|
|
|
|
|
2021-01-26 08:05:19 +00:00
|
|
|
/// An interface for custom async DNS resolvers.
|
2021-01-22 17:33:50 -08:00
|
|
|
///
|
2021-01-26 08:05:19 +00:00
|
|
|
/// # Usage
|
2021-01-22 17:33:50 -08:00
|
|
|
/// ```rust
|
|
|
|
/// use std::net::SocketAddr;
|
|
|
|
///
|
|
|
|
/// use actix_tls::connect::{Resolve, Resolver};
|
|
|
|
/// use futures_util::future::LocalBoxFuture;
|
|
|
|
///
|
2021-01-26 08:05:19 +00:00
|
|
|
/// // use trust-dns async tokio resolver
|
2021-01-22 17:33:50 -08:00
|
|
|
/// use trust_dns_resolver::TokioAsyncResolver;
|
|
|
|
///
|
|
|
|
/// struct MyResolver {
|
|
|
|
/// trust_dns: TokioAsyncResolver,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // impl Resolve trait and convert given host address str and port to SocketAddr.
|
|
|
|
/// impl Resolve for MyResolver {
|
|
|
|
/// fn lookup<'a>(
|
|
|
|
/// &'a self,
|
|
|
|
/// host: &'a str,
|
|
|
|
/// port: u16,
|
|
|
|
/// ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>> {
|
|
|
|
/// Box::pin(async move {
|
|
|
|
/// let res = self
|
|
|
|
/// .trust_dns
|
|
|
|
/// .lookup_ip(host)
|
|
|
|
/// .await?
|
|
|
|
/// .iter()
|
|
|
|
/// .map(|ip| SocketAddr::new(ip, port))
|
|
|
|
/// .collect();
|
|
|
|
/// Ok(res)
|
|
|
|
/// })
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// let resolver = MyResolver {
|
|
|
|
/// trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(),
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // construct custom resolver
|
|
|
|
/// let resolver = Resolver::new_custom(resolver);
|
|
|
|
///
|
|
|
|
/// // pass custom resolver to connector builder.
|
|
|
|
/// // connector would then be usable as a service or awc's connector.
|
|
|
|
/// let connector = actix_tls::connect::new_connector::<&str>(resolver.clone());
|
|
|
|
///
|
|
|
|
/// // resolver can be passed to connector factory where returned service factory
|
|
|
|
/// // can be used to construct new connector services.
|
|
|
|
/// let factory = actix_tls::connect::new_connector_factory::<&str>(resolver);
|
2021-01-26 08:05:19 +00:00
|
|
|
/// ```
|
2021-01-22 17:33:50 -08:00
|
|
|
pub trait Resolve {
|
|
|
|
fn lookup<'a>(
|
|
|
|
&'a self,
|
|
|
|
host: &'a str,
|
|
|
|
port: u16,
|
|
|
|
) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>>;
|
2019-04-11 09:57:21 -07:00
|
|
|
}
|
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
impl Resolver {
|
|
|
|
/// Constructor for custom Resolve trait object and use it as resolver.
|
|
|
|
pub fn new_custom(resolver: impl Resolve + 'static) -> Self {
|
|
|
|
Self::Custom(Rc::new(resolver))
|
2018-09-10 19:39:55 -07:00
|
|
|
}
|
2018-09-10 19:16:46 -07:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
// look up with default resolver variant.
|
|
|
|
fn look_up<T: Address>(req: &Connect<T>) -> JoinHandle<io::Result<IntoIter<SocketAddr>>> {
|
2021-01-26 08:05:19 +00:00
|
|
|
let host = req.hostname();
|
2021-01-22 17:33:50 -08:00
|
|
|
// TODO: Connect should always return host with port if possible.
|
|
|
|
let host = if req
|
2021-01-26 08:05:19 +00:00
|
|
|
.hostname()
|
2021-01-22 17:33:50 -08:00
|
|
|
.splitn(2, ':')
|
|
|
|
.last()
|
|
|
|
.and_then(|p| p.parse::<u16>().ok())
|
|
|
|
.map(|p| p == req.port())
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
host.to_string()
|
|
|
|
} else {
|
|
|
|
format!("{}:{}", host, req.port())
|
|
|
|
};
|
|
|
|
|
2021-01-26 08:05:19 +00:00
|
|
|
// run blocking DNS lookup in thread pool
|
2021-01-22 17:33:50 -08:00
|
|
|
spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(&host))
|
2018-09-10 19:16:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
impl<T: Address> Service<Connect<T>> for Resolver {
|
2019-03-13 15:37:12 -07:00
|
|
|
type Response = Connect<T>;
|
2019-03-13 12:40:11 -07:00
|
|
|
type Error = ConnectError;
|
2021-01-22 17:33:50 -08:00
|
|
|
type Future = ResolverFuture<T>;
|
2018-09-10 19:16:46 -07:00
|
|
|
|
2020-12-27 14:15:42 +00:00
|
|
|
actix_service::always_ready!();
|
2018-09-10 19:16:46 -07:00
|
|
|
|
2021-01-22 19:06:22 -08:00
|
|
|
fn call(&self, req: Connect<T>) -> Self::Future {
|
2021-01-26 08:05:19 +00:00
|
|
|
if req.addr.is_some() {
|
2021-01-22 17:33:50 -08:00
|
|
|
ResolverFuture::Connected(Some(req))
|
2021-01-26 08:05:19 +00:00
|
|
|
} else if let Ok(ip) = req.hostname().parse() {
|
2021-01-22 17:33:50 -08:00
|
|
|
let addr = SocketAddr::new(ip, req.port());
|
|
|
|
let req = req.set_addr(Some(addr));
|
|
|
|
ResolverFuture::Connected(Some(req))
|
2019-03-13 15:37:12 -07:00
|
|
|
} else {
|
2021-01-26 08:05:19 +00:00
|
|
|
trace!("DNS resolver: resolving host {:?}", req.hostname());
|
2020-02-25 23:44:59 -03:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
match self {
|
|
|
|
Self::Default => {
|
|
|
|
let fut = Self::look_up(&req);
|
|
|
|
ResolverFuture::LookUp(fut, Some(req))
|
|
|
|
}
|
2020-02-25 23:44:59 -03:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
Self::Custom(resolver) => {
|
|
|
|
let resolver = Rc::clone(&resolver);
|
|
|
|
ResolverFuture::LookupCustom(Box::pin(async move {
|
|
|
|
let addrs = resolver
|
2021-01-26 08:05:19 +00:00
|
|
|
.lookup(req.hostname(), req.port())
|
2021-01-22 17:33:50 -08:00
|
|
|
.await
|
|
|
|
.map_err(ConnectError::Resolver)?;
|
|
|
|
|
|
|
|
let req = req.set_addrs(addrs);
|
|
|
|
|
|
|
|
if req.addr.is_none() {
|
|
|
|
Err(ConnectError::NoRecords)
|
|
|
|
} else {
|
|
|
|
Ok(req)
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
2018-09-10 19:16:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
pub enum ResolverFuture<T: Address> {
|
|
|
|
Connected(Option<Connect<T>>),
|
|
|
|
LookUp(
|
|
|
|
JoinHandle<io::Result<IntoIter<SocketAddr>>>,
|
|
|
|
Option<Connect<T>>,
|
|
|
|
),
|
|
|
|
LookupCustom(LocalBoxFuture<'static, Result<Connect<T>, ConnectError>>),
|
|
|
|
}
|
|
|
|
|
2019-03-13 15:37:12 -07:00
|
|
|
impl<T: Address> Future for ResolverFuture<T> {
|
2019-11-14 18:38:24 +06:00
|
|
|
type Output = Result<Connect<T>, ConnectError>;
|
2018-09-10 19:16:46 -07:00
|
|
|
|
2019-12-02 22:30:09 +06:00
|
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
2021-01-22 17:33:50 -08:00
|
|
|
match self.get_mut() {
|
|
|
|
Self::Connected(conn) => Poll::Ready(Ok(conn
|
|
|
|
.take()
|
|
|
|
.expect("ResolverFuture polled after finished"))),
|
2021-01-26 08:05:19 +00:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
Self::LookUp(fut, req) => {
|
|
|
|
let res = match ready!(Pin::new(fut).poll(cx)) {
|
|
|
|
Ok(Ok(res)) => Ok(res),
|
|
|
|
Ok(Err(e)) => Err(ConnectError::Resolver(Box::new(e))),
|
|
|
|
Err(e) => Err(ConnectError::Io(e.into())),
|
|
|
|
};
|
|
|
|
|
|
|
|
let req = req.take().unwrap();
|
|
|
|
|
2021-01-26 08:05:19 +00:00
|
|
|
let addrs = res.map_err(|err| {
|
2021-01-22 17:33:50 -08:00
|
|
|
trace!(
|
|
|
|
"DNS resolver: failed to resolve host {:?} err: {:?}",
|
2021-01-26 08:05:19 +00:00
|
|
|
req.hostname(),
|
|
|
|
err
|
2021-01-22 17:33:50 -08:00
|
|
|
);
|
2021-01-26 08:05:19 +00:00
|
|
|
|
|
|
|
err
|
2021-01-22 17:33:50 -08:00
|
|
|
})?;
|
2019-07-17 02:17:51 +02:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
let req = req.set_addrs(addrs);
|
2019-07-17 02:17:51 +02:00
|
|
|
|
2019-03-13 15:37:12 -07:00
|
|
|
trace!(
|
|
|
|
"DNS resolver: host {:?} resolved to {:?}",
|
2021-01-26 08:05:19 +00:00
|
|
|
req.hostname(),
|
2019-07-17 02:17:51 +02:00
|
|
|
req.addrs()
|
2019-03-13 15:37:12 -07:00
|
|
|
);
|
2019-07-17 02:17:51 +02:00
|
|
|
|
|
|
|
if req.addr.is_none() {
|
2019-11-14 18:38:24 +06:00
|
|
|
Poll::Ready(Err(ConnectError::NoRecords))
|
2019-03-13 12:40:11 -07:00
|
|
|
} else {
|
2019-11-14 18:38:24 +06:00
|
|
|
Poll::Ready(Ok(req))
|
2019-03-13 12:40:11 -07:00
|
|
|
}
|
2018-09-10 19:16:46 -07:00
|
|
|
}
|
2021-01-26 08:05:19 +00:00
|
|
|
|
2021-01-22 17:33:50 -08:00
|
|
|
Self::LookupCustom(fut) => fut.as_mut().poll(cx),
|
2018-09-10 19:16:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|