pub mod arf;
pub mod dmarc;
pub mod tlsrpt;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, net::IpAddr};
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct DateRange {
pub begin: u64,
pub end: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct ReportMetadata {
pub org_name: String,
pub email: String,
pub extra_contact_info: Option<String>,
pub report_id: String,
pub date_range: DateRange,
pub error: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum Alignment {
Relaxed,
Strict,
#[default]
Unspecified,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum Disposition {
None,
Quarantine,
Reject,
#[default]
Unspecified,
}
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum ActionDisposition {
None,
Pass,
Quarantine,
Reject,
#[default]
Unspecified,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct PolicyPublished {
pub domain: String,
pub version_published: Option<f32>,
pub adkim: Alignment,
pub aspf: Alignment,
pub p: Disposition,
pub sp: Disposition,
pub testing: bool,
pub fo: Option<String>,
}
impl Eq for PolicyPublished {}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum DmarcResult {
Pass,
Fail,
#[default]
Unspecified,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum PolicyOverride {
Forwarded,
SampledOut,
TrustedForwarder,
MailingList,
LocalPolicy,
#[default]
Other,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct PolicyOverrideReason {
pub type_: PolicyOverride,
pub comment: Option<String>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct PolicyEvaluated {
pub disposition: ActionDisposition,
pub dkim: DmarcResult,
pub spf: DmarcResult,
pub reason: Vec<PolicyOverrideReason>,
}
#[derive(Debug, Clone, Hash, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct Row {
pub source_ip: Option<IpAddr>,
pub count: u32,
pub policy_evaluated: PolicyEvaluated,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct Extension {
pub name: String,
pub definition: String,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct Identifier {
pub envelope_to: Option<String>,
pub envelope_from: String,
pub header_from: String,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum DkimResult {
#[default]
None,
Pass,
Fail,
Policy,
Neutral,
TempError,
PermError,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct DKIMAuthResult {
pub domain: String,
pub selector: String,
pub result: DkimResult,
pub human_result: Option<String>,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum SPFDomainScope {
Helo,
MailFrom,
#[default]
Unspecified,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum SpfResult {
#[default]
None,
Neutral,
Pass,
Fail,
SoftFail,
TempError,
PermError,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct SPFAuthResult {
pub domain: String,
pub scope: SPFDomainScope,
pub result: SpfResult,
pub human_result: Option<String>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct AuthResult {
pub dkim: Vec<DKIMAuthResult>,
pub spf: Vec<SPFAuthResult>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct Record {
pub row: Row,
pub identifiers: Identifier,
pub auth_results: AuthResult,
pub extensions: Vec<Extension>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct Report {
pub version: f32,
pub report_metadata: ReportMetadata,
pub policy_published: PolicyPublished,
pub record: Vec<Record>,
pub extensions: Vec<Extension>,
}
impl Eq for Report {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
MailParseError,
ReportParseError(String),
UncompressError(String),
NoReportsFound,
}
impl From<String> for Error {
fn from(err: String) -> Self {
Error::ReportParseError(err)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub struct Feedback<'x> {
pub feedback_type: FeedbackType,
pub arrival_date: Option<i64>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub authentication_results: Vec<Cow<'x, str>>,
pub incidents: u32,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub original_envelope_id: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub original_mail_from: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub original_rcpt_to: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub reported_domain: Vec<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub reported_uri: Vec<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub reporting_mta: Option<Cow<'x, str>>,
pub source_ip: Option<IpAddr>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub user_agent: Option<Cow<'x, str>>,
pub version: u32,
pub source_port: u32,
pub auth_failure: AuthFailureType,
pub delivery_result: DeliveryResult,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_adsp_dns: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_canonicalized_body: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_canonicalized_header: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_domain: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_identity: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_selector: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub dkim_selector_dns: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub spf_dns: Option<Cow<'x, str>>,
pub identity_alignment: IdentityAlignment,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub message: Option<Cow<'x, str>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::Map<rkyv::with::AsOwned>))]
pub headers: Option<Cow<'x, str>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum AuthFailureType {
Adsp,
BodyHash,
Revoked,
Signature,
Spf,
Dmarc,
#[default]
Unspecified,
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum IdentityAlignment {
None,
Spf,
Dkim,
DkimSpf,
#[default]
Unspecified,
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum DeliveryResult {
Delivered,
Spam,
Policy,
Reject,
Other,
#[default]
Unspecified,
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, Serialize, Deserialize, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
)]
pub enum FeedbackType {
Abuse,
AuthFailure,
Fraud,
NotSpam,
#[default]
Other,
Virus,
}
impl From<&crate::DkimResult> for AuthFailureType {
fn from(value: &crate::DkimResult) -> Self {
match value {
crate::DkimResult::Neutral(err)
| crate::DkimResult::Fail(err)
| crate::DkimResult::PermError(err)
| crate::DkimResult::TempError(err) => match err {
crate::Error::FailedBodyHashMatch => AuthFailureType::BodyHash,
crate::Error::RevokedPublicKey => AuthFailureType::Revoked,
_ => AuthFailureType::Signature,
},
crate::DkimResult::Pass | crate::DkimResult::None => AuthFailureType::Signature,
}
}
}