Support for referencing context variables from dynamic values.

This commit is contained in:
mdecimus
2023-07-29 17:55:14 +02:00
parent 66170bc177
commit ceae6e9465
37 changed files with 593 additions and 577 deletions

View File

@@ -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"

View File

@@ -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
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 remote_ip(&self) -> IpAddr {
self.remote_ip
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 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()
}
fn listener_id(&self) -> u16 {
self.listener_id
}
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)),
}
}
}

View File

@@ -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},

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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()
);

View File

@@ -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);

View File

@@ -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);