summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/directory/src/lib.rs6
-rw-r--r--crates/smtp/src/config/auth.rs8
-rw-r--r--crates/smtp/src/config/if_block.rs51
-rw-r--r--crates/smtp/src/config/mod.rs6
-rw-r--r--crates/smtp/src/config/queue.rs6
-rw-r--r--crates/smtp/src/config/report.rs8
-rw-r--r--crates/smtp/src/config/session.rs24
-rw-r--r--crates/smtp/src/core/if_block.rs111
-rw-r--r--crates/smtp/src/core/mod.rs35
-rw-r--r--crates/smtp/src/core/params.rs2
-rw-r--r--crates/smtp/src/core/throttle.rs58
-rw-r--r--crates/smtp/src/inbound/data.rs4
-rw-r--r--crates/smtp/src/inbound/mail.rs5
-rw-r--r--crates/smtp/src/inbound/rcpt.rs7
-rw-r--r--crates/smtp/src/inbound/session.rs126
-rw-r--r--crates/smtp/src/inbound/vrfy.rs4
-rw-r--r--crates/smtp/src/outbound/lookup.rs8
-rw-r--r--crates/smtp/src/queue/mod.rs229
-rw-r--r--crates/smtp/src/queue/quota.rs7
-rw-r--r--crates/smtp/src/queue/throttle.rs11
-rw-r--r--crates/smtp/src/reporting/dmarc.rs45
-rw-r--r--crates/smtp/src/reporting/mod.rs2
-rw-r--r--crates/smtp/src/reporting/scheduler.rs21
-rw-r--r--crates/smtp/src/reporting/tls.rs26
-rw-r--r--crates/utils/src/config/dynvalue.rs101
-rw-r--r--crates/utils/src/config/mod.rs39
-rw-r--r--tests/resources/smtp/config/rules-dynvalue.toml18
-rw-r--r--tests/src/smtp/config.rs101
-rw-r--r--tests/src/smtp/inbound/auth.rs4
-rw-r--r--tests/src/smtp/inbound/dmarc.rs10
-rw-r--r--tests/src/smtp/inbound/rewrite.rs12
-rw-r--r--tests/src/smtp/inbound/sign.rs8
-rw-r--r--tests/src/smtp/lookup/sql.rs6
-rw-r--r--tests/src/smtp/lookup/utils.rs13
-rw-r--r--tests/src/smtp/queue/dsn.rs6
-rw-r--r--tests/src/smtp/reporting/dmarc.rs4
-rw-r--r--tests/src/smtp/reporting/tls.rs4
37 files changed, 576 insertions, 560 deletions
diff --git a/crates/directory/src/lib.rs b/crates/directory/src/lib.rs
index 5a72405b..7659f69e 100644
--- a/crates/directory/src/lib.rs
+++ b/crates/directory/src/lib.rs
@@ -181,7 +181,7 @@ pub enum AddressMapping {
Enable,
Custom {
regex: regex::Regex,
- mapping: DynValue,
+ mapping: DynValue<String>,
},
#[default]
Disable,
@@ -320,7 +320,7 @@ impl AddressMapping {
}
if !regex_capture.is_empty() {
- return mapping.apply(regex_capture);
+ return mapping.apply(regex_capture, &());
}
}
AddressMapping::Disable => (),
@@ -343,7 +343,7 @@ impl AddressMapping {
}
}
if !regex_capture.is_empty() {
- Some(mapping.apply(regex_capture))
+ Some(mapping.apply(regex_capture, &()))
} else {
None
}
diff --git a/crates/smtp/src/config/auth.rs b/crates/smtp/src/config/auth.rs
index 2c976e3b..65f770ea 100644
--- a/crates/smtp/src/config/auth.rs
+++ b/crates/smtp/src/config/auth.rs
@@ -69,7 +69,11 @@ impl ConfigAuth for Config {
.parse_if_block("auth.dkim.verify", ctx, &envelope_sender_keys)?
.unwrap_or_else(|| IfBlock::new(VerifyStrategy::Relaxed)),
sign: self
- .parse_if_block::<Vec<DynValue>>("auth.dkim.sign", ctx, &envelope_sender_keys)?
+ .parse_if_block::<Vec<DynValue<EnvelopeKey>>>(
+ "auth.dkim.sign",
+ ctx,
+ &envelope_sender_keys,
+ )?
.unwrap_or_default()
.map_if_block(&ctx.signers, "auth.dkim.sign", "signature")?,
},
@@ -78,7 +82,7 @@ impl ConfigAuth for Config {
.parse_if_block("auth.arc.verify", ctx, &envelope_sender_keys)?
.unwrap_or_else(|| IfBlock::new(VerifyStrategy::Relaxed)),
seal: self
- .parse_if_block::<Option<DynValue>>(
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
"auth.arc.seal",
ctx,
&envelope_sender_keys,
diff --git a/crates/smtp/src/config/if_block.rs b/crates/smtp/src/config/if_block.rs
index 2d01ea5c..8414b9f9 100644
--- a/crates/smtp/src/config/if_block.rs
+++ b/crates/smtp/src/config/if_block.rs
@@ -237,50 +237,7 @@ impl IfBlock<Option<String>> {
}
}
-/*
-impl IfBlock<Vec<String>> {
- pub fn map_if_block<T: ?Sized>(
- self,
- map: &AHashMap<String, Arc<T>>,
- key_name: &str,
- object_name: &str,
- ) -> super::Result<IfBlock<Vec<Arc<T>>>> {
- let mut if_then = Vec::with_capacity(self.if_then.len());
- for if_clause in self.if_then.into_iter() {
- if_then.push(IfThen {
- conditions: if_clause.conditions,
- then: Self::map_value(map, if_clause.then, object_name, key_name)?,
- });
- }
-
- Ok(IfBlock {
- if_then,
- default: Self::map_value(map, self.default, object_name, key_name)?,
- })
- }
-
- fn map_value<T: ?Sized>(
- map: &AHashMap<String, Arc<T>>,
- values: Vec<String>,
- object_name: &str,
- key_name: &str,
- ) -> super::Result<Vec<Arc<T>>> {
- let mut result = Vec::with_capacity(values.len());
- for value in values {
- if let Some(value) = map.get(&value) {
- result.push(value.clone());
- } else {
- return Err(format!(
- "Unable to find {object_name} {value:?} declared for {key_name:?}",
- ));
- }
- }
- Ok(result)
- }
-}
-*/
-
-impl IfBlock<Vec<DynValue>> {
+impl IfBlock<Vec<DynValue<EnvelopeKey>>> {
pub fn map_if_block<T: ?Sized>(
self,
map: &AHashMap<String, Arc<T>>,
@@ -303,7 +260,7 @@ impl IfBlock<Vec<DynValue>> {
fn map_value<T: ?Sized>(
map: &AHashMap<String, Arc<T>>,
- values: Vec<DynValue>,
+ values: Vec<DynValue<EnvelopeKey>>,
object_name: &str,
key_name: &str,
) -> super::Result<Vec<MaybeDynValue<T>>> {
@@ -328,7 +285,7 @@ impl IfBlock<Vec<DynValue>> {
}
}
-impl IfBlock<Option<DynValue>> {
+impl IfBlock<Option<DynValue<EnvelopeKey>>> {
pub fn map_if_block<T: ?Sized>(
self,
map: &AHashMap<String, Arc<T>>,
@@ -352,7 +309,7 @@ impl IfBlock<Option<DynValue>> {
fn map_value<T: ?Sized>(
map: &AHashMap<String, Arc<T>>,
- value: Option<DynValue>,
+ value: Option<DynValue<EnvelopeKey>>,
object_name: &str,
key_name: &str,
) -> super::Result<Option<MaybeDynValue<T>>> {
diff --git a/crates/smtp/src/config/mod.rs b/crates/smtp/src/config/mod.rs
index 0b149d98..4d1136d3 100644
--- a/crates/smtp/src/config/mod.rs
+++ b/crates/smtp/src/config/mod.rs
@@ -231,14 +231,14 @@ pub struct Auth {
pub struct Mail {
pub script: IfBlock<Option<Arc<Sieve>>>,
- pub rewrite: IfBlock<Option<DynValue>>,
+ pub rewrite: IfBlock<Option<DynValue<EnvelopeKey>>>,
}
pub struct Rcpt {
pub script: IfBlock<Option<Arc<Sieve>>>,
pub relay: IfBlock<bool>,
pub directory: IfBlock<Option<MaybeDynValue<dyn Directory>>>,
- pub rewrite: IfBlock<Option<DynValue>>,
+ pub rewrite: IfBlock<Option<DynValue<EnvelopeKey>>>,
// Errors
pub errors_max: IfBlock<usize>,
@@ -380,7 +380,7 @@ pub enum AddressMatch {
#[derive(Clone)]
pub enum MaybeDynValue<T: ?Sized> {
Dynamic {
- eval: DynValue,
+ eval: DynValue<EnvelopeKey>,
items: AHashMap<String, Arc<T>>,
},
Static(Arc<T>),
diff --git a/crates/smtp/src/config/queue.rs b/crates/smtp/src/config/queue.rs
index ba9419de..b666370b 100644
--- a/crates/smtp/src/config/queue.rs
+++ b/crates/smtp/src/config/queue.rs
@@ -189,7 +189,11 @@ impl ConfigQueue for Config {
.parse_if_block("report.dsn.from-address", ctx, &sender_envelope_keys)?
.unwrap_or_else(|| IfBlock::new(format!("MAILER-DAEMON@{default_hostname}"))),
sign: self
- .parse_if_block::<Vec<DynValue>>("report.dsn.sign", ctx, &sender_envelope_keys)?
+ .parse_if_block::<Vec<DynValue<EnvelopeKey>>>(
+ "report.dsn.sign",
+ ctx,
+ &sender_envelope_keys,
+ )?
.unwrap_or_default()
.map_if_block(&ctx.signers, "report.dsn.sign", "signature")?,
},
diff --git a/crates/smtp/src/config/report.rs b/crates/smtp/src/config/report.rs
index dce1b3e6..dca3d6db 100644
--- a/crates/smtp/src/config/report.rs
+++ b/crates/smtp/src/config/report.rs
@@ -120,7 +120,11 @@ impl ConfigReport for Config {
.parse_if_block(("report", id, "subject"), ctx, available_keys)?
.unwrap_or_else(|| IfBlock::new(format!("{} Report", id.to_ascii_uppercase()))),
sign: self
- .parse_if_block::<Vec<DynValue>>(("report", id, "sign"), ctx, available_keys)?
+ .parse_if_block::<Vec<DynValue<EnvelopeKey>>>(
+ ("report", id, "sign"),
+ ctx,
+ available_keys,
+ )?
.unwrap_or_default()
.map_if_block(&ctx.signers, &("report", id, "sign").as_key(), "signature")?,
send: self
@@ -173,7 +177,7 @@ impl ConfigReport for Config {
.parse_if_block(("report", id, "aggregate.send"), ctx, available_keys)?
.unwrap_or_default(),
sign: self
- .parse_if_block::<Vec<DynValue>>(
+ .parse_if_block::<Vec<DynValue<EnvelopeKey>>>(
("report", id, "aggregate.sign"),
ctx,
&rcpt_envelope_keys,
diff --git a/crates/smtp/src/config/session.rs b/crates/smtp/src/config/session.rs
index 6eb60eb4..2f75ec28 100644
--- a/crates/smtp/src/config/session.rs
+++ b/crates/smtp/src/config/session.rs
@@ -250,7 +250,11 @@ impl ConfigSession for Config {
Ok(Auth {
directory: self
- .parse_if_block::<Option<DynValue>>("session.auth.directory", ctx, &available_keys)?
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
+ "session.auth.directory",
+ ctx,
+ &available_keys,
+ )?
.unwrap_or_default()
.map_if_block(
&ctx.directory.directories,
@@ -297,7 +301,11 @@ impl ConfigSession for Config {
.unwrap_or_default()
.map_if_block(&ctx.scripts, "session.mail.script", "script")?,
rewrite: self
- .parse_if_block::<Option<DynValue>>("session.mail.rewrite", ctx, &available_keys)?
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
+ "session.mail.rewrite",
+ ctx,
+ &available_keys,
+ )?
.unwrap_or_default(),
})
}
@@ -321,7 +329,11 @@ impl ConfigSession for Config {
.parse_if_block("session.rcpt.relay", ctx, &available_keys)?
.unwrap_or_else(|| IfBlock::new(false)),
directory: self
- .parse_if_block::<Option<DynValue>>("session.rcpt.directory", ctx, &available_keys)?
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
+ "session.rcpt.directory",
+ ctx,
+ &available_keys,
+ )?
.unwrap_or_default()
.map_if_block(
&ctx.directory.directories,
@@ -338,7 +350,11 @@ impl ConfigSession for Config {
.parse_if_block("session.rcpt.max-recipients", ctx, &available_keys)?
.unwrap_or_else(|| IfBlock::new(100)),
rewrite: self
- .parse_if_block::<Option<DynValue>>("session.rcpt.rewrite", ctx, &available_keys)?
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
+ "session.rcpt.rewrite",
+ ctx,
+ &available_keys,
+ )?
.unwrap_or_default(),
})
}
diff --git a/crates/smtp/src/core/if_block.rs b/crates/smtp/src/core/if_block.rs
index fbfaf03c..9d6e4b89 100644
--- a/crates/smtp/src/core/if_block.rs
+++ b/crates/smtp/src/core/if_block.rs
@@ -21,28 +21,22 @@
* for more details.
*/
-use std::{
- borrow::Cow,
- net::{IpAddr, Ipv4Addr},
- sync::Arc,
-};
+use std::{borrow::Cow, net::IpAddr, sync::Arc};
-use utils::config::DynValue;
+use utils::config::{DynValue, KeyLookup};
use crate::config::{
Condition, ConditionMatch, Conditions, EnvelopeKey, IfBlock, IpAddrMask, MaybeDynValue,
StringMatch,
};
-use super::Envelope;
-
pub struct Captures<'x, T> {
value: &'x T,
captures: Vec<String>,
}
impl<T: Default> IfBlock<T> {
- pub async fn eval(&self, envelope: &impl Envelope) -> &T {
+ pub async fn eval(&self, envelope: &impl KeyLookup<Key = EnvelopeKey>) -> &T {
for if_then in &self.if_then {
if if_then.conditions.eval(envelope).await {
return &if_then.then;
@@ -52,7 +46,10 @@ impl<T: Default> IfBlock<T> {
&self.default
}
- pub async fn eval_and_capture(&self, envelope: &impl Envelope) -> Captures<'_, T> {
+ pub async fn eval_and_capture(
+ &self,
+ envelope: &impl KeyLookup<Key = EnvelopeKey>,
+ ) -> Captures<'_, T> {
for if_then in &self.if_then {
if let Some(captures) = if_then.conditions.eval_and_capture(envelope).await {
return Captures {
@@ -70,7 +67,7 @@ impl<T: Default> IfBlock<T> {
}
impl Conditions {
- pub async fn eval(&self, envelope: &impl Envelope) -> bool {
+ pub async fn eval(&self, envelope: &impl KeyLookup<Key = EnvelopeKey>) -> bool {
let mut conditions = self.conditions.iter();
let mut matched = false;
@@ -79,48 +76,27 @@ impl Conditions {
Condition::Match { key, value, not } => {
matched = match value {
ConditionMatch::String(value) => {
- let ctx_value = envelope.key_to_string(key);
+ let ctx_value = envelope.key(key);
match value {
StringMatch::Equal(value) => value.eq(ctx_value.as_ref()),
StringMatch::StartsWith(value) => ctx_value.starts_with(value),
StringMatch::EndsWith(value) => ctx_value.ends_with(value),
}
}
- ConditionMatch::IpAddrMask(value) => value.matches(&match key {
- EnvelopeKey::RemoteIp => envelope.remote_ip(),
- EnvelopeKey::LocalIp => envelope.local_ip(),
- _ => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
- }),
- ConditionMatch::UInt(value) => {
- *value
- == if key == &EnvelopeKey::Listener {
- envelope.listener_id()
- } else {
- debug_assert!(false, "Invalid value for UInt context key.");
- u16::MAX
- }
- }
- ConditionMatch::Int(value) => {
- *value
- == if key == &EnvelopeKey::Listener {
- envelope.priority()
- } else {
- debug_assert!(false, "Invalid value for UInt context key.");
- i16::MAX
- }
+ ConditionMatch::IpAddrMask(value) => {
+ value.matches(&envelope.key_as_ip(key))
}
+ ConditionMatch::UInt(value) => *value == envelope.key_as_int(key) as u16,
+ ConditionMatch::Int(value) => *value == envelope.key_as_int(key) as i16,
ConditionMatch::Lookup(lookup) => {
- if let Some(result) =
- lookup.contains(envelope.key_to_string(key).as_ref()).await
+ if let Some(result) = lookup.contains(envelope.key(key).as_ref()).await
{
result
} else {
return false;
}
}
- ConditionMatch::Regex(value) => {
- value.is_match(envelope.key_to_string(key).as_ref())
- }
+ ConditionMatch::Regex(value) => value.is_match(envelope.key(key).as_ref()),
} ^ not;
}
Condition::JumpIfTrue { positions } => {
@@ -145,7 +121,10 @@ impl Conditions {
matched
}
- pub async fn eval_and_capture(&self, envelope: &impl Envelope) -> Option<Vec<String>> {
+ pub async fn eval_and_capture(
+ &self,
+ envelope: &impl KeyLookup<Key = EnvelopeKey>,
+ ) -> Option<Vec<String>> {
let mut conditions = self.conditions.iter();
let mut matched = false;
let mut last_capture = vec![];
@@ -154,36 +133,18 @@ impl Conditions {
while let Some(rule) = conditions.next() {
match rule {
Condition::Match { key, value, not } => {
- let ctx_value = envelope.key_to_string(key);
+ let ctx_value = envelope.key(key);
matched = match value {
ConditionMatch::String(value) => match value {
StringMatch::Equal(value) => value.eq(ctx_value.as_ref()),
StringMatch::StartsWith(value) => ctx_value.starts_with(value),
StringMatch::EndsWith(value) => ctx_value.ends_with(value),
},
- ConditionMatch::IpAddrMask(value) => value.matches(&match key {
- EnvelopeKey::RemoteIp => envelope.remote_ip(),
- EnvelopeKey::LocalIp => envelope.local_ip(),
- _ => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
- }),
- ConditionMatch::UInt(value) => {
- *value
- == if key == &EnvelopeKey::Listener {
- envelope.listener_id()
- } else {
- debug_assert!(false, "Invalid value for UInt context key.");
- u16::MAX
- }
- }
- ConditionMatch::Int(value) => {
- *value
- == if key == &EnvelopeKey::Listener {
- envelope.priority()
- } else {
- debug_assert!(false, "Invalid value for UInt context key.");
- i16::MAX
- }
+ ConditionMatch::IpAddrMask(value) => {
+ value.matches(&envelope.key_as_ip(key))
}
+ ConditionMatch::UInt(value) => *value == envelope.key_as_int(key) as u16,
+ ConditionMatch::Int(value) => *value == envelope.key_as_int(key) as i16,
ConditionMatch::Lookup(lookup) => {
lookup.contains(ctx_value.as_ref()).await?
}
@@ -288,23 +249,23 @@ impl IpAddrMask {
}
}
-impl<'x> Captures<'x, DynValue> {
- pub fn into_value(self) -> Cow<'x, str> {
- self.value.apply(self.captures)
+impl<'x> Captures<'x, DynValue<EnvelopeKey>> {
+ pub fn into_value(self, keys: &'x impl KeyLookup<Key = EnvelopeKey>) -> Cow<'x, str> {
+ self.value.apply(self.captures, keys)
}
}
-impl<'x> Captures<'x, Option<DynValue>> {
- pub fn into_value(self) -> Option<Cow<'x, str>> {
- self.value.as_ref().map(|v| v.apply(self.captures))
+impl<'x> Captures<'x, Option<DynValue<EnvelopeKey>>> {
+ pub fn into_value(self, keys: &'x impl KeyLookup<Key = EnvelopeKey>) -> Option<Cow<'x, str>> {
+ self.value.as_ref().map(|v| v.apply(self.captures, keys))
}
}
impl<'x, T: ?Sized> Captures<'x, MaybeDynValue<T>> {
- pub fn into_value(self) -> Option<Arc<T>> {
+ pub fn into_value(self, keys: &impl KeyLookup<Key = EnvelopeKey>) -> Option<Arc<T>> {
match &self.value {
MaybeDynValue::Dynamic { eval, items } => {
- let r = eval.apply(self.captures);
+ let r = eval.apply(self.captures, keys);
match items.get(r.as_ref()) {
Some(value) => value.clone().into(),
@@ -326,12 +287,12 @@ impl<'x, T: ?Sized> Captures<'x, MaybeDynValue<T>> {
}
impl<'x, T: ?Sized> Captures<'x, Vec<MaybeDynValue<T>>> {
- pub fn into_value(self) -> Vec<Arc<T>> {
+ pub fn into_value(self, keys: &impl KeyLookup<Key = EnvelopeKey>) -> Vec<Arc<T>> {
let mut results = Vec::with_capacity(self.value.len());
for value in self.value.iter() {
match value {
MaybeDynValue::Dynamic { eval, items } => {
- let r = eval.apply_borrowed(&self.captures);
+ let r = eval.apply_borrowed(&self.captures, keys);
match items.get(r.as_ref()) {
Some(value) => {
results.push(value.clone());
@@ -357,10 +318,10 @@ impl<'x, T: ?Sized> Captures<'x, Vec<MaybeDynValue<T>>> {
}
impl<'x, T: ?Sized> Captures<'x, Option<MaybeDynValue<T>>> {
- pub fn into_value(self) -> Option<Arc<T>> {
+ pub fn into_value(self, keys: &impl KeyLookup<Key = EnvelopeKey>) -> Option<Arc<T>> {
match self.value.as_ref()? {
MaybeDynValue::Dynamic { eval, items } => {
- let r = eval.apply(self.captures);
+ let r = eval.apply(self.captures, keys);
match items.get(r.as_ref()) {
Some(value) => value.clone().into(),
None => {
diff --git a/crates/smtp/src/core/mod.rs b/crates/smtp/src/core/mod.rs
index 36b959fc..7f83de93 100644
--- a/crates/smtp/src/core/mod.rs
+++ b/crates/smtp/src/core/mod.rs
@@ -22,7 +22,6 @@
*/
use std::{
- borrow::Cow,
hash::Hash,
net::IpAddr,
sync::{atomic::AtomicU32, Arc},
@@ -50,8 +49,7 @@ use utils::{
use crate::{
config::{
- DkimSigner, EnvelopeKey, MailAuthConfig, QueueConfig, ReportConfig, SessionConfig,
- VerifyStrategy,
+ DkimSigner, MailAuthConfig, QueueConfig, ReportConfig, SessionConfig, VerifyStrategy,
},
inbound::auth::SaslToken,
outbound::{
@@ -279,37 +277,6 @@ impl Default for State {
}
}
-pub trait Envelope {
- fn local_ip(&self) -> IpAddr;
- fn remote_ip(&self) -> IpAddr;
- fn sender_domain(&self) -> &str;
- fn sender(&self) -> &str;
- fn rcpt_domain(&self) -> &str;
- fn rcpt(&self) -> &str;
- fn helo_domain(&self) -> &str;
- fn authenticated_as(&self) -> &str;
- fn mx(&self) -> &str;
- fn listener_id(&self) -> u16;
- fn priority(&self) -> i16;
-
- #[inline(always)]
- fn key_to_string(&self, key: &EnvelopeKey) -> Cow<'_, str> {
- match key {
- EnvelopeKey::Recipient => self.rcpt().into(),
- EnvelopeKey::RecipientDomain => self.rcpt_domain().into(),
- EnvelopeKey::Sender => self.sender().into(),
- EnvelopeKey::SenderDomain => self.sender_domain().into(),
- EnvelopeKey::Mx => self.mx().into(),
- EnvelopeKey::AuthenticatedAs => self.authenticated_as().into(),
- EnvelopeKey::HeloDomain => self.helo_domain().into(),
- EnvelopeKey::Listener => self.listener_id().to_string().into(),
- EnvelopeKey::RemoteIp => self.remote_ip().to_string().into(),
- EnvelopeKey::LocalIp => self.local_ip().to_string().into(),
- EnvelopeKey::Priority => self.priority().to_string().into(),
- }
- }
-}
-
impl VerifyStrategy {
#[inline(always)]
pub fn verify(&self) -> bool {
diff --git a/crates/smtp/src/core/params.rs b/crates/smtp/src/core/params.rs
index 2380adcc..79df57bb 100644
--- a/crates/smtp/src/core/params.rs
+++ b/crates/smtp/src/core/params.rs
@@ -44,7 +44,7 @@ impl<T: AsyncRead + AsyncWrite> Session<T> {
// Auth parameters
let ac = &self.core.session.config.auth;
- self.params.auth_directory = ac.directory.eval_and_capture(self).await.into_value();
+ self.params.auth_directory = ac.directory.eval_and_capture(self).await.into_value(self);
self.params.auth_require = *ac.require.eval(self).await;
self.params.auth_errors_max = *ac.errors_max.eval(self).await;
self.params.auth_errors_wait = *ac.errors_wait.eval(self).await;
diff --git a/crates/smtp/src/core/throttle.rs b/crates/smtp/src/core/throttle.rs
index 49536c67..783efdb3 100644
--- a/crates/smtp/src/core/throttle.rs
+++ b/crates/smtp/src/core/throttle.rs
@@ -24,7 +24,7 @@
use ::utils::listener::limiter::{ConcurrencyLimiter, RateLimiter};
use dashmap::mapref::entry::Entry;
use tokio::io::{AsyncRead, AsyncWrite};
-use utils::config::Rate;
+use utils::config::{KeyLookup, Rate};
use std::{
hash::{BuildHasher, Hash, Hasher},
@@ -34,7 +34,7 @@ use std::{
use crate::config::*;
-use super::{Envelope, Session};
+use super::Session;
#[derive(Debug)]
pub struct Limiter {
@@ -86,24 +86,31 @@ impl BuildHasher for ThrottleKeyHasherBuilder {
}
impl QueueQuota {
- pub fn new_key(&self, e: &impl Envelope) -> ThrottleKey {
+ pub fn new_key(&self, e: &impl KeyLookup<Key = EnvelopeKey>) -> ThrottleKey {
let mut hasher = blake3::Hasher::new();
if (self.keys & THROTTLE_RCPT) != 0 {
- hasher.update(e.rcpt().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::Recipient).as_bytes());
}
if (self.keys & THROTTLE_RCPT_DOMAIN) != 0 {
- hasher.update(e.rcpt_domain().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::RecipientDomain).as_bytes());
}
if (self.keys & THROTTLE_SENDER) != 0 {
- let sender = e.sender();
- hasher.update(if !sender.is_empty() { sender } else { "<>" }.as_bytes());
+ let sender = e.key(&EnvelopeKey::Sender);
+ hasher.update(
+ if !sender.is_empty() {
+ sender.as_ref()
+ } else {
+ "<>"
+ }
+ .as_bytes(),
+ );
}
if (self.keys & THROTTLE_SENDER_DOMAIN) != 0 {
- let sender_domain = e.sender_domain();
+ let sender_domain = e.key(&EnvelopeKey::SenderDomain);
hasher.update(
if !sender_domain.is_empty() {
- sender_domain
+ sender_domain.as_ref()
} else {
"<>"
}
@@ -126,24 +133,31 @@ impl QueueQuota {
}
impl Throttle {
- pub fn new_key(&self, e: &impl Envelope) -> ThrottleKey {
+ pub fn new_key(&self, e: &impl KeyLookup<Key = EnvelopeKey>) -> ThrottleKey {
let mut hasher = blake3::Hasher::new();
if (self.keys & THROTTLE_RCPT) != 0 {
- hasher.update(e.rcpt().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::Recipient).as_bytes());
}
if (self.keys & THROTTLE_RCPT_DOMAIN) != 0 {
- hasher.update(e.rcpt_domain().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::RecipientDomain).as_bytes());
}
if (self.keys & THROTTLE_SENDER) != 0 {
- let sender = e.sender();
- hasher.update(if !sender.is_empty() { sender } else { "<>" }.as_bytes());
+ let sender = e.key(&EnvelopeKey::Sender);
+ hasher.update(
+ if !sender.is_empty() {
+ sender.as_ref()
+ } else {
+ "<>"
+ }
+ .as_bytes(),
+ );
}
if (self.keys & THROTTLE_SENDER_DOMAIN) != 0 {
- let sender_domain = e.sender_domain();
+ let sender_domain = e.key(&EnvelopeKey::SenderDomain);
hasher.update(
if !sender_domain.is_empty() {
- sender_domain
+ sender_domain.as_ref()
} else {
"<>"
}
@@ -151,19 +165,19 @@ impl Throttle {
);
}
if (self.keys & THROTTLE_HELO_DOMAIN) != 0 {
- hasher.update(e.helo_domain().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::HeloDomain).as_bytes());
}
if (self.keys & THROTTLE_AUTH_AS) != 0 {
- hasher.update(e.authenticated_as().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::AuthenticatedAs).as_bytes());
}
if (self.keys & THROTTLE_LISTENER) != 0 {
- hasher.update(&e.listener_id().to_ne_bytes()[..]);
+ hasher.update(&e.key_as_int(&EnvelopeKey::Listener).to_ne_bytes()[..]);
}
if (self.keys & THROTTLE_MX) != 0 {
- hasher.update(e.mx().as_bytes());
+ hasher.update(e.key(&EnvelopeKey::Mx).as_bytes());
}
if (self.keys & THROTTLE_REMOTE_IP) != 0 {
- match &e.remote_ip() {
+ match &e.key_as_ip(&EnvelopeKey::RemoteIp) {
IpAddr::V4(ip) => {
hasher.update(&ip.octets()[..]);
}
@@ -173,7 +187,7 @@ impl Throttle {
}
}
if (self.keys & THROTTLE_LOCAL_IP) != 0 {
- match &e.local_ip() {
+ match &e.key_as_ip(&EnvelopeKey::LocalIp) {
IpAddr::V4(ip) => {
hasher.update(&ip.octets()[..]);
}
diff --git a/crates/smtp/src/inbound/data.rs b/crates/smtp/src/inbound/data.rs
index 0d94a50a..aee2d4bc 100644
--- a/crates/smtp/src/inbound/data.rs
+++ b/crates/smtp/src/inbound/data.rs
@@ -145,7 +145,7 @@ impl<T: AsyncWrite + AsyncRead + IsTls + Unpin> Session<T> {
// Verify ARC
let arc = *ac.arc.verify.eval(self).await;
- let arc_sealer = ac.arc.seal.eval_and_capture(self).await.into_value();
+ let arc_sealer = ac.arc.seal.eval_and_capture(self).await.into_value(self);
let arc_output = if arc.verify() || arc_sealer.is_some() {
let arc_output = self.core.resolvers.dns.verify_arc(&auth_message).await;
@@ -502,7 +502,7 @@ impl<T: AsyncWrite + AsyncRead + IsTls + Unpin> Session<T> {
// DKIM sign
let raw_message = edited_message.unwrap_or(raw_message);
- for signer in ac.dkim.sign.eval_and_capture(self).await.into_value() {
+ for signer in ac.dkim.sign.eval_and_capture(self).await.into_value(self) {
match signer.sign_chained(&[headers.as_ref(), &raw_message]) {
Ok(signature) => {
signature.write_header(&mut headers);
diff --git a/crates/smtp/src/inbound/mail.rs b/crates/smtp/src/inbound/mail.rs
index b0b0a5c5..db9a5063 100644
--- a/crates/smtp/src/inbound/mail.rs
+++ b/crates/smtp/src/inbound/mail.rs
@@ -172,13 +172,14 @@ impl<T: AsyncWrite + AsyncRead + Unpin + IsTls> Session<T> {
.rewrite
.eval_and_capture(self)
.await
- .into_value()
+ .into_value(self)
+ .map(|s| s.into_owned())
{
let mut mail_from = self.data.mail_from.as_mut().unwrap();
if new_address.contains('@') {
mail_from.address_lcase = new_address.to_lowercase();
mail_from.domain = mail_from.address_lcase.domain_part().to_string();
- mail_from.address = new_address.into_owned();
+ mail_from.address = new_address;
} else if new_address.is_empty() {
mail_from.address_lcase.clear();
mail_from.domain.clear();
diff --git a/crates/smtp/src/inbound/rcpt.rs b/crates/smtp/src/inbound/rcpt.rs
index 84002c57..2de78791 100644
--- a/crates/smtp/src/inbound/rcpt.rs
+++ b/crates/smtp/src/inbound/rcpt.rs
@@ -121,13 +121,14 @@ impl<T: AsyncWrite + AsyncRead + Unpin> Session<T> {
.rewrite
.eval_and_capture(self)
.await
- .into_value()
+ .into_value(self)
+ .map(|s| s.into_owned())
{
let mut rcpt = self.data.rcpt_to.last_mut().unwrap();
if new_address.contains('@') {
rcpt.address_lcase = new_address.to_lowercase();
rcpt.domain = rcpt.address_lcase.domain_part().to_string();
- rcpt.address = new_address.into_owned();
+ rcpt.address = new_address;
}
}
@@ -149,7 +150,7 @@ impl<T: AsyncWrite + AsyncRead + Unpin> Session<T> {
.directory
.eval_and_capture(self)
.await
- .into_value()
+ .into_value(self)
{
if let Ok(is_local_domain) = directory.is_local_domain(&rcpt.domain).await {
if is_local_domain {
diff --git a/crates/smtp/src/inbound/session.rs b/crates/smtp/src/inbound/session.rs
index b233c5bf..43abbeb4 100644
--- a/crates/smtp/src/inbound/session.rs
+++ b/crates/smtp/src/inbound/session.rs
@@ -21,7 +21,7 @@
* for more details.
*/
-use std::net::IpAddr;
+use std::net::{IpAddr, Ipv4Addr};
use smtp_proto::{
request::receiver::{
@@ -31,9 +31,12 @@ use smtp_proto::{
*,
};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
-use utils::config::ServerProtocol;
+use utils::config::{KeyLookup, ServerProtocol};
-use crate::core::{Envelope, Session, State};
+use crate::{
+ config::EnvelopeKey,
+ core::{Session, State},
+};
use super::{auth::SaslToken, IsTls};
@@ -392,75 +395,62 @@ impl<T: AsyncWrite + AsyncRead + Unpin> Session<T> {
}
}
-impl<T: AsyncRead + AsyncWrite> Envelope for Session<T> {
- #[inline(always)]
- fn local_ip(&self) -> IpAddr {
- self.data.local_ip
- }
-
- #[inline(always)]
- fn remote_ip(&self) -> IpAddr {
- self.data.remote_ip
- }
-
- #[inline(always)]
- fn sender_domain(&self) -> &str {
- self.data
- .mail_from
- .as_ref()
- .map(|a| a.domain.as_str())
- .unwrap_or_default()
- }
-
- #[inline(always)]
- fn sender(&self) -> &str {
- self.data
- .mail_from
- .as_ref()
- .map(|a| a.address_lcase.as_str())
- .unwrap_or_default()
- }
-
- #[inline(always)]
- fn rcpt_domain(&self) -> &str {
- self.data
- .rcpt_to
- .last()
- .map(|r| r.domain.as_str())
- .unwrap_or_default()
- }
-
- #[inline(always)]
- fn rcpt(&self) -> &str {
- self.data
- .rcpt_to
- .last()
- .map(|r| r.address_lcase.as_str())
- .unwrap_or_default()
- }
-
- #[inline(always)]
- fn helo_domain(&self) -> &str {
- self.data.helo_domain.as_str()
- }
+impl<T: AsyncRead + AsyncWrite> KeyLookup for Session<T> {
+ type Key = EnvelopeKey;
- #[inline(always)]
- fn authenticated_as(&self) -> &str {
- self.data.authenticated_as.as_str()
- }
-
- #[inline(always)]
- fn mx(&self) -> &str {
- ""
+ fn key(&self, key: &Self::Key) -> std::borrow::Cow<'_, str> {
+ match key {
+ EnvelopeKey::Recipient => self
+ .data
+ .rcpt_to
+ .last()
+ .map(|r| r.address_lcase.as_str())
+ .unwrap_or_default()
+ .into(),
+ EnvelopeKey::RecipientDomain => self
+ .data
+ .rcpt_to
+ .last()
+ .map(|r| r.domain.as_str())
+ .unwrap_or_default()
+ .into(),
+ EnvelopeKey::Sender => self
+ .data
+ .mail_from
+ .as_ref()
+ .map(|m| m.address_lcase.as_str())
+ .unwrap_or_default()
+ .into(),
+ EnvelopeKey::SenderDomain => self
+ .data
+ .mail_from
+ .as_ref()
+ .map(|m| m.domain.as_str())
+ .unwrap_or_default()
+ .into(),
+ EnvelopeKey::HeloDomain => self.data.helo_domain.as_str().into(),
+ EnvelopeKey::AuthenticatedAs => self.data.authenticated_as.as_str().into(),
+ EnvelopeKey::Listener => self.instance.id.as_str().into(),
+ EnvelopeKey::RemoteIp => self.data.remote_ip.to_string().into(),
+ EnvelopeKey::LocalIp => self.data.local_ip.to_string().into(),
+ EnvelopeKey::Priority => self.data.priority.to_string().into(),
+ EnvelopeKey::Mx => "".into(),
+ }
}
- #[inline(always)]
- fn listener_id(&self) -> u16 {
- self.instance.listener_id
+ fn key_as_int(&self, key: &Self::Key) -> i32 {
+ match key {
+ EnvelopeKey::Listener => self.instance.listener_id as i32,
+ EnvelopeKey::Priority => self.data.priority as i32,
+ _ => 0,
+ }
}
- #[inline(always)]
- fn priority(&self) -> i16 {
- self.data.priority
+ fn key_as_ip(&self, key: &Self::Key) -> IpAddr {
+ match key {
+ EnvelopeKey::RemoteIp => self.data.remote_ip,
+ EnvelopeKey::LocalIp => self.data.local_ip,
+ _ => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
+ }
}
}
diff --git a/crates/smtp/src/inbound/vrfy.rs b/crates/smtp/src/inbound/vrfy.rs
index ffd171a0..e79294bf 100644
--- a/crates/smtp/src/inbound/vrfy.rs
+++ b/crates/smtp/src/inbound/vrfy.rs
@@ -37,7 +37,7 @@ impl<T: AsyncWrite + AsyncRead + Unpin> Session<T> {
.directory
.eval_and_capture(self)
.await
- .into_value()
+ .into_value(self)
{
Some(address_lookup) if self.params.can_vrfy => {
match address_lookup.vrfy(&address.to_lowercase()).await {
@@ -98,7 +98,7 @@ impl<T: AsyncWrite + AsyncRead + Unpin> Session<T> {
.directory
.eval_and_capture(self)
.await
- .into_value()
+ .into_value(self)
{
Some(address_lookup) if self.params.can_expn => {
match address_lookup.expn(&address.to_lowercase()).await {
diff --git a/crates/smtp/src/outbound/lookup.rs b/crates/smtp/src/outbound/lookup.rs
index c5506a43..72778b8f 100644
--- a/crates/smtp/src/outbound/lookup.rs
+++ b/crates/smtp/src/outbound/lookup.rs
@@ -25,9 +25,11 @@ use std::net::IpAddr;
use mail_auth::MX;
use rand::{seq::SliceRandom, Rng};
+use utils::config::KeyLookup;
use crate::{
- core::{Envelope, SMTP},
+ config::EnvelopeKey,
+ core::SMTP,
queue::{Error, ErrorDetails, Status},
};
@@ -37,7 +39,7 @@ impl SMTP {
pub async fn resolve_host(
&self,
remote_host: &NextHop<'_>,
- envelope: &impl Envelope,
+ envelope: &impl KeyLookup<Key = EnvelopeKey>,
max_multihomed: usize,
) -> Result<(Option<IpAddr>, Vec<IpAddr>), Status<(), Error>> {
let remote_ips = self
@@ -100,7 +102,7 @@ impl SMTP {
} else {
Err(Status::TemporaryFailure(Error::DnsError(format!(
"No IP addresses found for {:?}.",
- envelope.mx()
+ envelope.key(&EnvelopeKey::Mx)
))))
}
}
diff --git a/crates/smtp/src/queue/mod.rs b/crates/smtp/src/queue/mod.rs
index d4275791..b80e87c5 100644
--- a/crates/smtp/src/queue/mod.rs
+++ b/crates/smtp/src/queue/mod.rs
@@ -31,9 +31,12 @@ use std::{
use serde::{Deserialize, Serialize};
use smtp_proto::Response;
-use utils::listener::limiter::{ConcurrencyLimiter, InFlight};
+use utils::{
+ config::KeyLookup,
+ listener::limiter::{ConcurrencyLimiter, InFlight},
+};
-use crate::core::{management, Envelope};
+use crate::{config::EnvelopeKey, core::management};
pub mod dsn;
pub mod manager;
@@ -241,49 +244,30 @@ impl<'x> SimpleEnvelope<'x> {
}
}
-impl<'x> Envelope for SimpleEnvelope<'x> {
- fn local_ip(&self) -> IpAddr {
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
- }
-
- fn remote_ip(&self) -> IpAddr {
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
- }
+impl<'x> KeyLookup for SimpleEnvelope<'x> {
+ type Key = EnvelopeKey;
- fn sender_domain(&self) -> &str {
- &self.message.return_path_domain
- }
-
- fn sender(&self) -> &str {
- &self.message.return_path_lcase
- }
-
- fn rcpt_domain(&self) -> &str {
- self.domain
- }
-
- fn rcpt(&self) -> &str {
- self.recipient
- }
-
- fn helo_domain(&self) -> &str {
- ""
- }
-
- fn authenticated_as(&self) -> &str {
- ""
- }
-
- fn mx(&self) -> &str {
- ""
+ fn key(&self, key: &Self::Key) -> std::borrow::Cow<'_, str> {
+ match key {
+ EnvelopeKey::Sender => self.message.return_path_lcase.as_str().into(),
+ EnvelopeKey::SenderDomain => self.message.return_path_domain.as_str().into(),
+ EnvelopeKey::Priority => self.message.priority.to_string().into(),
+ EnvelopeKey::Recipient => self.recipient.into(),
+ EnvelopeKey::RecipientDomain => self.domain.into(),
+ _ => "".into(),
+ }
}
- fn listener_id(&self) -> u16 {
- 0
+ fn key_as_int(&self, key: &Self::Key) -> i32 {
+ if matches!(key, EnvelopeKey::Priority) {
+ self.message.priority as i32
+ } else {
+ 0
+ }
}
- fn priority(&self) -> i16 {
- self.message.priority
+ fn key_as_ip(&self, _: &Self::Key) -> IpAddr {
+ IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
}
}
@@ -295,141 +279,86 @@ pub struct QueueEnvelope<'x> {
pub local_ip: IpAddr,
}
-impl<'x> Envelope for QueueEnvelope<'x> {
- fn local_ip(&self) -> IpAddr {
- self.local_ip
- }
-
- fn remote_ip(&self) -> IpAddr {
- self.remote_ip
- }
-
- fn sender_domain(&self) -> &str {
- &self.message.return_path_domain
- }
-
- fn sender(&self) -> &str {
- &self.message.return_path_lcase
- }
-
- fn rcpt_domain(&self) -> &str {
- self.domain
- }
+impl<'x> KeyLookup for QueueEnvelope<'x> {
+ type Key = EnvelopeKey;
- fn rcpt(&self) -> &str {
- ""
- }
-
- fn helo_domain(&self) -> &str {
- ""
- }
-
- fn authenticated_as(&self) -> &str {
- ""
- }
-
- fn mx(&self) -> &str {
- self.mx
+ fn key(&self, key: &Self::Key) -> std::borrow::Cow<'_, str> {
+ match key {
+ EnvelopeKey::Sender => self.message.return_path_lcase.as_str().into(),
+ EnvelopeKey::SenderDomain => self.message.return_path_domain.as_str().into(),
+ EnvelopeKey::RecipientDomain => self.domain.into(),
+ EnvelopeKey::Mx => self.mx.into(),
+ EnvelopeKey::Priority => self.message.priority.to_string().into(),
+ _ => "".into(),
+ }
}
- fn listener_id(&self) -> u16 {
- 0
+ fn key_as_int(&self, key: &Self::Key) -> i32 {
+ if matches!(key, EnvelopeKey::Priority) {
+ self.message.priority as i32
+ } else {
+ 0
+ }
}
- fn priority(&self) -> i16 {
- self.message.priority
+ fn key_as_ip(&self, key: &Self::Key) -> IpAddr {
+ match key {
+ EnvelopeKey::RemoteIp => self.remote_ip,
+ EnvelopeKey::LocalIp => self.local_ip,
+ _ => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
+ }
}
}
-impl Envelope for Message {
- fn local_ip(&self) -> IpAddr {
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
- }
-
- fn remote_ip(&self) -> IpAddr {
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
- }
+impl KeyLookup for Message {
+ type Key = EnvelopeKey;
- fn sender_domain(&self) -> &str {
- &self.return_path_domain
- }
-
- fn sender(&self) -> &str {
- &self.return_path_lcase
- }
-
- fn rcpt_domain(&self) -> &str {
- ""
- }
-
- fn rcpt(&self) -> &str {
- ""
- }
-
- fn helo_domain(&self) -> &str {
- ""
- }
-
- fn authenticated_as(&self) -> &str {
- ""
- }
-
- fn mx(&self) -> &str {
- ""
- }
-
- fn listener_id(&self) -> u16 {
- 0
+ fn key(&self, key: &Self::Key) -> std::borrow::Cow<'_, str> {
+ match key {
+ EnvelopeKey::Sender => self.return_path_lcase.as_str().into(),
+ EnvelopeKey::SenderDomain => self.return_path_domain.as_str().into(),
+ EnvelopeKey::Priority => self.priority.to_string().into(),
+ _ => "".into(),
+ }
}
- fn priority(&self) -> i16 {
- self.priority
- }
-}
-
-impl Envelope for &str {
- fn local_ip(&self) -> IpAddr {
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
+ fn key_as_int(&self, key: &Self::Key) -> i32 {
+ if matches!(key, EnvelopeKey::Priority) {
+ self.priority as i32
+ } else {
+ 0
+ }
}
- fn remote_ip(&self) -> IpAddr {
+ fn key_as_ip(&self, _: &Self::Key) -> IpAddr {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
}
+}
- fn sender_domain(&self) -> &str {
- ""
- }
-
- fn sender(&self) -> &str {
- ""
- }
-
- fn rcpt_domain(&self) -> &str {
- self
- }
-
- fn rcpt(&self) -> &str {
- ""
- }
+pub struct RecipientDomain<'x>(&'x str);
- fn helo_domain(&self) -> &str {
- ""
+impl<'x> RecipientDomain<'x> {
+ pub fn new(domain: &'x str) -> Self {
+ Self(domain)
}
+}
- fn authenticated_as(&self) -> &str {
- ""
- }
+impl<'x> KeyLookup for RecipientDomain<'x> {
+ type Key = EnvelopeKey;
- fn mx(&self) -> &str {
- ""
+ fn key(&self, key: &Self::Key) -> std::borrow::Cow<'_, str> {
+ match key {
+ EnvelopeKey::RecipientDomain => self.0.into(),
+ _ => "".into(),
+ }
}
- fn listener_id(&self) -> u16 {
+ fn key_as_int(&self, _: &Self::Key) -> i32 {
0
}
- fn priority(&self) -> i16 {
- 0
+ fn key_as_ip(&self, _: &Self::Key) -> IpAddr {
+ IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
}
}
diff --git a/crates/smtp/src/queue/quota.rs b/crates/smtp/src/queue/quota.rs
index affad4a6..377a9c14 100644
--- a/crates/smtp/src/queue/quota.rs
+++ b/crates/smtp/src/queue/quota.rs
@@ -24,10 +24,11 @@
use std::sync::{atomic::Ordering, Arc};
use dashmap::mapref::entry::Entry;
+use utils::config::KeyLookup;
use crate::{
- config::QueueQuota,
- core::{Envelope, QueueCore},
+ config::{EnvelopeKey, QueueQuota},
+ core::QueueCore,
};
use super::{Message, QuotaLimiter, SimpleEnvelope, Status, UsedQuota};
@@ -93,7 +94,7 @@ impl QueueCore {
async fn reserve_quota(
&self,
quota: &QueueQuota,
- envelope: &impl Envelope,
+ envelope: &impl KeyLookup<Key = EnvelopeKey>,
size: usize,
id: u64,
refs: &mut Vec<UsedQuota>,
diff --git a/crates/smtp/src/queue/throttle.rs b/crates/smtp/src/queue/throttle.rs
index 3fae6d2d..0699f78b 100644
--- a/crates/smtp/src/queue/throttle.rs
+++ b/crates/smtp/src/queue/throttle.rs
@@ -24,11 +24,14 @@
use std::time::Instant;
use dashmap::mapref::entry::Entry;
-use utils::listener::limiter::{ConcurrencyLimiter, InFlight, RateLimiter};
+use utils::{
+ config::KeyLookup,
+ listener::limiter::{ConcurrencyLimiter, InFlight, RateLimiter},
+};
use crate::{
- config::Throttle,
- core::{throttle::Limiter, Envelope, QueueCore},
+ config::{EnvelopeKey, Throttle},
+ core::{throttle::Limiter, QueueCore},
};
use super::{Domain, Status};
@@ -43,7 +46,7 @@ impl QueueCore {
pub async fn is_allowed(
&self,
throttle: &Throttle,
- envelope: &impl Envelope,
+ envelope: &impl KeyLookup<Key = EnvelopeKey>,
in_flight: &mut Vec<InFlight>,
span: &tracing::Span,
) -> Result<(), Error> {
diff --git a/crates/smtp/src/reporting/dmarc.rs b/crates/smtp/src/reporting/dmarc.rs
index cd0c19cb..4024c565 100644
--- a/crates/smtp/src/reporting/dmarc.rs
+++ b/crates/smtp/src/reporting/dmarc.rs
@@ -40,7 +40,7 @@ use tokio::{
use crate::{
config::AggregateFrequency,
core::{Session, SMTP},
- queue::{DomainPart, InstantFromTimestamp, Schedule},
+ queue::{DomainPart, InstantFromTimestamp, RecipientDomain, Schedule},
};
use super::{
@@ -376,25 +376,50 @@ impl GenerateDmarcReport for Arc<SMTP> {
.with_date_range_begin(path.created)
.with_date_range_end(deliver_at)
.with_report_id(format!("{}_{}", domain.policy, path.created))
- .with_email(handle.block_on(config.address.eval(&domain.inner.as_str())));
- if let Some(org_name) = handle.block_on(config.org_name.eval(&domain.inner.as_str())) {
+ .with_email(
+ handle.block_on(
+ config
+ .address
+ .eval(&RecipientDomain::new(domain.inner.as_str())),
+ ),
+ );
+ if let Some(org_name) = handle.block_on(
+ config
+ .org_name
+ .eval(&RecipientDomain::new(domain.inner.as_str())),
+ ) {
report = report.with_org_name(org_name);
}
- if let Some(contact_info) =
- handle.block_on(config.contact_info.eval(&domain.inner.as_str()))
- {
+ if let Some(contact_info) = handle.block_on(
+ config
+ .contact_info
+ .eval(&RecipientDomain::new(domain.inner.as_str())),
+ ) {
report = report.with_extra_contact_info(contact_info);
}
for (record, count) in record_map {
report.add_record(record.with_count(count));
}
- let from_addr = handle.block_on(config.address.eval(&domain.inner.as_str()));
+ let from_addr = handle.block_on(
+ config
+ .address
+ .eval(&RecipientDomain::new(domain.inner.as_str())),
+ );
let mut message = Vec::with_capacity(path.size);
let _ = report.write_rfc5322(
- handle.block_on(core.report.config.submitter.eval(&domain.inner.as_str())),
+ handle.block_on(
+ core.report
+ .config
+ .submitter
+ .eval(&RecipientDomain::new(domain.inner.as_str())),
+ ),
(
handle
- .block_on(config.name.eval(&domain.inner.as_str()))
+ .block_on(
+ config
+ .name
+ .eval(&RecipientDomain::new(domain.inner.as_str())),
+ )
.as_str(),
from_addr.as_str(),
),
@@ -432,7 +457,7 @@ impl Scheduler {
.config
.dmarc_aggregate
.max_size
- .eval(&event.domain.as_str())
+ .eval(&RecipientDomain::new(event.domain.as_str()))
.await;
let policy = event.dmarc_record.to_hash();
diff --git a/crates/smtp/src/reporting/mod.rs b/crates/smtp/src/reporting/mod.rs
index d562a43a..0c13ef9c 100644
--- a/crates/smtp/src/reporting/mod.rs
+++ b/crates/smtp/src/reporting/mod.rs
@@ -182,7 +182,7 @@ impl Message {
bytes: &[u8],
span: &tracing::Span,
) -> Option<Vec<u8>> {
- let signers = config.eval_and_capture(self).await.into_value();
+ let signers = config.eval_and_capture(self).await.into_value(self);
if !signers.is_empty() {
let mut headers = Vec::with_capacity(64);
for signer in signers.iter() {
diff --git a/crates/smtp/src/reporting/scheduler.rs b/crates/smtp/src/reporting/scheduler.rs
index 50b8ded4..c63a34da 100644
--- a/crates/smtp/src/reporting/scheduler.rs
+++ b/crates/smtp/src/reporting/scheduler.rs
@@ -47,7 +47,7 @@ use tokio::{
use crate::{
config::AggregateFrequency,
core::{management::ReportRequest, worker::SpawnCleanup, ReportCore, SMTP},
- queue::{InstantFromTimestamp, Schedule},
+ queue::{InstantFromTimestamp, RecipientDomain, Schedule},
};
use super::{dmarc::GenerateDmarcReport, tls::GenerateTlsReport, Event};
@@ -198,8 +198,23 @@ impl SMTP {
};
// Build base path
- let mut path = self.report.config.path.eval(&domain).await.clone();
- path.push((policy % *self.report.config.hash.eval(&domain).await).to_string());
+ let mut path = self
+ .report
+ .config
+ .path
+ .eval(&RecipientDomain::new(domain))
+ .await
+ .clone();
+ path.push(
+ (policy
+ % *self
+ .report
+ .config
+ .hash
+ .eval(&RecipientDomain::new(domain))
+ .await)
+ .to_string(),
+ );
let _ = fs::create_dir(&path).await;
// Build filename
diff --git a/crates/smtp/src/reporting/tls.rs b/crates/smtp/src/reporting/tls.rs
index 6f5e9a47..c3c578c1 100644
--- a/crates/smtp/src/reporting/tls.rs
+++ b/crates/smtp/src/reporting/tls.rs
@@ -42,7 +42,7 @@ use crate::{
config::AggregateFrequency,
core::SMTP,
outbound::mta_sts::{Mode, MxPattern},
- queue::{InstantFromTimestamp, Schedule},
+ queue::{InstantFromTimestamp, RecipientDomain, Schedule},
USER_AGENT,
};
@@ -93,14 +93,18 @@ impl GenerateTlsReport for Arc<SMTP> {
let config = &core.report.config.tls;
let mut report = TlsReport {
organization_name: handle
- .block_on(config.org_name.eval(&domain.as_str()))
+ .block_on(config.org_name.eval(&RecipientDomain::new(domain.as_str())))
.clone(),
date_range: DateRange {
start_datetime: DateTime::from_timestamp(path.created as i64),
end_datetime: DateTime::from_timestamp(deliver_at as i64),
},
contact_info: handle
- .block_on(config.contact_info.eval(&domain.as_str()))
+ .block_on(
+ config
+ .contact_info
+ .eval(&RecipientDomain::new(domain.as_str())),
+ )
.clone(),
report_id: format!(
"{}_{}",
@@ -241,13 +245,21 @@ impl GenerateTlsReport for Arc<SMTP> {
// Deliver report over SMTP
if !rcpts.is_empty() {
- let from_addr = handle.block_on(config.address.eval(&domain.as_str()));
+ let from_addr =
+ handle.block_on(config.address.eval(&RecipientDomain::new(domain.as_str())));
let mut message = Vec::with_capacity(path.size);
let _ = report.write_rfc5322_from_bytes(
&domain,
- handle.block_on(core.report.config.submitter.eval(&domain.as_str())),
+ handle.block_on(
+ core.report
+ .config
+ .submitter
+ .eval(&RecipientDomain::new(domain.as_str())),
+ ),
(
- handle.block_on(config.name.eval(&domain.as_str())).as_str(),
+ handle
+ .block_on(config.name.eval(&RecipientDomain::new(domain.as_str())))
+ .as_str(),
from_addr.as_str(),
),
rcpts.iter().copied(),
@@ -283,7 +295,7 @@ impl Scheduler {
.config
.tls
.max_size
- .eval(&event.domain.as_str())
+ .eval(&RecipientDomain::new(event.domain.as_str()))
.await;
let policy_hash = event.policy.to_hash();
diff --git a/crates/utils/src/config/dynvalue.rs b/crates/utils/src/config/dynvalue.rs
index f1c473f1..ec0a03ea 100644
--- a/crates/utils/src/config/dynvalue.rs
+++ b/crates/utils/src/config/dynvalue.rs
@@ -25,10 +25,10 @@ use std::borrow::Cow;
use super::{
utils::{AsKey, ParseValue},
- DynValue,
+ DynValue, KeyLookup,
};
-impl ParseValue for DynValue {
+impl<T: ParseValue> ParseValue for DynValue<T> {
#[allow(clippy::while_let_on_iterator)]
fn parse_value(key: impl AsKey, value: &str) -> super::Result<Self> {
let mut items = vec![];
@@ -38,35 +38,66 @@ impl ParseValue for DynValue {
while let Some(&ch) = iter.next() {
if ch == b'$' && matches!(iter.peek(), Some(b'{')) {
iter.next();
- if matches!(iter.peek(), Some(ch) if ch.is_ascii_digit()) {
- if !buf.is_empty() {
- items.push(DynValue::String(String::from_utf8(buf).unwrap()));
- buf = vec![];
+ match iter.peek() {
+ Some(ch) if **ch == b'{' => {
+ buf.push(b'$');
+ while let Some(&ch) = iter.next() {
+ if ch == b'}' {
+ break;
+ } else {
+ buf.push(ch);
+ }
+ }
}
-
- while let Some(&ch) = iter.next() {
+ Some(ch) => {
+ if !buf.is_empty() {
+ items.push(DynValue::String(String::from_utf8(buf).unwrap()));
+ buf = vec![];
+ }
if ch.is_ascii_digit() {
- buf.push(ch);
- } else if ch == b'}' && !buf.is_empty() {
- let str_num = std::str::from_utf8(&buf).unwrap();
- items.push(DynValue::Position(str_num.parse().map_err(|_| {
- format!(
- "Failed to parse position {str_num:?} in value {value:?} for key {}",
- key.as_key()
- )
- })?));
- buf.clear();
- break;
+ while let Some(&ch) = iter.next() {
+ if ch.is_ascii_digit() {
+ buf.push(ch);
+ } else if ch == b'}' && !buf.is_empty() {
+ let str_num = std::str::from_utf8(&buf).unwrap();
+ items.push(DynValue::Position(str_num.parse().map_err(|_| {
+ format!(
+ "Failed to parse position {str_num:?} in value {value:?} for key {}",
+ key.as_key()
+ )
+ })?));
+ buf.clear();
+ break;
+ } else {
+ return Err(format!(
+ "Invalid dynamic string {value:?} for key {}",
+ key.as_key()
+ ));
+ }
+ }
} else {
- return Err(format!(
- "Invalid dynamic string {value:?} for key {}",
- key.as_key()
- ));
+ while let Some(&ch) = iter.next() {
+ if ch == b'}' {
+ if !buf.is_empty() {
+ items.push(DynValue::Key(T::parse_value(
+ key.clone(),
+ std::str::from_utf8(&buf).unwrap_or_default(),
+ )?));
+ buf.clear();
+ break;
+ } else {
+ return Err(format!(
+ "Invalid dynamic string {value:?} for key {}",
+ key.as_key()
+ ));
+ }
+ } else {
+ buf.push(ch);
+ }
+ }
}
}
- } else {
- buf.push(b'$');
- buf.push(b'{');
+ None => {}
}
} else {
buf.push(ch);
@@ -90,8 +121,12 @@ impl ParseValue for DynValue {
}
}
-impl DynValue {
- pub fn apply(&self, captures: Vec<String>) -> Cow<str> {
+impl<T: ParseValue> DynValue<T> {
+ pub fn apply<'x, 'y: 'x>(
+ &'x self,
+ captures: Vec<String>,
+ keys: &'y impl KeyLookup<Key = T>,
+ ) -> Cow<'x, str> {
match self {
DynValue::String(value) => Cow::Borrowed(value.as_str()),
DynValue::Position(pos) => captures
@@ -110,16 +145,22 @@ impl DynValue {
result.push_str(capture);
}
}
+ DynValue::Key(key) => result.push_str(keys.key(key).as_ref()),
DynValue::List(_) => unreachable!(),
}
}
Cow::Owned(result)
}
+ DynValue::Key(key) => keys.key(key),
}
}
- pub fn apply_borrowed<'x, 'y: 'x>(&'x self, captures: &'y [String]) -> Cow<'x, str> {
+ pub fn apply_borrowed<'x, 'y: 'x>(
+ &'x self,
+ captures: &'y [String],
+ keys: &'y impl KeyLookup<Key = T>,
+ ) -> Cow<'x, str> {
match self {
DynValue::String(value) => Cow::Borrowed(value.as_str()),
DynValue::Position(pos) => captures
@@ -137,12 +178,14 @@ impl DynValue {
result.push_str(capture);
}
}
+ DynValue::Key(key) => result.push_str(keys.key(key).as_ref()),
DynValue::List(_) => unreachable!(),
}
}
Cow::Owned(result)
}
+ DynValue::Key(key) => keys.key(key),
}
}
}
diff --git a/crates/utils/src/config/mod.rs b/crates/utils/src/config/mod.rs
index d45cbc68..217354a2 100644
--- a/crates/utils/src/config/mod.rs
+++ b/crates/utils/src/config/mod.rs
@@ -27,13 +27,21 @@ pub mod listener;
pub mod parser;
pub mod utils;
-use std::{collections::BTreeMap, fmt::Display, net::SocketAddr, time::Duration};
+use std::{
+ borrow::Cow,
+ collections::BTreeMap,
+ fmt::Display,
+ net::{IpAddr, Ipv4Addr, SocketAddr},
+ time::Duration,
+};
use rustls::ServerConfig;
use tokio::net::TcpSocket;
use crate::{failed, UnwrapFailure};
+use self::utils::ParseValue;
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config {
pub keys: BTreeMap<String, String>,
@@ -76,10 +84,35 @@ pub enum ServerProtocol {
}
#[derive(Debug, Clone)]
-pub enum DynValue {
+pub enum DynValue<T: ParseValue> {
String(String),
Position(usize),
- List(Vec<DynValue>),
+ Key(T),
+ List(Vec<DynValue<T>>),
+}
+
+pub trait KeyLookup {
+ type Key: ParseValue;
+
+ fn key(&self, key: &Self::Key) -> Cow<'_, str>;
+ fn key_as_int(&self, key: &Self::Key) -> i32;
+ fn key_as_ip(&self, key: &Self::Key) -> IpAddr;
+}
+
+impl KeyLookup for () {
+ type Key = String;
+
+ fn key(&self, _: &Self::Key) -> Cow<'_, str> {
+ "".into()
+ }
+
+ fn key_as_int(&self, _: &Self::Key) -> i32 {
+ 0
+ }
+
+ fn key_as_ip(&self, _: &Self::Key) -> IpAddr {
+ IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
+ }
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
diff --git a/tests/resources/smtp/config/rules-dynvalue.toml b/tests/resources/smtp/config/rules-dynvalue.toml
index 36951bac..eafdad61 100644
--- a/tests/resources/smtp/config/rules-dynvalue.toml
+++ b/tests/resources/smtp/config/rules-dynvalue.toml
@@ -24,7 +24,7 @@ test = [
{if = "rcpt-domain", starts-with = "foo", then = "${0}${{0}}"},
{else = false}
]
-expect = "foo.example.org${{0}}"
+expect = "foo.example.org${0}"
[eval."regex"]
test = [
@@ -40,6 +40,13 @@ test = [
]
expect = "user@foo.example.org"
+[eval."envelope-match"]
+test = [
+ {if = "authenticated-as", matches = "^([^.]+)@(.+)$", then = "rcpt ${rcpt} listener ${listener} ip ${local-ip} priority ${priority}"},
+ {else = false}
+]
+expect = "rcpt user@foo.example.org listener 123 ip 192.168.9.3 priority -4"
+
[eval."static-match"]
test = [
{if = "authenticated-as", matches = "^([^.]+)@(.+)$", then = "hello world"},
@@ -64,6 +71,11 @@ type = "memory"
[directory."list_foo".lookup]
domains = ["foo"]
+[directory."list_123"]
+type = "memory"
+[directory."list_123".lookup]
+domains = ["123"]
+
[maybe-eval."dyn_mx"]
test = [
{if = "mx", matches = "([^.]+)\.(.+)$", then = "list_${1}"},
@@ -86,3 +98,7 @@ expect = "mx"
test = "list_foo"
expect = "foo"
+[maybe-eval."dyn_123"]
+test = "list_${listener}"
+expect = "123"
+
diff --git a/tests/src/smtp/config.rs b/tests/src/smtp/config.rs
index a1b0b13d..b441b994 100644
--- a/tests/src/smtp/config.rs
+++ b/tests/src/smtp/config.rs
@@ -21,22 +21,26 @@
* for more details.
*/
-use std::{borrow::Cow, fs, net::IpAddr, path::PathBuf, sync::Arc, time::Duration};
+use std::{
+ borrow::Cow,
+ fs,
+ net::{IpAddr, Ipv4Addr},
+ path::PathBuf,
+ sync::Arc,
+ time::Duration,
+};
use tokio::net::TcpSocket;
-use utils::config::{Config, DynValue, Listener, Rate, Server, ServerProtocol};
+use utils::config::{Config, DynValue, KeyLookup, Listener, Rate, Server, ServerProtocol};
use ahash::{AHashMap, AHashSet};
use directory::{config::ConfigDirectory, Lookup};
-use smtp::{
- config::{
- condition::ConfigCondition, if_block::ConfigIf, throttle::ConfigThrottle, Condition,
- ConditionMatch, Conditions, ConfigContext, EnvelopeKey, IfBlock, IfThen, IpAddrMask,
- StringMatch, Throttle, THROTTLE_AUTH_AS, THROTTLE_REMOTE_IP, THROTTLE_SENDER_DOMAIN,
- },
- core::Envelope,
+use smtp::config::{
+ condition::ConfigCondition, if_block::ConfigIf, throttle::ConfigThrottle, Condition,
+ ConditionMatch, Conditions, ConfigContext, EnvelopeKey, IfBlock, IfThen, IpAddrMask,
+ StringMatch, Throttle, THROTTLE_AUTH_AS, THROTTLE_REMOTE_IP, THROTTLE_SENDER_DOMAIN,
};
use super::add_test_certs;
@@ -597,7 +601,7 @@ async fn eval_dynvalue() {
for test_name in config.sub_keys("eval") {
//println!("============= Testing {:?} ==================", key);
let if_block = config
- .parse_if_block::<Option<DynValue>>(
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
("eval", test_name, "test"),
&context,
&[
@@ -621,7 +625,10 @@ async fn eval_dynvalue() {
.map(Cow::Owned);
assert_eq!(
- if_block.eval_and_capture(&envelope).await.into_value(),
+ if_block
+ .eval_and_capture(&envelope)
+ .await
+ .into_value(&envelope),
expected,
"failed for test {test_name:?}"
);
@@ -630,7 +637,7 @@ async fn eval_dynvalue() {
for test_name in config.sub_keys("maybe-eval") {
//println!("============= Testing {:?} ==================", key);
let if_block = config
- .parse_if_block::<Option<DynValue>>(
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
("maybe-eval", test_name, "test"),
&context,
&[
@@ -661,7 +668,7 @@ async fn eval_dynvalue() {
assert!(if_block
.eval_and_capture(&envelope)
.await
- .into_value()
+ .into_value(&envelope)
.unwrap()
.is_local_domain(expected)
.await
@@ -669,49 +676,39 @@ async fn eval_dynvalue() {
}
}
-impl Envelope for TestEnvelope {
- fn local_ip(&self) -> IpAddr {
- self.local_ip
- }
-
- fn remote_ip(&self) -> IpAddr {
- self.remote_ip
- }
-
- fn sender_domain(&self) -> &str {
- self.sender_domain.as_str()
- }
-
- fn sender(&self) -> &str {
- self.sender.as_str()
- }
-
- fn rcpt_domain(&self) -> &str {
- self.rcpt_domain.as_str()
- }
-
- fn rcpt(&self) -> &str {
- self.rcpt.as_str()
- }
-
- fn helo_domain(&self) -> &str {
- self.helo_domain.as_str()
- }
-
- fn authenticated_as(&self) -> &str {
- self.authenticated_as.as_str()
- }
-
- fn mx(&self) -> &str {
- self.mx.as_str()
+impl KeyLookup for TestEnvelope {
+ type Key = EnvelopeKey;
+
+ fn key(&self, key: &Self::Key) -> std::borrow::Cow<'_, str> {
+ match key {
+ EnvelopeKey::Recipient => self.rcpt.as_str().into(),
+ EnvelopeKey::RecipientDomain => self.rcpt_domain.as_str().into(),
+ EnvelopeKey::Sender => self.sender.as_str().into(),
+ EnvelopeKey::SenderDomain => self.sender_domain.as_str().into(),
+ EnvelopeKey::AuthenticatedAs => self.authenticated_as.as_str().into(),
+ EnvelopeKey::Listener => self.listener_id.to_string().into(),
+ EnvelopeKey::RemoteIp => self.remote_ip.to_string().into(),
+ EnvelopeKey::LocalIp => self.local_ip.to_string().into(),
+ EnvelopeKey::Priority => self.priority.to_string().into(),
+ EnvelopeKey::Mx => self.mx.as_str().into(),
+ EnvelopeKey::HeloDomain => self.helo_domain.as_str().into(),
+ }
}
- fn listener_id(&self) -> u16 {
- self.listener_id
+ fn key_as_int(&self, key: &Self::Key) -> i32 {
+ match key {
+ EnvelopeKey::Priority => self.priority as i32,
+ EnvelopeKey::Listener => self.listener_id as i32,
+ _ => todo!(),
+ }
}
- fn priority(&self) -> i16 {
- self.priority
+ fn key_as_ip(&self, key: &Self::Key) -> IpAddr {
+ match key {
+ EnvelopeKey::RemoteIp => self.remote_ip,
+ EnvelopeKey::LocalIp => self.local_ip,
+ _ => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
+ }
}
}
diff --git a/tests/src/smtp/inbound/auth.rs b/tests/src/smtp/inbound/auth.rs
index 013cccd8..53c8a37c 100644
--- a/tests/src/smtp/inbound/auth.rs
+++ b/tests/src/smtp/inbound/auth.rs
@@ -30,7 +30,7 @@ use crate::smtp::{
ParseTestConfig, TestConfig,
};
use smtp::{
- config::ConfigContext,
+ config::{ConfigContext, EnvelopeKey},
core::{Session, State, SMTP},
};
@@ -68,7 +68,7 @@ async fn auth() {
.parse_if(&ctx);
config.directory = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 'local'},
{else = false}]"
- .parse_if::<Option<DynValue>>(&ctx)
+ .parse_if::<Option<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.directory.directories, "", "")
.unwrap();
config.errors_max = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 2},
diff --git a/tests/src/smtp/inbound/dmarc.rs b/tests/src/smtp/inbound/dmarc.rs
index eadc1eac..dbc22277 100644
--- a/tests/src/smtp/inbound/dmarc.rs
+++ b/tests/src/smtp/inbound/dmarc.rs
@@ -42,7 +42,9 @@ use crate::smtp::{
ParseTestConfig, TestConfig, TestSMTP,
};
use smtp::{
- config::{AggregateFrequency, ConfigContext, IfBlock, MaybeDynValue, VerifyStrategy},
+ config::{
+ AggregateFrequency, ConfigContext, EnvelopeKey, IfBlock, MaybeDynValue, VerifyStrategy,
+ },
core::{Session, SMTP},
};
@@ -168,15 +170,15 @@ async fn dmarc() {
let mut config = &mut core.report.config;
config.spf.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
config.dmarc.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
config.dkim.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
diff --git a/tests/src/smtp/inbound/rewrite.rs b/tests/src/smtp/inbound/rewrite.rs
index eb895f58..57133a3b 100644
--- a/tests/src/smtp/inbound/rewrite.rs
+++ b/tests/src/smtp/inbound/rewrite.rs
@@ -113,7 +113,11 @@ async fn address_rewrite() {
.map_if_block(&ctx.scripts, "session.mail.script", "script")
.unwrap();
config.mail.rewrite = settings
- .parse_if_block::<Option<DynValue>>("session.mail.rewrite", &ctx, &available_keys)
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
+ "session.mail.rewrite",
+ &ctx,
+ &available_keys,
+ )
.unwrap()
.unwrap_or_default();
config.rcpt.script = settings
@@ -123,7 +127,11 @@ async fn address_rewrite() {
.map_if_block(&ctx.scripts, "session.rcpt.script", "script")
.unwrap();
config.rcpt.rewrite = settings
- .parse_if_block::<Option<DynValue>>("session.rcpt.rewrite", &ctx, &available_keys)
+ .parse_if_block::<Option<DynValue<EnvelopeKey>>>(
+ "session.rcpt.rewrite",
+ &ctx,
+ &available_keys,
+ )
.unwrap()
.unwrap_or_default();
config.rcpt.relay = IfBlock::new(true);
diff --git a/tests/src/smtp/inbound/sign.rs b/tests/src/smtp/inbound/sign.rs
index 6da52cc4..3a0f6aab 100644
--- a/tests/src/smtp/inbound/sign.rs
+++ b/tests/src/smtp/inbound/sign.rs
@@ -36,7 +36,9 @@ use crate::smtp::{
ParseTestConfig, TestConfig, TestSMTP,
};
use smtp::{
- config::{auth::ConfigAuth, ConfigContext, IfBlock, MaybeDynValue, VerifyStrategy},
+ config::{
+ auth::ConfigAuth, ConfigContext, EnvelopeKey, IfBlock, MaybeDynValue, VerifyStrategy,
+ },
core::{Session, SMTP},
};
@@ -174,11 +176,11 @@ async fn sign_and_seal() {
config.arc.verify = config.spf.verify_ehlo.clone();
config.dmarc.verify = config.spf.verify_ehlo.clone();
config.dkim.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
config.arc.seal = "'ed'"
- .parse_if::<Option<DynValue>>(&ctx)
+ .parse_if::<Option<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.sealers, "", "")
.unwrap();
diff --git a/tests/src/smtp/lookup/sql.rs b/tests/src/smtp/lookup/sql.rs
index 8a672074..ed1fe51b 100644
--- a/tests/src/smtp/lookup/sql.rs
+++ b/tests/src/smtp/lookup/sql.rs
@@ -35,7 +35,7 @@ use crate::{
},
};
use smtp::{
- config::{ConfigContext, IfBlock},
+ config::{ConfigContext, EnvelopeKey, IfBlock},
core::{Session, SMTP},
};
@@ -115,7 +115,7 @@ async fn lookup_sql() {
// Enable AUTH
let mut config = &mut core.session.config.auth;
config.directory = r"'sql'"
- .parse_if::<Option<DynValue>>(&ctx)
+ .parse_if::<Option<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.directory.directories, "", "")
.unwrap();
config.mechanisms = IfBlock::new(AUTH_PLAIN | AUTH_LOGIN);
@@ -124,7 +124,7 @@ async fn lookup_sql() {
// Enable VRFY/EXPN/RCPT
let mut config = &mut core.session.config.rcpt;
config.directory = r"'sql'"
- .parse_if::<Option<DynValue>>(&ctx)
+ .parse_if::<Option<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.directory.directories, "", "")
.unwrap();
config.relay = IfBlock::new(false);
diff --git a/tests/src/smtp/lookup/utils.rs b/tests/src/smtp/lookup/utils.rs
index 5bd76478..576380c4 100644
--- a/tests/src/smtp/lookup/utils.rs
+++ b/tests/src/smtp/lookup/utils.rs
@@ -37,6 +37,7 @@ use smtp::{
lookup::ToNextHop,
mta_sts::{Mode, MxPattern, Policy},
},
+ queue::RecipientDomain,
};
use crate::smtp::TestConfig;
@@ -75,7 +76,11 @@ async fn lookup_ip() {
// Ipv4 strategy
core.queue.config.ip_strategy = IfBlock::new(IpLookupStrategy::Ipv4thenIpv6);
let (source_ips, remote_ips) = core
- .resolve_host(&NextHop::MX("mx.foobar.org"), &"envelope", 2)
+ .resolve_host(
+ &NextHop::MX("mx.foobar.org"),
+ &RecipientDomain::new("envelope"),
+ 2,
+ )
.await
.unwrap();
assert!(ipv4.contains(&match source_ips.unwrap() {
@@ -87,7 +92,11 @@ async fn lookup_ip() {
// Ipv6 strategy
core.queue.config.ip_strategy = IfBlock::new(IpLookupStrategy::Ipv6thenIpv4);
let (source_ips, remote_ips) = core
- .resolve_host(&NextHop::MX("mx.foobar.org"), &"envelope", 2)
+ .resolve_host(
+ &NextHop::MX("mx.foobar.org"),
+ &RecipientDomain::new("envelope"),
+ 2,
+ )
.await
.unwrap();
assert!(ipv6.contains(&match source_ips.unwrap() {
diff --git a/tests/src/smtp/queue/dsn.rs b/tests/src/smtp/queue/dsn.rs
index cdc8c328..93adb8fb 100644
--- a/tests/src/smtp/queue/dsn.rs
+++ b/tests/src/smtp/queue/dsn.rs
@@ -36,7 +36,7 @@ use crate::smtp::{
ParseTestConfig, TestConfig, TestSMTP,
};
use smtp::{
- config::ConfigContext,
+ config::{ConfigContext, EnvelopeKey},
core::SMTP,
queue::{
DeliveryAttempt, Domain, Error, ErrorDetails, HostResponse, Message, Recipient, Schedule,
@@ -110,7 +110,7 @@ async fn generate_dsn() {
let ctx = ConfigContext::new(&[]).parse_signatures();
let mut config = &mut core.queue.config.dsn;
config.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
@@ -193,7 +193,7 @@ async fn compare_dsn(message: Box<Message>, test: &str) {
failed.set_extension("failed");
fs::write(&failed, dsn.as_bytes()).unwrap();
panic!(
- "Failed for {}, ouput saved to {}",
+ "Failed for {}, output saved to {}",
path.display(),
failed.display()
);
diff --git a/tests/src/smtp/reporting/dmarc.rs b/tests/src/smtp/reporting/dmarc.rs
index 7f929476..f3cde714 100644
--- a/tests/src/smtp/reporting/dmarc.rs
+++ b/tests/src/smtp/reporting/dmarc.rs
@@ -41,7 +41,7 @@ use crate::smtp::{
ParseTestConfig, TestConfig, TestSMTP,
};
use smtp::{
- config::{AggregateFrequency, ConfigContext, IfBlock},
+ config::{AggregateFrequency, ConfigContext, EnvelopeKey, IfBlock},
core::SMTP,
reporting::{
dmarc::GenerateDmarcReport,
@@ -67,7 +67,7 @@ async fn report_dmarc() {
config.path = IfBlock::new(temp_dir.temp_dir.clone());
config.hash = IfBlock::new(16);
config.dmarc_aggregate.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
config.dmarc_aggregate.max_size = IfBlock::new(4096);
diff --git a/tests/src/smtp/reporting/tls.rs b/tests/src/smtp/reporting/tls.rs
index 2e213009..627a3a89 100644
--- a/tests/src/smtp/reporting/tls.rs
+++ b/tests/src/smtp/reporting/tls.rs
@@ -38,7 +38,7 @@ use crate::smtp::{
ParseTestConfig, TestConfig, TestSMTP,
};
use smtp::{
- config::{AggregateFrequency, ConfigContext, IfBlock},
+ config::{AggregateFrequency, ConfigContext, EnvelopeKey, IfBlock},
core::SMTP,
reporting::{
scheduler::{ReportType, Scheduler},
@@ -64,7 +64,7 @@ async fn report_tls() {
config.path = IfBlock::new(temp_dir.temp_dir.clone());
config.hash = IfBlock::new(16);
config.tls.sign = "['rsa']"
- .parse_if::<Vec<DynValue>>(&ctx)
+ .parse_if::<Vec<DynValue<EnvelopeKey>>>(&ctx)
.map_if_block(&ctx.signers, "", "")
.unwrap();
config.tls.max_size = IfBlock::new(4096);