use std::str::FromStr;
use fqdn::FQDN;
use tracing::{debug, info};
use crate::{
http::dns::is_error_negative_lookup,
network::AsyncReadWrite,
smtp::{
ProtocolError,
inbound::{Session, SessionResult},
},
};
impl<S: AsyncReadWrite> Session<S> {
pub async fn handle_ehlo(&mut self, host: &str, extended: bool) -> SessionResult<()> {
let Ok(ehlo_hostname) = FQDN::from_str(host) else {
info!("{self}: {host}: Invalid EHLO hostname");
self.set_error(ProtocolError::InvalidEhloHostname(format!(
"{host}: incorrect hostname"
)));
return self.reply("550", "5.5.0", "Invalid EHLO hostname.").await;
};
if let Some(v) = &self.data.ehlo_hostname
&& v == &ehlo_hostname
{
return self.send_ehlo(extended).await;
}
if ehlo_hostname.depth() < 2 {
info!("{self}: {host}: EHLO is not FQDN");
self.set_error(ProtocolError::InvalidEhloHostname(format!(
"{host}: not FQDN"
)));
return self
.reply("550", "5.5.0", "EHLO hostname must be an FQDN.")
.await;
};
if self.cfg.verify_ehlo_hostname {
match self.cfg.authenticator.resolver().lookup_ip(host).await {
Ok(v) => match v.iter().next() {
Some(v) => {
debug!("{self}: {host}: EHLO hostname found in DNS: {v}");
}
None => {
info!("{self}: {host}: EHLO not found in DNS");
self.set_error(ProtocolError::InvalidEhloHostname(format!(
"{host}: not found in DNS"
)));
return self
.reply("550", "5.5.0", "EHLO hostname not found in DNS.")
.await;
}
},
Err(e) => {
info!("{self}: {host}: EHLO not found in DNS: {e:#}");
if is_error_negative_lookup(&e) {
self.set_error(ProtocolError::InvalidEhloHostname(format!(
"{host}: not found in DNS: {e:#}"
)));
return self
.reply("550", "5.5.0", "EHLO hostname not found in DNS.")
.await;
}
return self
.reply("451", "4.7.25", "Temporary error validating EHLO hostname.")
.await;
}
}
}
self.reset_message();
self.data.ehlo_hostname = Some(ehlo_hostname);
return self.send_ehlo(extended).await;
}
async fn send_ehlo(&mut self, extended: bool) -> SessionResult<()> {
let buf = if !extended {
&self.cfg.helo
} else if self.tls_info.is_none() && self.cfg.tls_mode.enabled() {
&self.cfg.ehlo_tls
} else {
&self.cfg.ehlo
};
self.write(&buf.clone()).await
}
}