use std::collections::HashMap;
use std::fmt::Debug;
use std::io as std_io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use hostname::get_hostname;
use native_tls::{self, TlsConnector as NativeTlsConnector, TlsConnectorBuilder};
use crate::{
ascii::IgnoreAsciiCaseStr,
data_types::{AddressLiteral, Capability, Domain, EhloParam},
};
#[derive(Debug, Clone)]
pub enum ClientId {
Domain(Domain),
AddressLiteral(AddressLiteral),
}
impl ClientId {
pub fn localhost() -> Self {
Self::from(Ipv4Addr::new(127, 0, 0, 1))
}
pub fn hostname() -> Self {
Self::try_hostname().unwrap_or_else(Self::localhost)
}
pub fn try_hostname() -> Option<Self> {
get_hostname().map(|name| {
let domain = Domain::new_unchecked(name);
ClientId::Domain(domain)
})
}
}
impl From<Domain> for ClientId {
fn from(dm: Domain) -> Self {
ClientId::Domain(dm)
}
}
impl From<AddressLiteral> for ClientId {
fn from(adl: AddressLiteral) -> Self {
ClientId::AddressLiteral(adl)
}
}
impl From<IpAddr> for ClientId {
fn from(saddr: IpAddr) -> Self {
let adl = AddressLiteral::from(saddr);
ClientId::from(adl)
}
}
impl From<Ipv4Addr> for ClientId {
fn from(saddr: Ipv4Addr) -> Self {
let adl = AddressLiteral::from(saddr);
ClientId::from(adl)
}
}
impl From<Ipv6Addr> for ClientId {
fn from(saddr: Ipv6Addr) -> Self {
let adl = AddressLiteral::from(saddr);
ClientId::from(adl)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TlsConfig<S = DefaultTlsSetup>
where
S: SetupTls,
{
pub domain: Domain,
pub setup: S,
}
impl From<Domain> for TlsConfig {
fn from(domain: Domain) -> Self {
TlsConfig {
domain,
setup: DefaultTlsSetup,
}
}
}
pub trait SetupTls: Debug + Send + 'static {
fn setup(self, builder: TlsConnectorBuilder) -> Result<NativeTlsConnector, native_tls::Error>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct DefaultTlsSetup;
impl SetupTls for DefaultTlsSetup {
fn setup(self, builder: TlsConnectorBuilder) -> Result<NativeTlsConnector, native_tls::Error> {
builder.build()
}
}
impl<F: 'static> SetupTls for F
where
F: Send + Debug + FnOnce(TlsConnectorBuilder) -> Result<NativeTlsConnector, native_tls::Error>,
{
fn setup(self, builder: TlsConnectorBuilder) -> Result<NativeTlsConnector, native_tls::Error> {
(self)(builder)
}
}
macro_rules! alttry {
($block:block => $emap:expr) => {{
let func = move || -> Result<_, _> { $block };
match func() {
Ok(ok) => ok,
Err(err) => {
#[allow(clippy::redundant_closure_call)]
return ($emap)(err);
}
}
}};
}
pub(crate) fn map_tls_err(err: native_tls::Error) -> std_io::Error {
std_io::Error::new(std_io::ErrorKind::Other, err)
}
#[derive(Debug, Clone)]
pub struct EhloData {
domain: Domain,
data: HashMap<Capability, Vec<EhloParam>>,
}
impl EhloData {
pub fn new(domain: Domain, data: HashMap<Capability, Vec<EhloParam>>) -> Self {
EhloData { domain, data }
}
pub fn has_capability<A>(&self, cap: A) -> bool
where
A: AsRef<str>,
{
self.data
.contains_key(<&IgnoreAsciiCaseStr>::from(cap.as_ref()))
}
pub fn get_capability_params<A>(&self, cap: A) -> Option<&[EhloParam]>
where
A: AsRef<str>,
{
self.data
.get(<&IgnoreAsciiCaseStr>::from(cap.as_ref()))
.map(|vec| &**vec)
}
pub fn capability_map(&self) -> &HashMap<Capability, Vec<EhloParam>> {
&self.data
}
pub fn domain(&self) -> &Domain {
&self.domain
}
}
impl From<(Domain, HashMap<Capability, Vec<EhloParam>>)> for EhloData {
fn from((domain, map): (Domain, HashMap<Capability, Vec<EhloParam>>)) -> Self {
EhloData::new(domain, map)
}
}
impl Into<(Domain, HashMap<Capability, Vec<EhloParam>>)> for EhloData {
fn into(self) -> (Domain, HashMap<Capability, Vec<EhloParam>>) {
let EhloData { domain, data } = self;
(domain, data)
}
}