Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
174 lines
5.5 KiB
Rust
174 lines
5.5 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use crate::{
|
|
smtp::session::{TestSession, VerifyResponse},
|
|
utils::{dns::DnsCache, server::TestServerBuilder},
|
|
};
|
|
use mail_auth::{SpfResult, common::parse::TxtRecordParser, spf::Spf};
|
|
use mail_parser::DateTime;
|
|
use registry::{
|
|
schema::structs::{
|
|
Expression, ExpressionMatch, MtaExtensions, MtaStageData, MtaStageEhlo, SenderAuth,
|
|
},
|
|
types::list::List,
|
|
};
|
|
use std::time::{Duration, Instant, SystemTime};
|
|
|
|
#[tokio::test]
|
|
async fn ehlo() {
|
|
let mut test = TestServerBuilder::new("smtp_ehlo_test")
|
|
.await
|
|
.with_http_listener(19005)
|
|
.await
|
|
.disable_services()
|
|
.build()
|
|
.await;
|
|
|
|
// Add test settings
|
|
let admin = test.account("admin");
|
|
admin.mta_no_auth().await;
|
|
admin
|
|
.registry_create_object(MtaExtensions {
|
|
future_release: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.1'".into(),
|
|
then: "1h".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
mt_priority: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.1'".into(),
|
|
then: "nsep".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaStageEhlo {
|
|
reject_non_fqdn: Expression {
|
|
else_: "starts_with(remote_ip, '10.0.0.')".into(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaStageData {
|
|
max_message_size: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.1'".into(),
|
|
then: "1024".into(),
|
|
}]),
|
|
else_: "2048".into(),
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(SenderAuth {
|
|
dmarc_verify: Expression {
|
|
else_: "relaxed".into(),
|
|
..Default::default()
|
|
},
|
|
reverse_ip_verify: Expression {
|
|
else_: "relaxed".into(),
|
|
..Default::default()
|
|
},
|
|
spf_ehlo_verify: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.2'".into(),
|
|
then: "strict".into(),
|
|
}]),
|
|
else_: "relaxed".into(),
|
|
},
|
|
spf_from_verify: Expression {
|
|
else_: "relaxed".into(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin.reload_settings().await;
|
|
test.reload_core();
|
|
|
|
test.server.txt_add(
|
|
"mx1.foobar.org",
|
|
Spf::parse(b"v=spf1 ip4:10.0.0.1 -all").unwrap(),
|
|
Instant::now() + Duration::from_secs(5),
|
|
);
|
|
test.server.txt_add(
|
|
"mx2.foobar.org",
|
|
Spf::parse(b"v=spf1 ip4:10.0.0.2 -all").unwrap(),
|
|
Instant::now() + Duration::from_secs(5),
|
|
);
|
|
|
|
// Reject non-FQDN domains
|
|
let mut session = test.new_mta_session();
|
|
session.data.remote_ip_str = "10.0.0.1".into();
|
|
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;
|
|
|
|
// EHLO capabilities evaluation
|
|
let response = session
|
|
.cmd("EHLO mx1.foobar.org", "250")
|
|
.await
|
|
.assert_contains("SIZE 1024")
|
|
.assert_contains("MT-PRIORITY NSEP")
|
|
.assert_contains("FUTURERELEASE 3600 ")
|
|
.assert_contains("STARTTLS");
|
|
|
|
// The advertised max-future-release-date-time is an RFC 3339 date-time
|
|
let now = SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map_or(0, |d| d.as_secs()) as i64;
|
|
let max_datetime = response
|
|
.iter()
|
|
.find_map(|line| line.split("FUTURERELEASE 3600 ").nth(1))
|
|
.and_then(|v| v.split_whitespace().next())
|
|
.and_then(DateTime::parse_rfc3339)
|
|
.expect("FUTURERELEASE did not advertise an RFC 3339 date-time")
|
|
.to_timestamp();
|
|
assert!(
|
|
((now + 3595)..=(now + 3605)).contains(&max_datetime),
|
|
"unexpected max-future-release-date-time {max_datetime}, now is {now}"
|
|
);
|
|
|
|
// SPF should be a Pass for 10.0.0.1
|
|
assert_eq!(
|
|
session.data.spf_ehlo.as_ref().unwrap().result(),
|
|
SpfResult::Pass
|
|
);
|
|
|
|
// Test SPF strict mode
|
|
session.data.helo_domain = "".into();
|
|
session.data.remote_ip_str = "10.0.0.2".into();
|
|
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();
|
|
session.response().assert_code("550 5.7.23");
|
|
|
|
// EHLO capabilities evaluation
|
|
session.ingest(b"EHLO mx2.foobar.org\r\n").await.unwrap();
|
|
assert_eq!(
|
|
session.data.spf_ehlo.as_ref().unwrap().result(),
|
|
SpfResult::Pass
|
|
);
|
|
session
|
|
.response()
|
|
.assert_code("250")
|
|
.assert_contains("SIZE 2048")
|
|
.assert_not_contains("MT-PRIORITY")
|
|
.assert_not_contains("FUTURERELEASE")
|
|
.assert_not_contains("STARTTLS");
|
|
}
|