Increase SMTP transaction logging details

This commit is contained in:
mdecimus 2024-08-21 16:12:01 +02:00
parent 6d76b26fb8
commit 147d9ded86
47 changed files with 395 additions and 312 deletions

85
Cargo.lock generated
View file

@ -411,33 +411,6 @@ dependencies = [
"url",
]
[[package]]
name = "aws-lc-rs"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77"
dependencies = [
"aws-lc-sys",
"mirai-annotations",
"paste",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703"
dependencies = [
"bindgen 0.69.4",
"cc",
"cmake",
"dunce",
"fs_extra",
"libc",
"paste",
]
[[package]]
name = "aws-region"
version = "0.25.4"
@ -598,9 +571,9 @@ dependencies = [
[[package]]
name = "bindgen"
version = "0.70.0"
version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0127a1da21afb5adaae26910922c3f7afd3d329ba1a1b98a0884cab4907a251"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
"bitflags 2.6.0",
"cexpr",
@ -1787,12 +1760,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "dyn-clone"
version = "1.0.17"
@ -2222,12 +2189,6 @@ dependencies = [
"syn 2.0.75",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "funty"
version = "2.0.0"
@ -3260,7 +3221,7 @@ dependencies = [
"nlp",
"p256",
"pkcs8",
"quick-xml 0.35.0",
"quick-xml 0.36.1",
"rand",
"rasn",
"rasn-cms",
@ -3826,6 +3787,18 @@ dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.2"
@ -3838,12 +3811,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "mirai-annotations"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1"
[[package]]
name = "mysql-common-derive"
version = "0.31.1"
@ -3864,9 +3831,9 @@ dependencies = [
[[package]]
name = "mysql_async"
version = "0.34.2"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0b66e411c31265e879d9814d03721f2daa7ad07337b6308cb4bb0cde7e6fd47"
checksum = "fbfe87d7e35cb72363326216cc1712b865d8d4f70abf3b2d2e6b251fb6b2f427"
dependencies = [
"bytes",
"crossbeam",
@ -3875,20 +3842,23 @@ dependencies = [
"futures-sink",
"futures-util",
"keyed_priority_queue",
"lazy_static",
"lru",
"mio 0.8.11",
"mysql_common",
"once_cell",
"pem",
"percent-encoding",
"pin-project",
"rand",
"rustls 0.23.12",
"rustls 0.22.4",
"rustls-pemfile 2.1.3",
"serde",
"serde_json",
"socket2",
"thiserror",
"tokio",
"tokio-rustls 0.26.0",
"tokio-rustls 0.25.0",
"tokio-util",
"twox-hash",
"url",
@ -3904,7 +3874,7 @@ checksum = "478b0ff3f7d67b79da2b96f56f334431aef65e15ba4b29dd74a4236e29582bdc"
dependencies = [
"base64 0.21.7",
"bigdecimal",
"bindgen 0.70.0",
"bindgen 0.70.1",
"bitflags 2.6.0",
"bitvec",
"btoi",
@ -4773,9 +4743,9 @@ dependencies = [
[[package]]
name = "quick-xml"
version = "0.35.0"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86e446ed58cef1bbfe847bc2fda0e2e4ea9f0e57b90c507d4781292590d72a4e"
checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc"
dependencies = [
"memchr",
]
@ -5526,8 +5496,6 @@ version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring 0.17.8",
"rustls-pki-types",
@ -5602,7 +5570,6 @@ version = "0.102.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
dependencies = [
"aws-lc-rs",
"ring 0.17.8",
"rustls-pki-types",
"untrusted 0.9.0",
@ -6588,7 +6555,7 @@ dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"mio 1.0.2",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",

View file

@ -340,12 +340,12 @@ impl SpanCollector {
fn into_vec(self) -> Vec<u64> {
match self {
Self::Vec(mut vec) => {
vec.sort_unstable();
vec.sort_unstable_by(|a, b| b.cmp(a));
vec
}
Self::HashSet(set) => {
let mut vec: Vec<u64> = set.into_iter().collect();
vec.sort_unstable();
vec.sort_unstable_by(|a, b| b.cmp(a));
vec
}
Self::Empty => Vec::new(),

View file

@ -56,7 +56,7 @@ async-trait = "0.1.68"
lz4_flex = { version = "0.11", default-features = false }
rev_lines = "0.3.0"
x509-parser = "0.16.0"
quick-xml = "0.35"
quick-xml = "0.36"
[features]

View file

@ -617,3 +617,60 @@ pub enum StartTlsResult {
smtp_client: SmtpClient<TcpStream>,
},
}
pub(crate) fn from_mail_send_error(error: &mail_send::Error) -> trc::Error {
let event = trc::EventType::Smtp(trc::SmtpEvent::Error).into_err();
match error {
mail_send::Error::Io(err) => event.details("I/O Error").reason(err),
mail_send::Error::Tls(err) => event.details("TLS Error").reason(err),
mail_send::Error::Base64(err) => event.details("Base64 Error").reason(err),
mail_send::Error::Auth(err) => event.details("SMTP Authentication Error").reason(err),
mail_send::Error::UnparseableReply => event.details("Unparseable SMTP Reply"),
mail_send::Error::UnexpectedReply(reply) => event
.details("Unexpected SMTP Response")
.ctx(trc::Key::Code, reply.code)
.ctx(trc::Key::Reason, reply.message.clone()),
mail_send::Error::AuthenticationFailed(reply) => event
.details("SMTP Authentication Failed")
.ctx(trc::Key::Code, reply.code)
.ctx(trc::Key::Reason, reply.message.clone()),
mail_send::Error::InvalidTLSName => event.details("Invalid TLS Name"),
mail_send::Error::MissingCredentials => event.details("Missing Authentication Credentials"),
mail_send::Error::MissingMailFrom => event.details("Missing Message Sender"),
mail_send::Error::MissingRcptTo => event.details("Missing Message Recipients"),
mail_send::Error::UnsupportedAuthMechanism => {
event.details("Unsupported Authentication Mechanism")
}
mail_send::Error::Timeout => event.details("Connection Timeout"),
mail_send::Error::MissingStartTls => event.details("STARTTLS not available"),
}
}
pub(crate) fn from_error_status(status: &Status<(), Error>) -> trc::Error {
let event = trc::EventType::Smtp(trc::SmtpEvent::Error).into_err();
let err = match status {
Status::TemporaryFailure(err) | Status::PermanentFailure(err) => err,
Status::Scheduled | Status::Completed(_) => return event, // This should not happen
};
match err {
Error::DnsError(err) => event.details("DNS Error").reason(err),
Error::UnexpectedResponse(reply) => event
.details("Unexpected SMTP Response")
.ctx(trc::Key::Code, reply.response.code)
.ctx(trc::Key::Reason, reply.response.message.clone()),
Error::ConnectionError(err) => event
.details("Connection Error")
.ctx(trc::Key::Reason, err.details.clone()),
Error::TlsError(err) => event
.details("TLS Error")
.ctx(trc::Key::Reason, err.details.clone()),
Error::DaneError(err) => event
.details("DANE Error")
.ctx(trc::Key::Reason, err.details.clone()),
Error::MtaStsError(err) => event.details("MTA-STS Error").reason(err),
Error::RateLimited => todo!(),
Error::ConcurrencyLimited => todo!(),
Error::Io(err) => event.details("I/O Error").reason(err),
}
}

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::outbound::client::SmtpClient;
use crate::outbound::client::{from_error_status, from_mail_send_error, SmtpClient};
use crate::outbound::mta_sts::verify::VerifyPolicy;
use crate::outbound::{client::StartTlsResult, dane::verify::TlsaVerify};
use common::config::{
@ -325,7 +325,15 @@ impl DeliveryAttempt {
TlsRpt(TlsRptEvent::RecordFetch),
SpanId = message.span_id,
Domain = domain.domain.clone(),
Details = format!("{record:?}"),
Details = record
.rua
.iter()
.map(|uri| trc::Value::from(match uri {
mail_auth::mta_sts::ReportUri::Mail(uri)
| mail_auth::mta_sts::ReportUri::Http(uri) =>
uri.to_string(),
}))
.collect::<Vec<_>>(),
Elapsed = time.elapsed(),
);
@ -364,7 +372,12 @@ impl DeliveryAttempt {
MtaSts(MtaStsEvent::PolicyFetch),
SpanId = message.span_id,
Domain = domain.domain.clone(),
Details = mta_sts_policy.to_string(),
Strict = mta_sts_policy.enforce(),
Details = mta_sts_policy
.mx
.iter()
.map(|mx| trc::Value::String(mx.to_string()))
.collect::<Vec<_>>(),
Elapsed = time.elapsed(),
);
@ -885,7 +898,7 @@ impl DeliveryAttempt {
LocalIp = source_ip,
RemoteIp = remote_ip,
RemotePort = remote_host.port(),
Reason = err.to_string(),
CausedBy = from_mail_send_error(&err),
Elapsed = time.elapsed(),
);
@ -1018,11 +1031,17 @@ impl DeliveryAttempt {
Hostname = envelope.mx.to_string(),
Version = format!(
"{:?}",
smtp_client.tls_connection().protocol_version()
smtp_client
.tls_connection()
.protocol_version()
.unwrap()
),
Details = format!(
"{:?}",
smtp_client.tls_connection().negotiated_cipher_suite()
smtp_client
.tls_connection()
.negotiated_cipher_suite()
.unwrap()
),
Elapsed = time.elapsed(),
);
@ -1097,7 +1116,12 @@ impl DeliveryAttempt {
SpanId = message.span_id,
Domain = domain.domain.clone(),
Hostname = envelope.mx.to_string(),
Details = reason.clone(),
Code = response.as_ref().map(|r| r.code()),
Details = response
.as_ref()
.map(|r| r.message().as_str())
.unwrap_or("STARTTLS was not advertised by host")
.to_string(),
Elapsed = time.elapsed(),
);
@ -1141,7 +1165,7 @@ impl DeliveryAttempt {
SpanId = message.span_id,
Domain = domain.domain.clone(),
Hostname = envelope.mx.to_string(),
Reason = error.to_string(),
Reason = from_mail_send_error(&error),
Elapsed = time.elapsed(),
);
@ -1206,7 +1230,7 @@ impl DeliveryAttempt {
SpanId = message.span_id,
Domain = domain.domain.clone(),
Hostname = envelope.mx.to_string(),
Reason = format!("{error:?}"),
Reason = from_mail_send_error(&error),
);
last_status = Status::from_tls_error(envelope.mx, error);
@ -1226,7 +1250,7 @@ impl DeliveryAttempt {
SpanId = message.span_id,
Domain = domain.domain.clone(),
Hostname = envelope.mx.to_string(),
Details = status.to_string(),
Details = from_error_status(&status),
);
last_status = status;
@ -1333,12 +1357,12 @@ impl Message {
for (idx, domain) in self.domains.iter_mut().enumerate() {
match &domain.status {
Status::TemporaryFailure(err) if domain.expires <= now => {
Status::TemporaryFailure(_) if domain.expires <= now => {
trc::event!(
Delivery(DeliveryEvent::Failed),
SpanId = self.span_id,
Domain = domain.domain.clone(),
Reason = err.to_string(),
Reason = from_error_status(&domain.status),
);
for rcpt in &mut self.recipients {

View file

@ -5,7 +5,7 @@
*/
use common::config::smtp::queue::RequireOptional;
use mail_send::{smtp::AssertReply, Credentials};
use mail_send::Credentials;
use smtp_proto::{
EhloResponse, Severity, EXT_CHUNKING, EXT_DSN, EXT_REQUIRE_TLS, EXT_SIZE, EXT_SMTP_UTF8,
MAIL_REQUIRETLS, MAIL_RET_FULL, MAIL_RET_HDRS, MAIL_SMTPUTF8, RCPT_NOTIFY_DELAY,
@ -16,6 +16,7 @@ use std::{fmt::Write, time::Instant};
use tokio::io::{AsyncRead, AsyncWrite};
use trc::DeliveryEvent;
use crate::outbound::client::{from_error_status, from_mail_send_error};
use crate::{
core::SMTP,
queue::{ErrorDetails, HostResponse, RCPT_STATUS_CHANGED},
@ -64,7 +65,7 @@ impl Message {
Delivery(DeliveryEvent::EhloRejected),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = status.to_string(),
CausedBy = from_error_status(&status),
Elapsed = time.elapsed(),
);
smtp_client.quit().await;
@ -80,7 +81,7 @@ impl Message {
Delivery(DeliveryEvent::AuthFailed),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = err.to_string(),
CausedBy = from_mail_send_error(&err),
Elapsed = time.elapsed(),
);
@ -117,30 +118,37 @@ impl Message {
let time = Instant::now();
smtp_client.timeout = params.timeout_mail;
let cmd = self.build_mail_from(&capabilities);
if let Err(err) = smtp_client
.cmd(cmd.as_bytes())
.await
.and_then(|r| r.assert_positive_completion())
{
match smtp_client.cmd(cmd.as_bytes()).await.and_then(|r| {
if r.is_positive_completion() {
Ok(r)
} else {
Err(mail_send::Error::UnexpectedReply(r))
}
}) {
Ok(response) => {
trc::event!(
Delivery(DeliveryEvent::MailFrom),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
From = self.return_path.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
}
Err(err) => {
trc::event!(
Delivery(DeliveryEvent::MailFromRejected),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = err.to_string(),
CausedBy = from_mail_send_error(&err),
Elapsed = time.elapsed(),
);
smtp_client.quit().await;
return Status::from_smtp_error(params.hostname, &cmd, err);
}
trc::event!(
Delivery(DeliveryEvent::MailFrom),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
From = self.return_path.to_string(),
Elapsed = time.elapsed(),
);
}
// RCPT TO
let mut total_rcpt = 0;
@ -167,7 +175,8 @@ impl Message {
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
To = rcpt.address.to_string(),
Details = response.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
@ -185,7 +194,8 @@ impl Message {
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
To = rcpt.address.to_string(),
Reason = response.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
@ -211,7 +221,7 @@ impl Message {
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
To = rcpt.address.to_string(),
Reason = err.to_string(),
CausedBy = from_mail_send_error(&err),
Elapsed = time.elapsed(),
);
@ -234,7 +244,7 @@ impl Message {
Delivery(DeliveryEvent::MessageRejected),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = status.to_string(),
CausedBy = from_error_status(&status),
Elapsed = time.elapsed(),
);
@ -257,7 +267,8 @@ impl Message {
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
To = rcpt.address.to_string(),
Details = status.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
@ -270,7 +281,8 @@ impl Message {
Delivery(DeliveryEvent::MessageRejected),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = response.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
@ -287,7 +299,7 @@ impl Message {
Delivery(DeliveryEvent::MessageRejected),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = status.to_string(),
CausedBy = from_error_status(&status),
Elapsed = time.elapsed(),
);
@ -311,7 +323,8 @@ impl Message {
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
To = rcpt.address.to_string(),
Details = response.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
@ -327,7 +340,8 @@ impl Message {
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
To = rcpt.address.to_string(),
Reason = response.to_string(),
Code = response.code,
Details = response.message.to_string(),
Elapsed = time.elapsed(),
);
@ -356,7 +370,7 @@ impl Message {
Delivery(DeliveryEvent::MessageRejected),
SpanId = params.session_id,
Hostname = params.hostname.to_string(),
Reason = status.to_string(),
CausedBy = from_error_status(&status),
Elapsed = time.elapsed(),
);

View file

@ -17,6 +17,7 @@ use std::time::Duration;
use store::write::now;
use crate::core::SMTP;
use crate::outbound::client::from_error_status;
use super::{
Domain, Error, ErrorDetails, HostResponse, Message, MessageSource, QueueEnvelope, Recipient,
@ -79,7 +80,8 @@ impl SMTP {
SpanId = message.span_id,
To = rcpt.address_lcase.clone(),
Hostname = response.hostname.clone(),
Details = response.response.to_string(),
Code = response.response.code,
Details = response.response.message.to_string(),
);
}
Status::TemporaryFailure(response) if domain.notify.due <= now => {
@ -88,7 +90,8 @@ impl SMTP {
SpanId = message.span_id,
To = rcpt.address_lcase.clone(),
Hostname = response.hostname.entity.clone(),
Details = response.response.to_string(),
Code = response.response.code,
Details = response.response.message.to_string(),
NextRetry = trc::Value::Timestamp(domain.retry.due),
Expires = trc::Value::Timestamp(domain.expires),
Total = domain.retry.inner,
@ -100,28 +103,29 @@ impl SMTP {
SpanId = message.span_id,
To = rcpt.address_lcase.clone(),
Hostname = response.hostname.entity.clone(),
Details = response.response.to_string(),
Code = response.response.code,
Details = response.response.message.to_string(),
Total = domain.retry.inner,
);
}
Status::Scheduled => {
// There is no status for this address, use the domain's status.
match &domain.status {
Status::PermanentFailure(err) => {
Status::PermanentFailure(_) => {
trc::event!(
Delivery(trc::DeliveryEvent::DsnPermFail),
SpanId = message.span_id,
To = rcpt.address_lcase.clone(),
Details = err.to_string(),
Details = from_error_status(&domain.status),
Total = domain.retry.inner,
);
}
Status::TemporaryFailure(err) if domain.notify.due <= now => {
Status::TemporaryFailure(_) if domain.notify.due <= now => {
trc::event!(
Delivery(trc::DeliveryEvent::DsnTempFail),
SpanId = message.span_id,
To = rcpt.address_lcase.clone(),
Details = err.to_string(),
Details = from_error_status(&domain.status),
NextRetry = trc::Value::Timestamp(domain.retry.due),
Expires = trc::Value::Timestamp(domain.expires),
Total = domain.retry.inner,

View file

@ -34,7 +34,7 @@ rustls = { version = "0.23.5", optional = true, default-features = false, featur
rustls-pki-types = { version = "1", optional = true }
ring = { version = "0.17", optional = true }
bytes = { version = "1.0", optional = true }
mysql_async = { version = "0.34", default-features = false, features = ["default-rustls"], optional = true }
mysql_async = { version = "=0.34.1", default-features = false, features = ["default-rustls"], optional = true }
elasticsearch = { version = "8.5.0-alpha.1", default-features = false, features = ["rustls-tls"], optional = true }
serde_json = {version = "1.0.64", optional = true }
regex = "1.7.0"

View file

@ -6,7 +6,7 @@
use std::{borrow::Cow, fmt::Debug, str::FromStr, time::Duration};
use mail_auth::common::headers::HeaderWriter;
use mail_auth::common::verify::VerifySignature;
use crate::*;
@ -324,15 +324,8 @@ impl From<&mail_auth::DmarcResult> for Event<EventType> {
impl From<&mail_auth::DkimOutput<'_>> for Event<EventType> {
fn from(value: &mail_auth::DkimOutput<'_>) -> Self {
Event::from(value.result()).ctx_opt(
Key::Contents,
value.signature().map(|s| {
let mut buf = Vec::new();
s.write_header(&mut buf);
String::from_utf8(buf)
.map(Value::String)
.unwrap_or_else(|err| Value::Bytes(err.into_bytes()))
}),
Key::Domain,
value.signature().map(|s| s.domain().to_string()),
)
}
}

View file

@ -10,12 +10,23 @@
use std::time::Duration;
use common::enterprise::{license::LicenseKey, undelete::DeletedBlob, Enterprise};
use common::{
config::telemetry::{StoreTracer, TelemetrySubscriberType},
enterprise::{license::LicenseKey, undelete::DeletedBlob, Enterprise},
telemetry::tracers::store::{TracingQuery, TracingStore},
};
use imap_proto::ResponseType;
use jmap::api::management::enterprise::undelete::{UndeleteRequest, UndeleteResponse};
use store::write::now;
use trc::{
ipc::{bitset::Bitset, subscriber::SubscriberBuilder},
DeliveryEvent, EventType, SmtpEvent,
};
use crate::imap::{ImapConnection, Type};
use crate::{
imap::{ImapConnection, Type},
jmap::delivery::SmtpConnection,
};
use super::{delivery::AssertResult, JMAPTest, ManagementApi};
@ -37,8 +48,14 @@ pub async fn test(params: &mut JMAPTest) {
params.server.shared_core.store(core.into());
assert!(params.server.shared_core.load().is_enterprise_edition());
// Undelete
// Create test account
params
.directory
.create_test_user_with_email("jdoe@example.com", "secret", "John Doe")
.await;
undelete(params).await;
tracing(params).await;
// Disable Enterprise
let mut core = params.server.shared_core.load_full().as_ref().clone();
@ -53,20 +70,99 @@ Subject: undelete test
test
";
#[derive(serde::Deserialize, Debug)]
#[allow(dead_code)]
pub(super) struct List<T> {
pub items: Vec<T>,
pub total: usize,
async fn tracing(params: &mut JMAPTest) {
// Enable tracing
let store = params.server.core.storage.data.clone();
TelemetrySubscriberType::StoreTracer(StoreTracer {
store: store.clone(),
})
.spawn(
SubscriberBuilder::new("store-tracer".to_string()).with_interests(Box::new(Bitset::all())),
true,
);
// Make sure there are no span entries in the db
assert_eq!(
store
.query_spans(
&[TracingQuery::EventType(EventType::Smtp(
SmtpEvent::ConnectionStart
))],
0,
0
)
.await
.unwrap(),
Vec::<u64>::new()
);
// Send an email
let mut lmtp = SmtpConnection::connect().await;
lmtp.ingest(
"bill@example.com",
&["jdoe@example.com"],
concat!(
"From: bill@example.com\r\n",
"To: jdoe@example.com\r\n",
"Subject: TPS Report\r\n",
"X-Spam-Status: No\r\n",
"\r\n",
"I'm going to need those TPS reports ASAP. ",
"So, if you could do that, that'd be great."
),
)
.await;
lmtp.quit().await;
tokio::time::sleep(Duration::from_millis(200)).await;
// Purge should not delete anything at this point
store.purge_spans(Duration::from_secs(1)).await.unwrap();
// There should be a span entry in the db
for span_type in [
EventType::Delivery(DeliveryEvent::AttemptStart),
EventType::Smtp(SmtpEvent::ConnectionStart),
] {
let spans = store
.query_spans(&[TracingQuery::EventType(span_type)], 0, 0)
.await
.unwrap();
assert_eq!(spans.len(), 1, "{span_type:?}");
assert_eq!(
store.get_span(spans[0]).await.unwrap()[0].inner.typ,
span_type
);
}
async fn undelete(params: &mut JMAPTest) {
// Create test account
params
.directory
.create_test_user_with_email("jdoe@example.com", "secret", "John Doe")
.await;
// Try searching
for keyword in ["bill@example.com", "jdoe@example.com", "example.com"] {
let spans = store
.query_spans(&[TracingQuery::Keywords(keyword.to_string())], 0, 0)
.await
.unwrap();
assert_eq!(spans.len(), 2, "keyword: {keyword}");
assert!(spans[0] > spans[1], "keyword: {keyword}");
}
// Purge should delete the span entries
tokio::time::sleep(Duration::from_millis(800)).await;
store.purge_spans(Duration::from_secs(1)).await.unwrap();
for query in [
TracingQuery::EventType(EventType::Smtp(SmtpEvent::ConnectionStart)),
TracingQuery::EventType(EventType::Delivery(DeliveryEvent::AttemptStart)),
TracingQuery::Keywords("bill@example.com".to_string()),
TracingQuery::Keywords("jdoe@example.com".to_string()),
TracingQuery::Keywords("example.com".to_string()),
] {
assert_eq!(
store.query_spans(&[query], 0, 0).await.unwrap(),
Vec::<u64>::new()
);
}
}
async fn undelete(_params: &mut JMAPTest) {
// Authenticate
let mut imap = ImapConnection::connect(b"_x ").await;
imap.send("AUTHENTICATE PLAIN {32+}\r\nAGpkb2VAZXhhbXBsZS5jb20Ac2VjcmV0")
@ -163,3 +259,10 @@ async fn undelete(params: &mut JMAPTest) {
.await
.assert_contains("Subject: undelete test");
}
#[derive(serde::Deserialize, Debug)]
#[allow(dead_code)]
pub(super) struct List<T> {
pub items: Vec<T>,
pub total: usize,
}

View file

@ -300,7 +300,7 @@ pub async fn jmap_tests() {
)
.await;
webhooks::test(&mut params).await;
/*webhooks::test(&mut params).await;
email_query::test(&mut params, delete).await;
email_get::test(&mut params).await;
email_set::test(&mut params).await;
@ -325,7 +325,7 @@ pub async fn jmap_tests() {
quota::test(&mut params).await;
crypto::test(&mut params).await;
blob::test(&mut params).await;
purge::test(&mut params).await;
purge::test(&mut params).await;*/
enterprise::test(&mut params).await;
if delete {

View file

@ -100,18 +100,8 @@ public-suffix = "file://{LIST_PATH}/public-suffix.dat"
#[tokio::test(flavor = "multi_thread")]
async fn antispam() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(
tracing_subscriber::EnvFilter::builder()
.parse(
"smtp=debug,imap=debug,jmap=debug,store=debug,utils=debug,directory=debug,common=trace",
)
.unwrap(),
)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Prepare config
let tests = [

View file

@ -68,12 +68,7 @@ future-release = [{if = '!is_empty(authenticated_as)', then = '1d'},
#[tokio::test]
async fn auth() {
// Enable logging
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
let tmp_dir = TempDir::new("smtp_auth_test", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();

View file

@ -14,6 +14,9 @@ use crate::smtp::{
#[tokio::test]
async fn basic_commands() {
// Enable logging
crate::enable_logging();
let mut session = Session::test(build_smtp(Core::default(), Inner::default()));
// STARTTLS should be available on clear text connections

View file

@ -102,13 +102,7 @@ enable = true
#[tokio::test]
async fn data() {
// Enable logging
/*let disable = 1;
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
// Create temp dir for queue
let mut inner = Inner::default();

View file

@ -92,6 +92,9 @@ verify = [{if = "sender_domain = 'test.net'", then = 'relaxed'},
#[tokio::test]
async fn dmarc() {
// Enable logging
crate::enable_logging();
let mut inner = Inner::default();
let tmp_dir = TempDir::new("smtp_dmarc_test", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG.to_string() + SIGNATURES)).unwrap();

View file

@ -38,6 +38,9 @@ ehlo = [{if = "remote_ip = '10.0.0.2'", then = 'strict'},
#[tokio::test]
async fn ehlo() {
// Enable logging
crate::enable_logging();
let mut config = Config::new(CONFIG).unwrap();
let core = Core::parse(&mut config, Default::default(), Default::default()).await;
core.smtp.resolvers.dns.txt_add(

View file

@ -29,6 +29,9 @@ duration = [{if = "remote_ip = '10.0.0.3'", then = '500ms'},
#[tokio::test]
async fn limits() {
// Enable logging
crate::enable_logging();
let mut config = Config::new(CONFIG).unwrap();
let core = Core::parse(&mut config, Default::default(), Default::default()).await;

View file

@ -70,6 +70,9 @@ enable = true
#[tokio::test]
async fn mail() {
// Enable logging
crate::enable_logging();
let tmp_dir = TempDir::new("smtp_mail_test", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
let stores = Stores::parse_all(&mut config).await;

View file

@ -99,13 +99,7 @@ stages = ["data"]
#[tokio::test]
async fn milter_session() {
// Enable logging
/*let disable = "true";
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
// Configure tests
let tmp_dir = TempDir::new("smtp_milter_test", true);

View file

@ -86,13 +86,8 @@ enable = true
#[tokio::test]
async fn rcpt() {
// Enable logging
/*
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
let tmp_dir = TempDir::new("smtp_rcpt_test", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
let stores = Stores::parse_all(&mut config).await;

View file

@ -66,12 +66,9 @@ if allof( envelope :localpart :contains "to" ".",
#[tokio::test]
async fn address_rewrite() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Prepare config
let mut config = Config::new(CONFIG).unwrap();

View file

@ -125,13 +125,8 @@ verify = "relaxed"
#[tokio::test]
async fn sign_and_seal() {
// Enable logging
/*let disable = "true";
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
let tmp_dir = TempDir::new("smtp_sign_test", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG.to_string() + SIGNATURES)).unwrap();

View file

@ -45,13 +45,7 @@ enable = true
#[tokio::test]
async fn throttle_inbound() {
// Enable logging
/*let disable = "true";
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
let tmp_dir = TempDir::new("smtp_inbound_throttle", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();

View file

@ -65,6 +65,9 @@ expn = [{if = "remote_ip = '10.0.0.1'", then = true},
#[tokio::test]
async fn vrfy_expn() {
// Enable logging
crate::enable_logging();
let tmp_dir = TempDir::new("smtp_vrfy_test", true);
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
let stores = Stores::parse_all(&mut config).await;

View file

@ -99,13 +99,7 @@ expect = "0-1-2-2"
#[tokio::test]
async fn lookup_sql() {
// Enable logging
/*let disable = true;
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
crate::enable_logging();
// Parse settings
let temp_dir = TempDir::new("smtp_lookup_tests", true);

View file

@ -49,6 +49,9 @@ ip-strategy = "ipv6_then_ipv4"
#[tokio::test]
async fn lookup_ip() {
// Enable logging
crate::enable_logging();
let ipv6 = [
"a:b::1".parse().unwrap(),
"a:b::2".parse().unwrap(),

View file

@ -66,12 +66,9 @@ pub(super) struct List<T> {
#[tokio::test]
#[serial_test::serial]
async fn manage_queue() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start remote test server
let mut remote = TestServer::new("smtp_manage_queue_remote", REMOTE, true).await;

View file

@ -54,12 +54,9 @@ max-size = 1024
#[tokio::test]
#[serial_test::serial]
async fn manage_reports() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::DEBUG)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start reporting service
let local = TestServer::new("smtp_manage_reports", CONFIG, true).await;

View file

@ -82,13 +82,9 @@ return-path = false
#[tokio::test]
#[serial_test::serial]
async fn dane_verify() {
/*let disable = 1;
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_dane_remote", REMOTE, true).await;

View file

@ -50,12 +50,9 @@ return-path = false
#[tokio::test]
#[serial_test::serial]
async fn extensions() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_ext_remote", REMOTE, true).await;

View file

@ -51,13 +51,9 @@ chunking = false
#[tokio::test]
#[serial_test::serial]
async fn fallback_relay() {
/*let disable = 1;
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_fallback_remote", REMOTE, true).await;

View file

@ -30,12 +30,9 @@ relay = true
#[tokio::test]
#[serial_test::serial]
async fn ip_lookup_strategy() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_iplookup_remote", REMOTE, true).await;

View file

@ -62,12 +62,9 @@ allow-invalid-certs = true
#[tokio::test]
#[serial_test::serial]
async fn lmtp_delivery() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("lmtp_delivery_remote", REMOTE, true).await;

View file

@ -59,12 +59,9 @@ return-path = false
#[tokio::test]
#[serial_test::serial]
async fn mta_sts_verify() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_mta_sts_remote", REMOTE, true).await;

View file

@ -70,12 +70,9 @@ This is a smuggled message
#[tokio::test]
#[serial_test::serial]
async fn smtp_delivery() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_delivery_remote", REMOTE, true).await;

View file

@ -66,12 +66,9 @@ enable = true
#[tokio::test]
async fn throttle_outbound() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Build test message
let mut test_message = new_message(0);

View file

@ -43,13 +43,9 @@ chunking = false
#[tokio::test]
#[serial_test::serial]
async fn starttls_optional() {
/*let disable = 1;
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let mut remote = TestServer::new("smtp_starttls_remote", REMOTE, true).await;

View file

@ -33,13 +33,8 @@ relay = true
#[tokio::test]
#[serial_test::serial]
async fn concurrent_queue() {
/*let disable = true;
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::DEBUG)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Start test server
let remote = TestServer::new("smtp_concurrent_queue_remote", REMOTE, true).await;

View file

@ -34,6 +34,9 @@ sign = "['rsa']"
#[tokio::test]
async fn generate_dsn() {
// Enable logging
crate::enable_logging();
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("resources");
path.push("smtp");

View file

@ -23,6 +23,9 @@ relay = true
#[tokio::test]
async fn queue_due() {
// Enable logging
crate::enable_logging();
let local = TestServer::new("smtp_queue_due_test", CONFIG, true).await;
let core = local.build_smtp();
let qr = &local.qr;

View file

@ -35,12 +35,9 @@ expire = [{if = "sender_domain = 'test.org'", then = "6s"},
#[tokio::test]
async fn queue_retry() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::DEBUG)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Create temp dir for queue
let mut local = TestServer::new("smtp_queue_retry_test", CONFIG, true).await;

View file

@ -28,6 +28,9 @@ store = "1s"
#[tokio::test(flavor = "multi_thread")]
async fn report_analyze() {
// Enable logging
crate::enable_logging();
// Create temp dir for queue
let mut local = TestServer::new("smtp_analyze_report_test", CONFIG, true).await;

View file

@ -46,12 +46,9 @@ sign = "['rsa']"
#[tokio::test]
async fn report_dmarc() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::DEBUG)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Create scheduler
let mut local = TestServer::new(

View file

@ -33,12 +33,8 @@ send = "daily"
#[tokio::test]
async fn report_scheduler() {
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::DEBUG)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Create scheduler
let local = TestServer::new("smtp_report_queue_test", CONFIG, true).await;

View file

@ -42,13 +42,8 @@ sign = "['rsa']"
#[tokio::test]
async fn report_tls() {
/*let disable = "true";
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
// Enable logging
crate::enable_logging();
// Create scheduler
let mut local = TestServer::new(

View file

@ -175,7 +175,6 @@ impl TestSession for Session<DummyIo> {
)
.await
.unwrap();
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
self.response().assert_code(expected_code);
}