diff --git a/crates/directory/src/core/config.rs b/crates/directory/src/core/config.rs index 27ce1c65..928f9719 100644 --- a/crates/directory/src/core/config.rs +++ b/crates/directory/src/core/config.rs @@ -27,9 +27,12 @@ use deadpool::{ }; use std::{sync::Arc, time::Duration}; use store::{Store, Stores}; -use utils::config::{ - utils::{AsKey, ParseValue}, - Config, Servers, +use utils::{ + config::{ + utils::{AsKey, ParseValue}, + Config, Servers, + }, + expr::Token, }; use ahash::AHashMap; @@ -171,9 +174,13 @@ impl AddressMapping { "Invalid value for address mapping {key:?}: {value:?}", )), } - } else if let Some(if_block) = - config.parse_if_block(key, |name| Err(format!("Invalid variable name {name:?}.",)))? - { + } else if let Some(if_block) = config.parse_if_block(key, |name| { + if ["address", "email"].contains(&name) { + Ok(Token::Variable(1)) + } else { + Err(format!("Invalid variable name {name:?}.",)) + } + })? { Ok(AddressMapping::Custom(if_block)) } else { Ok(AddressMapping::Disable) diff --git a/crates/directory/src/lib.rs b/crates/directory/src/lib.rs index 45532a22..eb0bcff0 100644 --- a/crates/directory/src/lib.rs +++ b/crates/directory/src/lib.rs @@ -300,15 +300,21 @@ impl AddressMapping { } } AddressMapping::Custom(if_block) => { - let result = if_block - .eval( - |_| Variable::default(), - |_, _| async { Variable::default() }, - ) - .await - .into_string(); - if !result.is_empty() { - return result.into_owned().into(); + if let Ok(result) = String::try_from( + if_block + .eval( + |name| { + if name == 1 { + Variable::from(address) + } else { + Variable::default() + } + }, + |_, _| async { Variable::default() }, + ) + .await, + ) { + return result.into(); } } AddressMapping::Disable => (), @@ -323,16 +329,23 @@ impl AddressMapping { .rsplit_once('@') .map(|(_, domain_part)| format!("@{}", domain_part)) .map(Cow::Owned), + AddressMapping::Custom(if_block) => { - let result = if_block - .eval( - |_| Variable::default(), - |_, _| async { Variable::default() }, - ) - .await - .into_string(); - if !result.is_empty() { - Some(result.into_owned().into()) + if let Ok(result) = String::try_from( + if_block + .eval( + |name| { + if name == 1 { + Variable::from(address) + } else { + Variable::default() + } + }, + |_, _| async { Variable::default() }, + ) + .await, + ) { + Some(result.into()) } else { None } diff --git a/crates/smtp/src/config/auth.rs b/crates/smtp/src/config/auth.rs index 9af8e692..ba8d056b 100644 --- a/crates/smtp/src/config/auth.rs +++ b/crates/smtp/src/config/auth.rs @@ -305,9 +305,9 @@ impl<'x> TryFrom> for VerifyStrategy { fn try_from(value: expr::Variable<'x>) -> Result { match value { expr::Variable::Integer(c) => match c { - 0 => Ok(VerifyStrategy::Relaxed), - 1 => Ok(VerifyStrategy::Strict), - 2 => Ok(VerifyStrategy::Disable), + 2 => Ok(VerifyStrategy::Relaxed), + 3 => Ok(VerifyStrategy::Strict), + 4 => Ok(VerifyStrategy::Disable), _ => Err(()), }, _ => Err(()), @@ -318,9 +318,9 @@ impl<'x> TryFrom> for VerifyStrategy { impl From for Constant { fn from(value: VerifyStrategy) -> Self { Constant::Integer(match value { - VerifyStrategy::Relaxed => 0, - VerifyStrategy::Strict => 1, - VerifyStrategy::Disable => 2, + VerifyStrategy::Relaxed => 2, + VerifyStrategy::Strict => 3, + VerifyStrategy::Disable => 4, }) } } diff --git a/crates/smtp/src/config/queue.rs b/crates/smtp/src/config/queue.rs index e689cd34..2bb839b0 100644 --- a/crates/smtp/src/config/queue.rs +++ b/crates/smtp/src/config/queue.rs @@ -382,9 +382,9 @@ impl<'x> TryFrom> for RequireOptional { fn try_from(value: Variable<'x>) -> Result { match value { - utils::expr::Variable::Integer(0) => Ok(RequireOptional::Optional), + utils::expr::Variable::Integer(2) => Ok(RequireOptional::Optional), utils::expr::Variable::Integer(1) => Ok(RequireOptional::Require), - utils::expr::Variable::Integer(2) => Ok(RequireOptional::Disable), + utils::expr::Variable::Integer(0) => Ok(RequireOptional::Disable), _ => Err(()), } } @@ -393,9 +393,9 @@ impl<'x> TryFrom> for RequireOptional { impl From for Constant { fn from(value: RequireOptional) -> Self { Constant::Integer(match value { - RequireOptional::Optional => 0, + RequireOptional::Optional => 2, RequireOptional::Require => 1, - RequireOptional::Disable => 2, + RequireOptional::Disable => 0, }) } } diff --git a/crates/smtp/src/config/report.rs b/crates/smtp/src/config/report.rs index 230dc679..c434a57c 100644 --- a/crates/smtp/src/config/report.rs +++ b/crates/smtp/src/config/report.rs @@ -214,9 +214,9 @@ impl From for Constant { fn from(value: AggregateFrequency) -> Self { match value { AggregateFrequency::Never => 0.into(), - AggregateFrequency::Hourly => 1.into(), - AggregateFrequency::Daily => 2.into(), - AggregateFrequency::Weekly => 3.into(), + AggregateFrequency::Hourly => 2.into(), + AggregateFrequency::Daily => 3.into(), + AggregateFrequency::Weekly => 4.into(), } } } @@ -227,9 +227,9 @@ impl<'x> TryFrom> for AggregateFrequency { fn try_from(value: Variable<'x>) -> Result { match value { Variable::Integer(0) => Ok(AggregateFrequency::Never), - Variable::Integer(1) => Ok(AggregateFrequency::Hourly), - Variable::Integer(2) => Ok(AggregateFrequency::Daily), - Variable::Integer(3) => Ok(AggregateFrequency::Weekly), + Variable::Integer(2) => Ok(AggregateFrequency::Hourly), + Variable::Integer(3) => Ok(AggregateFrequency::Daily), + Variable::Integer(4) => Ok(AggregateFrequency::Weekly), _ => Err(()), } } diff --git a/crates/smtp/src/config/shared.rs b/crates/smtp/src/config/shared.rs index f31210c4..80d42825 100644 --- a/crates/smtp/src/config/shared.rs +++ b/crates/smtp/src/config/shared.rs @@ -31,7 +31,6 @@ use super::{ConfigContext, RelayHost}; pub trait ConfigShared { fn parse_shared(&self, ctx: &ConfigContext) -> super::Result; - fn parse_host(&self, id: &str) -> super::Result; } diff --git a/crates/smtp/src/config/throttle.rs b/crates/smtp/src/config/throttle.rs index 0f41646e..0942db62 100644 --- a/crates/smtp/src/config/throttle.rs +++ b/crates/smtp/src/config/throttle.rs @@ -122,15 +122,15 @@ impl ParseTrottleKey for &str { fn parse_throttle_key(&self, key: &str) -> super::Result { match *self { "rcpt" => Ok(THROTTLE_RCPT), - "rcpt-domain" => Ok(THROTTLE_RCPT_DOMAIN), + "rcpt_domain" => Ok(THROTTLE_RCPT_DOMAIN), "sender" => Ok(THROTTLE_SENDER), - "sender-domain" => Ok(THROTTLE_SENDER_DOMAIN), - "authenticated-as" => Ok(THROTTLE_AUTH_AS), + "sender_domain" => Ok(THROTTLE_SENDER_DOMAIN), + "authenticated_as" => Ok(THROTTLE_AUTH_AS), "listener" => Ok(THROTTLE_LISTENER), "mx" => Ok(THROTTLE_MX), - "remote-ip" => Ok(THROTTLE_REMOTE_IP), - "local-ip" => Ok(THROTTLE_LOCAL_IP), - "helo-domain" => Ok(THROTTLE_HELO_DOMAIN), + "remote_ip" => Ok(THROTTLE_REMOTE_IP), + "local_ip" => Ok(THROTTLE_LOCAL_IP), + "helo_domain" => Ok(THROTTLE_HELO_DOMAIN), _ => Err(format!("Invalid throttle key {self:?} found in {key:?}")), } } diff --git a/crates/smtp/src/core/eval.rs b/crates/smtp/src/core/eval.rs index 948e13c4..d7f4756e 100644 --- a/crates/smtp/src/core/eval.rs +++ b/crates/smtp/src/core/eval.rs @@ -33,20 +33,14 @@ pub const F_KEY_EXISTS: u32 = 2; pub const VARIABLES_MAP: &[(&str, u32)] = &[ ("rcpt", V_RECIPIENT), - ("rcpt-domain", V_RECIPIENT_DOMAIN), ("rcpt_domain", V_RECIPIENT_DOMAIN), ("sender", V_SENDER), - ("sender-domain", V_SENDER_DOMAIN), ("sender_domain", V_SENDER_DOMAIN), ("mx", V_MX), - ("helo-domain", V_HELO_DOMAIN), ("helo_domain", V_HELO_DOMAIN), - ("authenticated-as", V_AUTHENTICATED_AS), ("authenticated_as", V_AUTHENTICATED_AS), ("listener", V_LISTENER), - ("remote-ip", V_REMOTE_IP), ("remote_ip", V_REMOTE_IP), - ("local-ip", V_LOCAL_IP), ("local_ip", V_LOCAL_IP), ("priority", V_PRIORITY), ]; diff --git a/crates/utils/src/config/utils.rs b/crates/utils/src/config/utils.rs index 2b99a186..598de524 100644 --- a/crates/utils/src/config/utils.rs +++ b/crates/utils/src/config/utils.rs @@ -441,9 +441,9 @@ impl<'x> TryFrom> for MtPriority { fn try_from(value: Variable<'x>) -> Result { match value { Variable::Integer(value) => match value { - 0 => Ok(MtPriority::Mixer), - 1 => Ok(MtPriority::Stanag4406), - 2 => Ok(MtPriority::Nsep), + 2 => Ok(MtPriority::Mixer), + 3 => Ok(MtPriority::Stanag4406), + 4 => Ok(MtPriority::Nsep), _ => Err(()), }, Variable::String(value) => MtPriority::parse_value("", &value).map_err(|_| ()), @@ -455,9 +455,9 @@ impl<'x> TryFrom> for MtPriority { impl From for Constant { fn from(value: MtPriority) -> Self { Constant::Integer(match value { - MtPriority::Mixer => 0, - MtPriority::Stanag4406 => 1, - MtPriority::Nsep => 2, + MtPriority::Mixer => 2, + MtPriority::Stanag4406 => 3, + MtPriority::Nsep => 4, }) } } @@ -503,10 +503,10 @@ impl<'x> TryFrom> for IpLookupStrategy { fn try_from(value: Variable<'x>) -> Result { match value { Variable::Integer(value) => match value { - 0 => Ok(IpLookupStrategy::Ipv4Only), - 1 => Ok(IpLookupStrategy::Ipv6Only), - 2 => Ok(IpLookupStrategy::Ipv6thenIpv4), - 3 => Ok(IpLookupStrategy::Ipv4thenIpv6), + 2 => Ok(IpLookupStrategy::Ipv4Only), + 3 => Ok(IpLookupStrategy::Ipv6Only), + 4 => Ok(IpLookupStrategy::Ipv6thenIpv4), + 5 => Ok(IpLookupStrategy::Ipv4thenIpv6), _ => Err(()), }, Variable::String(value) => IpLookupStrategy::parse_value("", &value).map_err(|_| ()), @@ -518,10 +518,10 @@ impl<'x> TryFrom> for IpLookupStrategy { impl From for Constant { fn from(value: IpLookupStrategy) -> Self { Constant::Integer(match value { - IpLookupStrategy::Ipv4Only => 0, - IpLookupStrategy::Ipv6Only => 1, - IpLookupStrategy::Ipv6thenIpv4 => 2, - IpLookupStrategy::Ipv4thenIpv6 => 3, + IpLookupStrategy::Ipv4Only => 2, + IpLookupStrategy::Ipv6Only => 3, + IpLookupStrategy::Ipv6thenIpv4 => 4, + IpLookupStrategy::Ipv4thenIpv6 => 5, }) } } diff --git a/tests/resources/smtp/config/throttle.toml b/tests/resources/smtp/config/throttle.toml index 3e9dcbaa..c0b0dc42 100644 --- a/tests/resources/smtp/config/throttle.toml +++ b/tests/resources/smtp/config/throttle.toml @@ -1,10 +1,10 @@ [[throttle]] -match = {if = "remote-ip", eq = "127.0.0.1"} -key = ["remote-ip", "authenticated-as"] +match = "remote_ip == '127.0.0.1'" +key = ["remote_ip", "authenticated_as"] concurrency = 100 rate = "50/30s" [[throttle]] -key = "sender-domain" +key = "sender_domain" concurrency = 10000 diff --git a/tests/src/directory/mod.rs b/tests/src/directory/mod.rs index f2f35bd5..43039c91 100644 --- a/tests/src/directory/mod.rs +++ b/tests/src/directory/mod.rs @@ -630,8 +630,8 @@ async fn address_mappings() { expected-catch = false [custom] - catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } - subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } + catch-all = [{if = "matches('(.+)@(.+)$', address)", then = "'info@' + $2"}, {else = false}] + subaddressing = [{ if = "matches('^([^.]+)\.([^.]+)@(.+)$', address)", then = "$2 + '@' + $3" }, {else = false}] expected-sub = "doe+alias@example.org" expected-catch = "info@example.org" "#; diff --git a/tests/src/directory/sql.rs b/tests/src/directory/sql.rs index 87e69bf2..75bb61a1 100644 --- a/tests/src/directory/sql.rs +++ b/tests/src/directory/sql.rs @@ -223,7 +223,6 @@ async fn sql_directory() { handle.email_to_ids("info@example.org").await.unwrap(), map_account_ids(base_store, vec!["bill", "jane", "john"]).await ); - let todo = "test regex subaddressing"; assert_eq!( handle.email_to_ids("jane+alias@example.org").await.unwrap(), map_account_ids(base_store, vec!["jane"]).await diff --git a/tests/src/smtp/config.rs b/tests/src/smtp/config.rs index 08ac1497..75a54134 100644 --- a/tests/src/smtp/config.rs +++ b/tests/src/smtp/config.rs @@ -307,7 +307,13 @@ fn parse_throttle() { throttle, vec![ Throttle { - expr: Expression::default(), + expr: Expression { + items: vec![ + ExpressionItem::Variable(8), + ExpressionItem::Constant(Constant::String("127.0.0.1".to_string())), + ExpressionItem::BinaryOperator(BinaryOperator::Eq) + ] + }, keys: THROTTLE_REMOTE_IP | THROTTLE_AUTH_AS, concurrency: 100.into(), rate: Rate { diff --git a/tests/src/smtp/inbound/antispam.rs b/tests/src/smtp/inbound/antispam.rs index 8623be18..8d405938 100644 --- a/tests/src/smtp/inbound/antispam.rs +++ b/tests/src/smtp/inbound/antispam.rs @@ -229,6 +229,8 @@ async fn antispam() { let mut ctx = ConfigContext::new(&[]); ctx.stores = config.parse_stores().await.unwrap(); core.sieve = config.parse_sieve(&mut ctx).unwrap(); + core.shared.lookup_stores = ctx.stores.lookup_stores.clone(); + core.shared.scripts = ctx.scripts.clone(); let config = &mut core.session.config; config.rcpt.relay = IfBlock::new(true); @@ -325,6 +327,7 @@ async fn antispam() { let value = value.trim(); match param { "remote_ip" => { + session.data.remote_ip_str = value.to_string(); session.data.remote_ip = value.parse().unwrap(); } "helo_domain" => { diff --git a/tests/src/smtp/inbound/auth.rs b/tests/src/smtp/inbound/auth.rs index f73cda54..d4cde99e 100644 --- a/tests/src/smtp/inbound/auth.rs +++ b/tests/src/smtp/inbound/auth.rs @@ -22,7 +22,6 @@ */ use directory::core::config::ConfigDirectory; -use smtp_proto::{AUTH_LOGIN, AUTH_PLAIN}; use store::{Store, Stores}; use utils::config::{if_block::IfBlock, Config, Servers}; @@ -31,7 +30,7 @@ use crate::smtp::{ ParseTestConfig, TestConfig, }; use smtp::{ - config::ConfigContext, + config::session::Mechanism, core::{Session, State, SMTP}, }; @@ -59,41 +58,37 @@ member-of = ["sales", "support"] #[tokio::test] async fn auth() { let mut core = SMTP::test(); - let mut ctx = ConfigContext::new(&[]); - ctx.directory = Config::new(DIRECTORY) + core.shared.directories = Config::new(DIRECTORY) .unwrap() .parse_directory(&Stores::default(), &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let config = &mut core.session.config.auth; - config.require = r"[{if = 'remote-ip', eq = '10.0.0.1', then = true}, - {else = false}]" + config.require = r#"[{if = "remote_ip = '10.0.0.1'", then = true}, + {else = false}]"# .parse_if(); - config.directory = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 'local'}, - {else = false}]" + config.directory = r#"[{if = "remote_ip = '10.0.0.1'", then = "'local'"}, + {else = false}]"# .parse_if(); - config.errors_max = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 2}, - {else = 3}]" + config.errors_max = r#"[{if = "remote_ip = '10.0.0.1'", then = 2}, + {else = 3}]"# .parse_if(); config.errors_wait = "'100ms'".parse_if(); - config.mechanisms = format!( - "[{{if = 'remote-ip', eq = '10.0.0.1', then = {}}}, - {{else = 0}}]", - AUTH_PLAIN | AUTH_LOGIN - ) - .as_str() - .parse_if(); + config.mechanisms = r#"[{if = "remote_ip = '10.0.0.1'", then = "[plain, login]"}, + {else = 0}]"# + .parse_if_constant::(); config.must_match_sender = IfBlock::new(true); core.session.config.extensions.future_release = - r"[{if = 'authenticated-as', ne = '', then = '1d'}, + r"[{if = '!is_empty(authenticated_as)', then = '1d'}, {else = false}]" .parse_if(); // EHLO should not advertise plain text auth without TLS let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.stream.tls = false; session @@ -160,7 +155,7 @@ async fn auth() { session.cmd("cDRzc3cwcmQ=", "235 2.7.0").await; // Login should not be advertised to 10.0.0.2 - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session.stream.tls = true; session diff --git a/tests/src/smtp/inbound/data.rs b/tests/src/smtp/inbound/data.rs index 2aead711..9149745f 100644 --- a/tests/src/smtp/inbound/data.rs +++ b/tests/src/smtp/inbound/data.rs @@ -75,17 +75,18 @@ async fn data() { // Create temp dir for queue let mut qr = core.init_test_queue("smtp_data_test"); - let directory = Config::new(DIRECTORY) + core.shared.directories = Config::new(DIRECTORY) .unwrap() .parse_directory(&Stores::default(), &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let config = &mut core.session.config.rcpt; config.directory = IfBlock::new("local".to_string()); let config = &mut core.session.config; - config.data.add_auth_results = "[{if = 'remote-ip', eq = '10.0.0.3', then = true}, - {else = false}]" + config.data.add_auth_results = r#"[{if = "remote_ip = '10.0.0.3'", then = true}, + {else = false}]"# .parse_if(); config.data.add_date = config.data.add_auth_results.clone(); config.data.add_message_id = config.data.add_auth_results.clone(); @@ -93,30 +94,30 @@ async fn data() { config.data.add_return_path = config.data.add_auth_results.clone(); config.data.add_received_spf = config.data.add_auth_results.clone(); config.data.max_received_headers = IfBlock::new(3); - config.data.max_messages = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 1}, - {else = 100}]" + config.data.max_messages = r#"[{if = "remote_ip = '10.0.0.1'", then = 1}, + {else = 100}]"# .parse_if(); - core.queue.config.quota = r"[[queue.quota]] - match = {if = 'sender', eq = 'john@doe.org'} + core.queue.config.quota = r#"[[queue.quota]] + match = "sender = 'john@doe.org'" key = ['sender'] messages = 1 [[queue.quota]] - match = {if = 'rcpt-domain', eq = 'foobar.org'} - key = ['rcpt-domain'] + match = "rcpt_domain = 'foobar.org'" + key = ['rcpt_domain'] size = 450 [[queue.quota]] - match = {if = 'rcpt', eq = 'jane@domain.net'} + match = "rcpt = 'jane@domain.net'" key = ['rcpt'] size = 450 - " + "# .parse_quota(); // Test queue message builder let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.test_builder().await; @@ -162,7 +163,7 @@ async fn data() { session.rset().await; // Headers should be added to messages from 10.0.0.3 - session.data.remote_ip = "10.0.0.3".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.3".to_string(); session.eval_session_params().await; session .send_message("john@doe.org", &["mike@test.com"], "test:no_msgid", "250") @@ -183,7 +184,7 @@ async fn data() { // Only one message is allowed in the queue from john@doe.org let mut queued_messages = vec![]; - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session .send_message("john@doe.org", &["bill@foobar.org"], "test:no_dkim", "250") diff --git a/tests/src/smtp/inbound/dmarc.rs b/tests/src/smtp/inbound/dmarc.rs index b4ac0a93..d7a54f8c 100644 --- a/tests/src/smtp/inbound/dmarc.rs +++ b/tests/src/smtp/inbound/dmarc.rs @@ -62,7 +62,7 @@ email = ["jdoe@example.com"] #[tokio::test] async fn dmarc() { let mut core = SMTP::test(); - let ctx = ConfigContext::new(&[]).parse_signatures(); + core.shared.signers = ConfigContext::new(&[]).parse_signatures().signers; // Create temp dir for queue let mut qr = core.init_test_queue("smtp_dmarc_test"); @@ -131,11 +131,12 @@ async fn dmarc() { // Create report channels let mut rr = core.init_test_report(); - let directory = Config::new(DIRECTORY) + core.shared.directories = Config::new(DIRECTORY) .unwrap() .parse_directory(&Stores::default(), &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let config = &mut core.session.config.rcpt; config.directory = IfBlock::new("local".to_string()); @@ -148,31 +149,32 @@ async fn dmarc() { config.data.add_received_spf = IfBlock::new(true); let config = &mut core.report.config; - config.dkim.send = "[1, 1s]".parse_if(); + config.dkim.send = "\"[1, 1s]\"".parse_if(); config.dmarc.send = config.dkim.send.clone(); config.spf.send = config.dkim.send.clone(); config.dmarc_aggregate.send = IfBlock::new(AggregateFrequency::Daily); let config = &mut core.mail_auth; - config.spf.verify_ehlo = "[{if = 'remote-ip', eq = '10.0.0.2', then = 'strict'}, - { else = 'relaxed' }]" - .parse_if(); + config.spf.verify_ehlo = r#"[{if = "remote_ip = '10.0.0.2'", then = 'strict'}, + { else = 'relaxed' }]"# + .parse_if_constant::(); config.spf.verify_mail_from = config.spf.verify_ehlo.clone(); config.dmarc.verify = IfBlock::new(VerifyStrategy::Strict); config.arc.verify = config.dmarc.verify.clone(); - config.dkim.verify = "[{if = 'sender-domain', eq = 'test.net', then = 'relaxed'}, - { else = 'strict' }]" - .parse_if(); + config.dkim.verify = r#"[{if = "sender_domain = 'test.net'", then = 'relaxed'}, + { else = 'strict' }]"# + .parse_if_constant::(); let config = &mut core.report.config; - config.spf.sign = "['rsa']".parse_if(); - config.dmarc.sign = "['rsa']".parse_if(); - config.dkim.sign = "['rsa']".parse_if(); + config.spf.sign = "\"['rsa']\"".parse_if(); + config.dmarc.sign = "\"['rsa']\"".parse_if(); + config.dkim.sign = "\"['rsa']\"".parse_if(); // SPF must pass let core = Arc::new(core); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session.eval_session_params().await; session.ehlo("mx.example.com").await; session.mail_from("bill@example.com", "550 5.7.23").await; @@ -195,7 +197,8 @@ async fn dmarc() { qr.assert_empty_queue(); // Invalid DKIM signatures should be rejected - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session.eval_session_params().await; session .send_message( diff --git a/tests/src/smtp/inbound/ehlo.rs b/tests/src/smtp/inbound/ehlo.rs index b214a575..17c422af 100644 --- a/tests/src/smtp/inbound/ehlo.rs +++ b/tests/src/smtp/inbound/ehlo.rs @@ -24,13 +24,17 @@ use std::time::{Duration, Instant}; use mail_auth::{common::parse::TxtRecordParser, spf::Spf, SpfResult}; +use smtp_proto::MtPriority; use utils::config::if_block::IfBlock; use crate::smtp::{ session::{TestSession, VerifyResponse}, ParseTestConfig, TestConfig, }; -use smtp::core::{Session, SMTP}; +use smtp::{ + config::VerifyStrategy, + core::{Session, SMTP}, +}; #[tokio::test] async fn ehlo() { @@ -47,23 +51,24 @@ async fn ehlo() { ); let config = &mut core.session.config; - config.data.max_message_size = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 1024}, - {else = 2048}]" + config.data.max_message_size = r#"[{if = "remote_ip = '10.0.0.1'", then = 1024}, + {else = 2048}]"# .parse_if(); - config.extensions.future_release = r"[{if = 'remote-ip', eq = '10.0.0.1', then = '1h'}, - {else = false}]" - .parse_if(); - config.extensions.mt_priority = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 'nsep'}, - {else = false}]" - .parse_if(); - core.mail_auth.spf.verify_ehlo = r"[{if = 'remote-ip', eq = '10.0.0.2', then = 'strict'}, - {else = 'relaxed'}]" + config.extensions.future_release = r#"[{if = "remote_ip = '10.0.0.1'", then = '1h'}, + {else = false}]"# .parse_if(); + config.extensions.mt_priority = r#"[{if = "remote_ip = '10.0.0.1'", then = 'nsep'}, + {else = false}]"# + .parse_if_constant::(); + core.mail_auth.spf.verify_ehlo = r#"[{if = "remote_ip = '10.0.0.2'", then = 'strict'}, + {else = 'relaxed'}]"# + .parse_if_constant::(); config.ehlo.reject_non_fqdn = IfBlock::new(true); // Reject non-FQDN domains let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session.stream.tls = false; session.eval_session_params().await; session.cmd("EHLO domain", "550 5.5.0").await; @@ -85,7 +90,8 @@ async fn ehlo() { // Test SPF strict mode session.data.helo_domain = String::new(); - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session.stream.tls = true; session.eval_session_params().await; session.ingest(b"EHLO mx1.foobar.org\r\n").await.unwrap(); diff --git a/tests/src/smtp/inbound/limits.rs b/tests/src/smtp/inbound/limits.rs index 33607e95..b0b9b0ed 100644 --- a/tests/src/smtp/inbound/limits.rs +++ b/tests/src/smtp/inbound/limits.rs @@ -35,20 +35,20 @@ use smtp::core::{Session, SMTP}; async fn limits() { let mut core = SMTP::test(); let config = &mut core.session.config; - config.transfer_limit = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 10}, - {else = 1024}]" + config.transfer_limit = r#"[{if = "remote_ip = '10.0.0.1'", then = 10}, + {else = 1024}]"# .parse_if(); - config.timeout = r"[{if = 'remote-ip', eq = '10.0.0.2', then = '500ms'}, - {else = '30m'}]" + config.timeout = r#"[{if = "remote_ip = '10.0.0.2'", then = '500ms'}, + {else = '30m'}]"# .parse_if(); - config.duration = r"[{if = 'remote-ip', eq = '10.0.0.3', then = '500ms'}, - {else = '60m'}]" + config.duration = r#"[{if = "remote_ip = '10.0.0.3'", then = '500ms'}, + {else = '60m'}]"# .parse_if(); let (_tx, rx) = watch::channel(true); // Exceed max line length let mut session = Session::test_with_shutdown(core, rx); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); let mut buf = vec![b'A'; 2049]; session.ingest(&buf).await.unwrap(); session.ingest(b"\r\n").await.unwrap(); @@ -66,7 +66,7 @@ async fn limits() { session.response().assert_code("451 4.7.28"); // Loitering - session.data.remote_ip = "10.0.0.3".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.3".to_string(); session.data.valid_until = Instant::now(); session.eval_session_params().await; tokio::time::sleep(Duration::from_millis(600)).await; @@ -75,7 +75,7 @@ async fn limits() { session.response().assert_code("453 4.3.2"); // Timeout - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.data.valid_until = Instant::now(); session.eval_session_params().await; session.write_rx("MAIL FROM:\r\n"); diff --git a/tests/src/smtp/inbound/mail.rs b/tests/src/smtp/inbound/mail.rs index 2bda8ca6..f662d121 100644 --- a/tests/src/smtp/inbound/mail.rs +++ b/tests/src/smtp/inbound/mail.rs @@ -27,7 +27,7 @@ use std::{ }; use mail_auth::{common::parse::TxtRecordParser, spf::Spf, IprevResult, SpfResult}; -use smtp_proto::{MAIL_BY_NOTIFY, MAIL_BY_RETURN, MAIL_REQUIRETLS}; +use smtp_proto::{MtPriority, MAIL_BY_NOTIFY, MAIL_BY_RETURN, MAIL_REQUIRETLS}; use utils::config::if_block::IfBlock; use crate::smtp::{ @@ -71,39 +71,40 @@ async fn mail() { let config = &mut core.session.config; config.ehlo.require = IfBlock::new(true); core.mail_auth.spf.verify_ehlo = IfBlock::new(VerifyStrategy::Relaxed); - core.mail_auth.spf.verify_mail_from = r"[{if = 'remote-ip', eq = '10.0.0.2', then = 'strict'}, - {else = 'relaxed'}]" + core.mail_auth.spf.verify_mail_from = r#"[{if = "remote_ip = '10.0.0.2'", then = 'strict'}, + {else = 'relaxed'}]"# + .parse_if_constant::(); + core.mail_auth.iprev.verify = r#"[{if = "remote_ip = '10.0.0.2'", then = 'strict'}, + {else = 'relaxed'}]"# + .parse_if_constant::(); + config.extensions.future_release = r#"[{if = "remote_ip = '10.0.0.2'", then = '1d'}, + {else = false}]"# .parse_if(); - core.mail_auth.iprev.verify = r"[{if = 'remote-ip', eq = '10.0.0.2', then = 'strict'}, - {else = 'relaxed'}]" + config.extensions.deliver_by = r#"[{if = "remote_ip = '10.0.0.2'", then = '1d'}, + {else = false}]"# .parse_if(); - config.extensions.future_release = r"[{if = 'remote-ip', eq = '10.0.0.2', then = '1d'}, - {else = false}]" + config.extensions.requiretls = r#"[{if = "remote_ip = '10.0.0.2'", then = true}, + {else = false}]"# .parse_if(); - config.extensions.deliver_by = r"[{if = 'remote-ip', eq = '10.0.0.2', then = '1d'}, - {else = false}]" - .parse_if(); - config.extensions.requiretls = r"[{if = 'remote-ip', eq = '10.0.0.2', then = true}, - {else = false}]" - .parse_if(); - config.extensions.mt_priority = r"[{if = 'remote-ip', eq = '10.0.0.2', then = 'nsep'}, - {else = false}]" - .parse_if(); - config.data.max_message_size = r"[{if = 'remote-ip', eq = '10.0.0.2', then = 2048}, - {else = 1024}]" + config.extensions.mt_priority = r#"[{if = "remote_ip = '10.0.0.2'", then = 'nsep'}, + {else = false}]"# + .parse_if_constant::(); + config.data.max_message_size = r#"[{if = "remote_ip = '10.0.0.2'", then = 2048}, + {else = 1024}]"# .parse_if(); - config.throttle.mail_from = r"[[throttle]] - match = {if = 'remote-ip', eq = '10.0.0.1'} + config.throttle.mail_from = r#"[[throttle]] + match = "remote_ip = '10.0.0.1'" key = 'sender' rate = '2/1s' - " + "# .parse_throttle(); // Be rude and do not say EHLO let core = Arc::new(core); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session.eval_session_params().await; session .ingest(b"MAIL FROM:\r\n") @@ -174,7 +175,8 @@ async fn mail() { session.response().assert_code("552 5.3.4"); // Test strict IPREV - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session.data.iprev = None; session.eval_session_params().await; session diff --git a/tests/src/smtp/inbound/milter.rs b/tests/src/smtp/inbound/milter.rs index 2f38d24b..89fa2a70 100644 --- a/tests/src/smtp/inbound/milter.rs +++ b/tests/src/smtp/inbound/milter.rs @@ -84,7 +84,7 @@ async fn milter_session() { // Build session let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.doe.org").await; diff --git a/tests/src/smtp/inbound/rcpt.rs b/tests/src/smtp/inbound/rcpt.rs index c0ca8d80..c33b6c49 100644 --- a/tests/src/smtp/inbound/rcpt.rs +++ b/tests/src/smtp/inbound/rcpt.rs @@ -69,38 +69,39 @@ async fn rcpt() { let mut core = SMTP::test(); let config_ext = &mut core.session.config.extensions; - let directory = Config::new(DIRECTORY) + core.shared.directories = Config::new(DIRECTORY) .unwrap() .parse_directory(&Stores::default(), &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let config = &mut core.session.config.rcpt; config.directory = IfBlock::new("local".to_string()); - config.max_recipients = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 3}, - {else = 5}]" + config.max_recipients = r#"[{if = "remote_ip = '10.0.0.1'", then = 3}, + {else = 5}]"# .parse_if(); - config.relay = r"[{if = 'remote-ip', eq = '10.0.0.1', then = false}, - {else = true}]" + config.relay = r#"[{if = "remote_ip = '10.0.0.1'", then = false}, + {else = true}]"# .parse_if(); - config_ext.dsn = r"[{if = 'remote-ip', eq = '10.0.0.1', then = false}, - {else = true}]" + config_ext.dsn = r#"[{if = "remote_ip = '10.0.0.1'", then = false}, + {else = true}]"# .parse_if(); - config.errors_max = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 3}, - {else = 100}]" + config.errors_max = r#"[{if = "remote_ip = '10.0.0.1'", then = 3}, + {else = 100}]"# .parse_if(); - config.errors_wait = r"[{if = 'remote-ip', eq = '10.0.0.1', then = '5ms'}, - {else = '1s'}]" + config.errors_wait = r#"[{if = "remote_ip = '10.0.0.1'", then = '5ms'}, + {else = '1s'}]"# .parse_if(); - core.session.config.throttle.rcpt_to = r"[[throttle]] - match = {if = 'remote-ip', eq = '10.0.0.1'} + core.session.config.throttle.rcpt_to = r#"[[throttle]] + match = "remote_ip = '10.0.0.1'" key = 'sender' rate = '2/1s' - " + "# .parse_throttle(); // RCPT without MAIL FROM let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx1.foobar.org").await; session.rcpt_to("jane@foobar.org", "503 5.5.1").await; @@ -153,7 +154,7 @@ async fn rcpt() { } // Relaying should be allowed for 10.0.0.2 - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session.rset().await; session.mail_from("john@example.net", "250").await; diff --git a/tests/src/smtp/inbound/rewrite.rs b/tests/src/smtp/inbound/rewrite.rs index 16070a68..9d6bcd32 100644 --- a/tests/src/smtp/inbound/rewrite.rs +++ b/tests/src/smtp/inbound/rewrite.rs @@ -32,19 +32,15 @@ use utils::config::{if_block::IfBlock, utils::NoConstants, Config, Servers}; const CONFIG: &str = r#" [session.mail] -rewrite = [ { all-of = [ { if = "sender-domain", ends-with = ".foobar.net" }, - { if = "sender", matches = "^([^.]+)@([^.]+)\.(.+)$"}, - ], then = "${1}+${2}@${3}" }, +rewrite = [ { if = "ends_with(sender_domain, '.foobar.net') & matches('^([^.]+)@([^.]+)\.(.+)$', sender)", then = "$1 + '+' + $2 + '@' + $3"}, { else = false } ] -script = [ { if = "sender-domain", eq = "foobar.org", then = "mail" }, +script = [ { if = "sender_domain = 'foobar.org'", then = "'mail'" }, { else = false } ] [session.rcpt] -rewrite = [ { all-of = [ { if = "rcpt-domain", eq = "foobar.net" }, - { if = "rcpt", matches = "^([^.]+)\.([^.]+)@(.+)$"}, - ], then = "${1}+${2}@${3}" }, +rewrite = [ { if = "rcpt_domain = 'foobar.net' & matches('^([^.]+)\.([^.]+)@(.+)$', rcpt)", then = "$1 + '+' + $2 + '@' + $3"}, { else = false } ] -script = [ { if = "rcpt-domain", eq = "foobar.org", then = "rcpt" }, +script = [ { if = "rcpt_domain = 'foobar.org'", then = "'rcpt'" }, { else = false } ] [sieve.trusted] @@ -104,6 +100,7 @@ async fn address_rewrite() { .await .unwrap(); core.sieve = settings.parse_sieve(&mut ctx).unwrap(); + core.shared.scripts = ctx.scripts; let config = &mut core.session.config; config.mail.script = settings .parse_if_block("session.mail.script", |name| { @@ -133,7 +130,7 @@ async fn address_rewrite() { // Init session let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.doe.org").await; diff --git a/tests/src/smtp/inbound/scripts.rs b/tests/src/smtp/inbound/scripts.rs index bfc340ac..e579d6fc 100644 --- a/tests/src/smtp/inbound/scripts.rs +++ b/tests/src/smtp/inbound/scripts.rs @@ -55,9 +55,9 @@ format = "list" values = ["spammer.org", "spammer.net"] [session.data.pipe."test"] -command = [ { if = "remote-ip", eq = "10.0.0.123", then = "/bin/bash" }, +command = [ { if = "remote_ip = '10.0.0.123'", then = "'/bin/bash'" }, { else = false } ] -arguments = ["%CFG_PATH%/pipe_me.sh", "hello", "world"] +arguments = "['%CFG_PATH%/pipe_me.sh', 'hello', 'world']" timeout = "10s" [sieve.trusted] @@ -133,12 +133,16 @@ async fn sieve_scripts() { ) .unwrap(); ctx.stores = config.parse_stores().await.unwrap(); - ctx.directory = config + core.shared.lookup_stores = ctx.stores.lookup_stores.clone(); + core.shared.directories = config .parse_directory(&ctx.stores, &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let pipes = config.parse_pipes(&[V_REMOTE_IP]).unwrap(); core.sieve = config.parse_sieve(&mut ctx).unwrap(); + core.shared.signers = ctx.signers; + core.shared.scripts = ctx.scripts.clone(); let config = &mut core.session.config; config.connect.script = IfBlock::new("stage_connect".to_string()); config.ehlo.script = IfBlock::new("stage_ehlo".to_string()); @@ -151,7 +155,8 @@ async fn sieve_scripts() { // Build session let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.88".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.88".parse().unwrap(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); assert!(!session.init_conn().await); // Run tests @@ -184,7 +189,8 @@ async fn sieve_scripts() { session .response() .assert_contains("503 5.5.3 Your IP '10.0.0.88' is not welcomed here"); - session.data.remote_ip = "10.0.0.5".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.5".parse().unwrap(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); assert!(session.init_conn().await); session .response() @@ -359,7 +365,8 @@ async fn sieve_scripts() { qr.assert_empty_queue(); // Test pipes - session.data.remote_ip = "10.0.0.123".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.123".parse().unwrap(); + session.data.remote_ip = session.data.remote_ip_str.parse().unwrap(); session .send_message( "test@example.net", diff --git a/tests/src/smtp/inbound/sign.rs b/tests/src/smtp/inbound/sign.rs index 7ba4d55f..909bdee8 100644 --- a/tests/src/smtp/inbound/sign.rs +++ b/tests/src/smtp/inbound/sign.rs @@ -150,11 +150,12 @@ async fn sign_and_seal() { Instant::now() + Duration::from_secs(5), ); - let directory = Config::new(DIRECTORY) + core.shared.directories = Config::new(DIRECTORY) .unwrap() .parse_directory(&Stores::default(), &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let config = &mut core.session.config.rcpt; config.directory = IfBlock::new("local".to_string()); @@ -168,17 +169,19 @@ async fn sign_and_seal() { let config = &mut core.mail_auth; let ctx = ConfigContext::new(&[]).parse_signatures(); + core.shared.signers = ctx.signers; + core.shared.sealers = ctx.sealers; config.spf.verify_ehlo = IfBlock::new(VerifyStrategy::Relaxed); config.spf.verify_mail_from = config.spf.verify_ehlo.clone(); config.dkim.verify = config.spf.verify_ehlo.clone(); config.arc.verify = config.spf.verify_ehlo.clone(); config.dmarc.verify = config.spf.verify_ehlo.clone(); - config.dkim.sign = "['rsa']".parse_if(); - config.arc.seal = "'ed'".parse_if(); + config.dkim.sign = "\"['rsa']\"".parse_if(); + config.arc.seal = "\"'ed'\"".parse_if(); // Test DKIM signing let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session.ehlo("mx.example.com").await; session diff --git a/tests/src/smtp/inbound/throttle.rs b/tests/src/smtp/inbound/throttle.rs index 2e20873b..de77c584 100644 --- a/tests/src/smtp/inbound/throttle.rs +++ b/tests/src/smtp/inbound/throttle.rs @@ -30,27 +30,27 @@ use smtp::core::{Session, SessionAddress, SMTP}; async fn throttle_inbound() { let mut core = SMTP::test(); let config = &mut core.session.config; - config.throttle.connect = r"[[throttle]] - match = {if = 'remote-ip', eq = '10.0.0.1'} - key = 'remote-ip' + config.throttle.connect = r#"[[throttle]] + match = "remote_ip = '10.0.0.1'" + key = 'remote_ip' concurrency = 2 rate = '3/1s' - " + "# .parse_throttle(); - config.throttle.mail_from = r"[[throttle]] + config.throttle.mail_from = r#"[[throttle]] key = 'sender' rate = '2/1s' - " + "# .parse_throttle(); - config.throttle.rcpt_to = r"[[throttle]] - key = ['remote-ip', 'rcpt'] + config.throttle.rcpt_to = r#"[[throttle]] + key = ['remote_ip', 'rcpt'] rate = '2/1s' - " + "# .parse_throttle(); // Test connection concurrency limit let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); assert!( session.is_allowed().await, "Concurrency limiter too strict." @@ -105,6 +105,6 @@ async fn throttle_inbound() { assert!(session.is_allowed().await, "Rate limiter too strict."); assert!(session.is_allowed().await, "Rate limiter too strict."); assert!(!session.is_allowed().await, "Rate limiter failed."); - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); assert!(session.is_allowed().await, "Rate limiter too strict."); } diff --git a/tests/src/smtp/inbound/vrfy.rs b/tests/src/smtp/inbound/vrfy.rs index debe9c2e..4ec8c394 100644 --- a/tests/src/smtp/inbound/vrfy.rs +++ b/tests/src/smtp/inbound/vrfy.rs @@ -29,10 +29,7 @@ use crate::smtp::{ session::{TestSession, VerifyResponse}, ParseTestConfig, TestConfig, }; -use smtp::{ - config::ConfigContext, - core::{Session, SMTP}, -}; +use smtp::core::{Session, SMTP}; const DIRECTORY: &str = r#" [directory."local"] @@ -64,27 +61,27 @@ email-list = ["sales@foobar.org"] #[tokio::test] async fn vrfy_expn() { let mut core = SMTP::test(); - let ctx = ConfigContext::new(&[]); - let directory = Config::new(DIRECTORY) + core.shared.directories = Config::new(DIRECTORY) .unwrap() .parse_directory(&Stores::default(), &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; let config = &mut core.session.config.rcpt; config.directory = IfBlock::new("local".to_string()); let config = &mut core.session.config.extensions; - config.vrfy = r"[{if = 'remote-ip', eq = '10.0.0.1', then = true}, - {else = false}]" + config.vrfy = r#"[{if = "remote_ip = '10.0.0.1'", then = true}, + {else = false}]"# .parse_if(); - config.expn = r"[{if = 'remote-ip', eq = '10.0.0.1', then = true}, - {else = false}]" + config.expn = r#"[{if = "remote_ip = '10.0.0.1'", then = true}, + {else = false}]"# .parse_if(); - // EHLO should not avertise VRFY/EXPN to 10.0.0.2 + // EHLO should not advertise VRFY/EXPN to 10.0.0.2 let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session .ehlo("mx.foobar.org") @@ -95,7 +92,7 @@ async fn vrfy_expn() { session.cmd("EXPN sales@foobar.org", "252 2.5.1").await; // EHLO should advertise VRFY/EXPN for 10.0.0.1 - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session .ehlo("mx.foobar.org") diff --git a/tests/src/smtp/lookup/sql.rs b/tests/src/smtp/lookup/sql.rs index 64c292d1..105b5150 100644 --- a/tests/src/smtp/lookup/sql.rs +++ b/tests/src/smtp/lookup/sql.rs @@ -87,10 +87,12 @@ async fn lookup_sql() { let mut ctx = ConfigContext::new(&[]); let config = Config::new(&config_file).unwrap(); ctx.stores = config.parse_stores().await.unwrap(); - ctx.directory = config + core.shared.lookup_stores = ctx.stores.lookup_stores.clone(); + core.shared.directories = config .parse_directory(&ctx.stores, &Servers::default(), Store::default()) .await - .unwrap(); + .unwrap() + .directories; // Obtain directory handle let handle = DirectoryStore { @@ -142,30 +144,30 @@ async fn lookup_sql() { // Enable AUTH let config = &mut core.session.config.auth; - config.directory = r"'sql'".parse_if(); + config.directory = "\"'sql'\"".parse_if(); config.mechanisms = IfBlock::new(Mechanism::from(AUTH_PLAIN | AUTH_LOGIN)); config.errors_wait = IfBlock::new(Duration::from_millis(5)); // Enable VRFY/EXPN/RCPT let config = &mut core.session.config.rcpt; - config.directory = r"'sql'".parse_if(); + config.directory = "\"'sql'\"".parse_if(); config.relay = IfBlock::new(false); config.errors_wait = IfBlock::new(Duration::from_millis(5)); // Enable REQUIRETLS based on SQL lookup core.session.config.extensions.requiretls = - r"[{if = 'remote-ip', in-list = 'sql/is_ip_allowed', then = true}, - {else = false}]" + r#"[{if = "key_exists('sql/is_ip_allowed', remote_ip)", then = true}, + {else = false}]"# .parse_if(); let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.50".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.50".parse().unwrap(); session.eval_session_params().await; session.stream.tls = true; session .ehlo("mx.foobar.org") .await .assert_contains("REQUIRETLS"); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session .ehlo("mx1.foobar.org") diff --git a/tests/src/smtp/lookup/utils.rs b/tests/src/smtp/lookup/utils.rs index 7bcaf2ee..e73249d8 100644 --- a/tests/src/smtp/lookup/utils.rs +++ b/tests/src/smtp/lookup/utils.rs @@ -41,13 +41,13 @@ use crate::smtp::{ParseTestConfig, TestConfig}; #[tokio::test] async fn lookup_ip() { - let ipv6 = vec![ + let ipv6 = [ "a:b::1".parse().unwrap(), "a:b::2".parse().unwrap(), "a:b::3".parse().unwrap(), "a:b::4".parse().unwrap(), ]; - let ipv4 = vec![ + let ipv4 = [ "10.0.0.1".parse().unwrap(), "10.0.0.2".parse().unwrap(), "10.0.0.3".parse().unwrap(), @@ -55,18 +55,18 @@ async fn lookup_ip() { ]; let mut core = SMTP::test(); core.queue.config.source_ip.ipv4 = format!( - "[{}]", + "\"[{}]\"", ipv4.iter() - .map(|ip| format!("\"{}\"", ip)) + .map(|ip| format!("'{}'", ip)) .collect::>() .join(",") ) .as_str() .parse_if(); core.queue.config.source_ip.ipv6 = format!( - "[{}]", + "\"[{}]\"", ipv6.iter() - .map(|ip| format!("\"{}\"", ip)) + .map(|ip| format!("'{}'", ip)) .collect::>() .join(",") ) diff --git a/tests/src/smtp/management/queue.rs b/tests/src/smtp/management/queue.rs index 484bfedb..c757beb0 100644 --- a/tests/src/smtp/management/queue.rs +++ b/tests/src/smtp/management/queue.rs @@ -149,7 +149,7 @@ async fn manage_queue() { ("f", ("", vec!["success@foobar.org", "delay@foobar.org"])), ]); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("foobar.net").await; for test_num in 0..6 { diff --git a/tests/src/smtp/mod.rs b/tests/src/smtp/mod.rs index bf58b970..3f58bc89 100644 --- a/tests/src/smtp/mod.rs +++ b/tests/src/smtp/mod.rs @@ -55,7 +55,7 @@ use smtp::{ }, outbound::dane::DnssecResolver, }; -use utils::config::{if_block::IfBlock, Config}; +use utils::config::{if_block::IfBlock, utils::ConstantValue, Config}; pub mod config; pub mod inbound; @@ -68,6 +68,7 @@ pub mod session; pub trait ParseTestConfig { fn parse_if(&self) -> IfBlock; + fn parse_if_constant(&self) -> IfBlock; fn parse_throttle(&self) -> Vec; fn parse_quota(&self) -> QueueQuotas; fn parse_queue_throttle(&self) -> QueueThrottle; @@ -76,10 +77,14 @@ pub trait ParseTestConfig { impl ParseTestConfig for &str { fn parse_if(&self) -> IfBlock { + self.parse_if_constant::() + } + + fn parse_if_constant(&self) -> IfBlock { Config::new(&format!("test = {self}\n")) - .unwrap() + .unwrap_or_else(|err| panic!("Failed to parse if {}: {}", self, err)) .parse_if_block("test", |name| { - map_expr_token::( + map_expr_token::( name, &[ V_RECIPIENT, @@ -96,7 +101,7 @@ impl ParseTestConfig for &str { ], ) }) - .unwrap() + .unwrap_or_else(|err| panic!("Failed to parse if {}: {}", self, err)) .unwrap() } diff --git a/tests/src/smtp/outbound/dane.rs b/tests/src/smtp/outbound/dane.rs index 31626ee3..ac498b3d 100644 --- a/tests/src/smtp/outbound/dane.rs +++ b/tests/src/smtp/outbound/dane.rs @@ -108,7 +108,7 @@ async fn dane_verify() { let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/outbound/extensions.rs b/tests/src/smtp/outbound/extensions.rs index f8c2c18e..55d39979 100644 --- a/tests/src/smtp/outbound/extensions.rs +++ b/tests/src/smtp/outbound/extensions.rs @@ -83,7 +83,7 @@ async fn extensions() { let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/outbound/ip_lookup.rs b/tests/src/smtp/outbound/ip_lookup.rs index 4b9e4310..f4a45c2c 100644 --- a/tests/src/smtp/outbound/ip_lookup.rs +++ b/tests/src/smtp/outbound/ip_lookup.rs @@ -87,7 +87,7 @@ async fn ip_lookup_strategy() { let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/outbound/lmtp.rs b/tests/src/smtp/outbound/lmtp.rs index 60c3af19..e198babc 100644 --- a/tests/src/smtp/outbound/lmtp.rs +++ b/tests/src/smtp/outbound/lmtp.rs @@ -33,7 +33,7 @@ use crate::smtp::{ ParseTestConfig, TestConfig, TestSMTP, }; use smtp::{ - config::ConfigContext, + config::shared::ConfigShared, core::{Session, SMTP}, queue::{manager::Queue, DeliveryAttempt, Event, WorkerResult}, }; @@ -78,29 +78,30 @@ async fn lmtp_delivery() { // Multiple delivery attempts let mut local_qr = core.init_test_queue("lmtp_delivery_local"); - - let mut ctx = ConfigContext::new(&[]); - let config = Config::new(REMOTE).unwrap(); - core.queue.config.next_hop = "[{if = 'rcpt-domain', eq = 'foobar.org', then = 'lmtp'}, - {else = false}]" + core.shared.relay_hosts.insert( + "lmtp".to_string(), + Config::new(REMOTE).unwrap().parse_host("lmtp").unwrap(), + ); + core.queue.config.next_hop = r#"[{if = "rcpt_domain = 'foobar.org'", then = "'lmtp'"}, + {else = false}]"# .parse_if(); core.session.config.rcpt.relay = IfBlock::new(true); core.session.config.rcpt.max_recipients = IfBlock::new(100); core.session.config.extensions.dsn = IfBlock::new(true); let config = &mut core.queue.config; config.retry = IfBlock::new(Duration::from_millis(100)); - config.notify = "[{if = 'rcpt-domain', eq = 'foobar.org', then = ['100ms', '200ms']}, - {else = ['100ms']}]" + config.notify = r#"[{if = "rcpt_domain = 'foobar.org'", then = "['100ms', '200ms']"}, + {else = ['100ms']}]"# .parse_if(); - config.expire = "[{if = 'rcpt-domain', eq = 'foobar.org', then = '400ms'}, - {else = '500ms'}]" + config.expire = r#"[{if = "rcpt_domain = 'foobar.org'", then = "400ms"}, + {else = "500ms"}]"# .parse_if(); config.timeout.data = IfBlock::new(Duration::from_millis(50)); let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/outbound/mta_sts.rs b/tests/src/smtp/outbound/mta_sts.rs index bba8e923..1f66b7fb 100644 --- a/tests/src/smtp/outbound/mta_sts.rs +++ b/tests/src/smtp/outbound/mta_sts.rs @@ -95,7 +95,7 @@ async fn mta_sts_verify() { let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/outbound/smtp.rs b/tests/src/smtp/outbound/smtp.rs index c18f92b2..6ed79bef 100644 --- a/tests/src/smtp/outbound/smtp.rs +++ b/tests/src/smtp/outbound/smtp.rs @@ -36,7 +36,6 @@ use crate::smtp::{ ParseTestConfig, TestConfig, TestSMTP, }; use smtp::{ - config::ConfigContext, core::{Session, SMTP}, queue::{manager::Queue, DeliveryAttempt, Event, WorkerResult}, }; @@ -110,18 +109,18 @@ async fn smtp_delivery() { core.session.config.extensions.dsn = IfBlock::new(true); let config = &mut core.queue.config; config.retry = IfBlock::new(Duration::from_millis(100)); - config.notify = "[{if = 'rcpt-domain', eq = 'foobar.org', then = ['100ms', '200ms']}, - {if = 'rcpt-domain', eq = 'foobar.com', then = ['500ms', '600ms']}, - {else = ['100ms']}]" + config.notify = r#"[{if = "rcpt_domain = 'foobar.org'", then = "['100ms', '200ms']"}, + {if = "rcpt_domain = 'foobar.com'", then = "['500ms', '600ms']"}, + {else = ['100ms']}]"# .parse_if(); - config.expire = "[{if = 'rcpt-domain', eq = 'foobar.org', then = '650ms'}, - {else = '750ms'}]" + config.expire = r#"[{if = "rcpt_domain = 'foobar.org'", then = "650ms"}, + {else = "750ms"}]"# .parse_if(); let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session @@ -250,7 +249,7 @@ async fn smtp_delivery() { // SMTP smuggling for separator in ["\n", "\r"].iter() { - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; diff --git a/tests/src/smtp/outbound/throttle.rs b/tests/src/smtp/outbound/throttle.rs index 0519f9d5..8b383d75 100644 --- a/tests/src/smtp/outbound/throttle.rs +++ b/tests/src/smtp/outbound/throttle.rs @@ -39,37 +39,37 @@ use smtp::{ queue::{manager::Queue, DeliveryAttempt, Message, QueueEnvelope}, }; -const THROTTLE: &str = " +const THROTTLE: &str = r#" [[queue.throttle]] -match = {if = 'sender-domain', eq = 'foobar.org'} -key = 'sender-domain' +match = "sender_domain = 'foobar.org'" +key = 'sender_domain' concurrency = 1 [[queue.throttle]] -match = {if = 'sender-domain', eq = 'foobar.net'} -key = 'sender-domain' +match = "sender_domain = 'foobar.net'" +key = 'sender_domain' rate = '1/30m' [[queue.throttle]] -match = {if = 'rcpt-domain', eq = 'example.org'} -key = 'rcpt-domain' +match = "rcpt_domain = 'example.org'" +key = 'rcpt_domain' concurrency = 1 [[queue.throttle]] -match = {if = 'rcpt-domain', eq = 'example.net'} -key = 'rcpt-domain' +match = "rcpt_domain = 'example.net'" +key = 'rcpt_domain' rate = '1/40m' [[queue.throttle]] -match = {if = 'mx', eq = 'mx.test.org'} +match = "mx = 'mx.test.org'" key = 'mx' concurrency = 1 [[queue.throttle]] -match = {if = 'mx', eq = 'mx.test.net'} +match = "mx = 'mx.test.net'" key = 'mx' rate = '1/50m' -"; +"#; #[tokio::test] async fn throttle_outbound() { @@ -94,7 +94,7 @@ async fn throttle_outbound() { let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/outbound/tls.rs b/tests/src/smtp/outbound/tls.rs index dc8788b2..6e084800 100644 --- a/tests/src/smtp/outbound/tls.rs +++ b/tests/src/smtp/outbound/tls.rs @@ -82,7 +82,7 @@ async fn starttls_optional() { let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session diff --git a/tests/src/smtp/queue/dsn.rs b/tests/src/smtp/queue/dsn.rs index 50fbc7b6..9590e85f 100644 --- a/tests/src/smtp/queue/dsn.rs +++ b/tests/src/smtp/queue/dsn.rs @@ -107,9 +107,9 @@ async fn generate_dsn() { // Load config let mut core = SMTP::test(); - let ctx = ConfigContext::new(&[]).parse_signatures(); + core.shared.signers = ConfigContext::new(&[]).parse_signatures().signers; let config = &mut core.queue.config.dsn; - config.sign = "['rsa']".parse_if(); + config.sign = "\"['rsa']\"".parse_if(); // Create temp dir for queue let mut qr = core.init_test_queue("smtp_dsn_test"); diff --git a/tests/src/smtp/queue/retry.rs b/tests/src/smtp/queue/retry.rs index 792c3763..08932265 100644 --- a/tests/src/smtp/queue/retry.rs +++ b/tests/src/smtp/queue/retry.rs @@ -57,19 +57,19 @@ async fn queue_retry() { config.deliver_by = IfBlock::new(Duration::from_secs(86400)); config.future_release = IfBlock::new(Duration::from_secs(86400)); let config = &mut core.queue.config; - config.retry = "[100ms, 200ms, 300ms]".parse_if(); - config.notify = "[{if = 'sender-domain', eq = 'test.org', then = ['150ms', '200ms']}, - {else = ['15h', '22h']}]" + config.retry = r#""[100ms, 200ms, 300ms]""#.parse_if(); + config.notify = r#"[{if = "sender_domain = 'test.org'", then = "['150ms', '200ms']"}, + {else = ['15h', '22h']}]"# .parse_if(); - config.expire = "[{if = 'sender-domain', eq = 'test.org', then = '600ms'}, - {else = '1d'}]" + config.expire = r#"[{if = "sender_domain = 'test.org'", then = "600ms"}, + {else = '1d'}]"# .parse_if(); // Create test message let core = Arc::new(core); let mut queue = Queue::default(); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; session @@ -166,7 +166,7 @@ async fn queue_retry() { .assert_contains("Action: failed"); // Test FUTURERELEASE + DELIVERBY (RETURN) - session.data.remote_ip = "10.0.0.2".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.2".to_string(); session.eval_session_params().await; session .send_message( diff --git a/tests/src/smtp/reporting/analyze.rs b/tests/src/smtp/reporting/analyze.rs index 25d988a2..9c36896c 100644 --- a/tests/src/smtp/reporting/analyze.rs +++ b/tests/src/smtp/reporting/analyze.rs @@ -56,7 +56,7 @@ async fn report_analyze() { // Create test message let core = Arc::new(core); let mut session = Session::test(core.clone()); - session.data.remote_ip = "10.0.0.1".parse().unwrap(); + session.data.remote_ip_str = "10.0.0.1".to_string(); session.eval_session_params().await; session.ehlo("mx.test.org").await; diff --git a/tests/src/smtp/reporting/dmarc.rs b/tests/src/smtp/reporting/dmarc.rs index a9b358b0..29d1009b 100644 --- a/tests/src/smtp/reporting/dmarc.rs +++ b/tests/src/smtp/reporting/dmarc.rs @@ -61,12 +61,12 @@ async fn report_dmarc() { // Create scheduler let mut core = SMTP::test(); - let ctx = ConfigContext::new(&[]).parse_signatures(); + core.shared.signers = ConfigContext::new(&[]).parse_signatures().signers; let temp_dir = make_temp_dir("smtp_report_dmarc_test", true); let config = &mut core.report.config; config.path = temp_dir.temp_dir.clone(); config.hash = IfBlock::new(16); - config.dmarc_aggregate.sign = "['rsa']".parse_if(); + config.dmarc_aggregate.sign = "\"['rsa']\"".parse_if(); config.dmarc_aggregate.max_size = IfBlock::new(4096); config.submitter = IfBlock::new("mx.example.org".to_string()); config.dmarc_aggregate.address = IfBlock::new("reports@example.org".to_string()); diff --git a/tests/src/smtp/reporting/tls.rs b/tests/src/smtp/reporting/tls.rs index 3a4a21ad..eecf5abe 100644 --- a/tests/src/smtp/reporting/tls.rs +++ b/tests/src/smtp/reporting/tls.rs @@ -58,12 +58,12 @@ async fn report_tls() { // Create scheduler let mut core = SMTP::test(); - let ctx = ConfigContext::new(&[]).parse_signatures(); + core.shared.signers = ConfigContext::new(&[]).parse_signatures().signers; let temp_dir = make_temp_dir("smtp_report_tls_test", true); let config = &mut core.report.config; config.path = temp_dir.temp_dir.clone(); config.hash = IfBlock::new(16); - config.tls.sign = "['rsa']".parse_if(); + config.tls.sign = "\"['rsa']\"".parse_if(); config.tls.max_size = IfBlock::new(4096); config.submitter = IfBlock::new("mx.example.org".to_string()); config.tls.address = IfBlock::new("reports@example.org".to_string());