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.
392 lines
12 KiB
Rust
392 lines
12 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use crate::{
|
|
smtp::{
|
|
inbound::TestMessage,
|
|
session::{TestSession, VerifyResponse, load_test_message},
|
|
},
|
|
utils::server::TestServerBuilder,
|
|
};
|
|
use registry::{
|
|
schema::{
|
|
enums::MtaQueueQuotaKey,
|
|
prelude::ObjectType,
|
|
structs::{
|
|
Expression, ExpressionMatch, MtaQueueQuota, MtaStageData, SenderAuth, SpamSettings,
|
|
},
|
|
},
|
|
types::{list::List, map::Map},
|
|
};
|
|
|
|
#[tokio::test]
|
|
async fn data() {
|
|
let mut test = TestServerBuilder::new("smtp_data_test")
|
|
.await
|
|
.with_http_listener(19004)
|
|
.await
|
|
.disable_services()
|
|
.capture_queue()
|
|
.build()
|
|
.await;
|
|
|
|
// Create test users
|
|
let admin = test.account("admin");
|
|
for (name, secret, description, aliases) in [
|
|
("[email protected]", "12345 + extra safety", "John Doe", &[]),
|
|
("[email protected]", "abcde + extra safety", "Jane Smith", &[]),
|
|
(
|
|
"[email protected]",
|
|
"p4ssw0rd + extra safety",
|
|
"Bill Foobar",
|
|
&[],
|
|
),
|
|
(
|
|
"[email protected]",
|
|
"p4ssw0rd + extra safety",
|
|
"Mike Foobar",
|
|
&[],
|
|
),
|
|
] {
|
|
admin
|
|
.create_user_account(name, secret, description, aliases, vec![])
|
|
.await;
|
|
}
|
|
|
|
// Add test settings
|
|
admin.mta_no_auth().await;
|
|
admin
|
|
.registry_create_object(SpamSettings {
|
|
enable: false,
|
|
..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 {
|
|
else_: "relaxed".into(),
|
|
..Default::default()
|
|
},
|
|
spf_from_verify: Expression {
|
|
else_: "relaxed".into(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaStageData {
|
|
add_auth_results_header: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.3'".into(),
|
|
then: "true".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
add_date_header: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.3'".into(),
|
|
then: "true".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
add_message_id_header: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.3'".into(),
|
|
then: "true".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
add_received_header: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.3'".into(),
|
|
then: "true".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
add_received_spf_header: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.3'".into(),
|
|
then: "true".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
add_return_path_header: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.3'".into(),
|
|
then: "true".into(),
|
|
}]),
|
|
else_: "false".into(),
|
|
},
|
|
max_messages: Expression {
|
|
match_: List::from_iter([ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.1'".into(),
|
|
then: "1".into(),
|
|
}]),
|
|
else_: "100".into(),
|
|
},
|
|
max_received_headers: Expression {
|
|
else_: "3".into(),
|
|
..Default::default()
|
|
},
|
|
max_message_size: Expression {
|
|
match_: List::from_iter([
|
|
ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.4'".into(),
|
|
then: "100".into(),
|
|
},
|
|
ExpressionMatch {
|
|
if_: "remote_ip = '10.0.0.5'".into(),
|
|
then: "0".into(),
|
|
},
|
|
]),
|
|
else_: "104857600".into(),
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaQueueQuota {
|
|
description: None,
|
|
enable: true,
|
|
key: Map::new(vec![MtaQueueQuotaKey::Sender]),
|
|
match_: Expression {
|
|
else_: "sender = '[email protected]'".into(),
|
|
..Default::default()
|
|
},
|
|
messages: Some(1),
|
|
size: None,
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaQueueQuota {
|
|
description: None,
|
|
enable: true,
|
|
key: Map::new(vec![MtaQueueQuotaKey::RcptDomain]),
|
|
match_: Expression {
|
|
else_: "rcpt_domain = 'foobar.org'".into(),
|
|
..Default::default()
|
|
},
|
|
messages: None,
|
|
size: Some(450),
|
|
})
|
|
.await;
|
|
admin
|
|
.registry_create_object(MtaQueueQuota {
|
|
description: None,
|
|
enable: true,
|
|
key: Map::new(vec![MtaQueueQuotaKey::Rcpt]),
|
|
match_: Expression {
|
|
else_: "rcpt = '[email protected]'".into(),
|
|
..Default::default()
|
|
},
|
|
messages: None,
|
|
size: Some(450),
|
|
})
|
|
.await;
|
|
admin.reload_settings().await;
|
|
test.reload_core();
|
|
test.expect_reload_settings().await;
|
|
|
|
// Test queue message builder
|
|
let mut session = test.new_mta_session();
|
|
session.data.remote_ip_str = "10.0.0.1".into();
|
|
session.eval_session_params().await;
|
|
session.test_builder().await;
|
|
|
|
// Send DATA without RCPT
|
|
session.ehlo("mx.doe.org").await;
|
|
session.ingest(b"DATA\r\n").await.unwrap();
|
|
session.response().assert_code("503 5.5.1");
|
|
|
|
// Send BDAT without MAIL FROM
|
|
session.ingest(b"BDAT 10\r\n0123456789").await.unwrap();
|
|
session
|
|
.response()
|
|
.assert_code("503 5.5.1")
|
|
.assert_contains("MAIL is required")
|
|
.assert_not_contains("552");
|
|
|
|
// Send BDAT without RCPT
|
|
session.mail_from("[email protected]", "250").await;
|
|
session.ingest(b"BDAT 10\r\n0123456789").await.unwrap();
|
|
session
|
|
.response()
|
|
.assert_code("503 5.5.1")
|
|
.assert_contains("RCPT is required")
|
|
.assert_not_contains("552");
|
|
session.rset().await;
|
|
|
|
// Send a BDAT chunk exceeding the maximum message size
|
|
let mut size_session = test.new_mta_session();
|
|
size_session.data.remote_ip_str = "10.0.0.4".into();
|
|
size_session.eval_session_params().await;
|
|
size_session.ehlo("mx.doe.org").await;
|
|
size_session.mail_from("[email protected]", "250").await;
|
|
size_session.rcpt_to("[email protected]", "250").await;
|
|
let mut chunk = b"BDAT 200 LAST\r\n".to_vec();
|
|
chunk.extend_from_slice(&[b'A'; 200]);
|
|
size_session.ingest(&chunk).await.unwrap();
|
|
size_session.response().assert_code("552 5.3.4");
|
|
size_session.rset().await;
|
|
|
|
// A maximum message size of zero disables the limit
|
|
size_session.data.remote_ip_str = "10.0.0.5".into();
|
|
size_session.eval_session_params().await;
|
|
size_session
|
|
.ingest(b"MAIL FROM:<[email protected]> SIZE=1073741824\r\n")
|
|
.await
|
|
.unwrap();
|
|
size_session.response().assert_code("250");
|
|
size_session.rset().await;
|
|
size_session
|
|
.send_message("[email protected]", &["[email protected]"], "test:no_dkim", "250")
|
|
.await;
|
|
test.expect_message().await;
|
|
|
|
// Send broken message
|
|
session
|
|
.send_message("[email protected]", &["[email protected]"], "invalid", "550 5.7.7")
|
|
.await;
|
|
|
|
// Naive Loop detection
|
|
session
|
|
.send_message(
|
|
"[email protected]",
|
|
&["[email protected]"],
|
|
"test:loop",
|
|
"450 4.4.6",
|
|
)
|
|
.await;
|
|
|
|
// No headers should be added to messages from 10.0.0.1
|
|
session
|
|
.send_message("[email protected]", &["[email protected]"], "test:no_msgid", "250")
|
|
.await;
|
|
assert_eq!(
|
|
test.expect_message().await.read_message(&test).await,
|
|
format!("{}\r\n", load_test_message("no_msgid", "messages"))
|
|
);
|
|
|
|
// Maximum one message per session is allowed for 10.0.0.1
|
|
session.mail_from("[email protected]", "250").await;
|
|
session.rcpt_to("[email protected]", "250").await;
|
|
session.ingest(b"DATA\r\n").await.unwrap();
|
|
session.response().assert_code("452 4.4.5");
|
|
session.rset().await;
|
|
|
|
// Headers should be added to messages from 10.0.0.3
|
|
session.data.remote_ip_str = "10.0.0.3".into();
|
|
session.eval_session_params().await;
|
|
session
|
|
.send_message("[email protected]", &["[email protected]"], "test:no_msgid", "250")
|
|
.await;
|
|
test.expect_message()
|
|
.await
|
|
.read_lines(&test)
|
|
.await
|
|
.assert_contains("From: ")
|
|
.assert_contains("To: ")
|
|
.assert_contains("Subject: ")
|
|
.assert_contains("Date: ")
|
|
.assert_contains("Message-ID: ")
|
|
.assert_contains("Return-Path: ")
|
|
.assert_contains("Received: ")
|
|
.assert_contains("Authentication-Results: ")
|
|
.assert_contains("Received-SPF: ");
|
|
|
|
// Send a message using multiple BDAT chunks
|
|
session.mail_from("[email protected]", "250").await;
|
|
session.rcpt_to("[email protected]", "250").await;
|
|
let message = load_test_message("no_msgid", "messages");
|
|
let (first, last) = message.as_bytes().split_at(message.len() / 2);
|
|
let mut chunk = format!("BDAT {}\r\n", first.len()).into_bytes();
|
|
chunk.extend_from_slice(first);
|
|
session.ingest(&chunk).await.unwrap();
|
|
session.response().assert_code("250 2.6.0");
|
|
let mut chunk = format!("BDAT {} LAST\r\n", last.len()).into_bytes();
|
|
chunk.extend_from_slice(last);
|
|
session.ingest(&chunk).await.unwrap();
|
|
session.response().assert_code("250");
|
|
test.expect_message()
|
|
.await
|
|
.read_lines(&test)
|
|
.await
|
|
.assert_contains("Subject: ")
|
|
.assert_contains("Received: ");
|
|
|
|
// Only one message is allowed in the queue from [email protected]
|
|
session.data.remote_ip_str = "10.0.0.2".into();
|
|
session.eval_session_params().await;
|
|
session
|
|
.send_message("[email protected]", &["[email protected]"], "test:no_dkim", "250")
|
|
.await;
|
|
session
|
|
.send_message(
|
|
"[email protected]",
|
|
&["[email protected]"],
|
|
"test:no_dkim",
|
|
"452 4.3.1",
|
|
)
|
|
.await;
|
|
|
|
// Release quota
|
|
test.clear_queue().await;
|
|
|
|
// Only 1500 bytes are allowed in the queue to domain foobar.org
|
|
session
|
|
.send_message(
|
|
"[email protected]",
|
|
&["[email protected]"],
|
|
"test:no_dkim",
|
|
"250",
|
|
)
|
|
.await;
|
|
session
|
|
.send_message(
|
|
"[email protected]",
|
|
&["[email protected]"],
|
|
"test:no_dkim",
|
|
"452 4.3.1",
|
|
)
|
|
.await;
|
|
|
|
// Only 1500 bytes are allowed in the queue to recipient [email protected]
|
|
session
|
|
.send_message(
|
|
"[email protected]",
|
|
&["[email protected]"],
|
|
"test:no_dkim",
|
|
"250",
|
|
)
|
|
.await;
|
|
session
|
|
.send_message(
|
|
"[email protected]",
|
|
&["[email protected]"],
|
|
"test:no_dkim",
|
|
"452 4.3.1",
|
|
)
|
|
.await;
|
|
|
|
// Make sure store is empty
|
|
test.clear_queue().await;
|
|
let admin = test.account("admin");
|
|
admin.registry_destroy_all(ObjectType::MtaQueueQuota).await;
|
|
admin
|
|
.registry_destroy_all(ObjectType::MtaInboundThrottle)
|
|
.await;
|
|
test.assert_is_empty().await;
|
|
}
|