kumo_log_types/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use crate::rfc5965::ARFReport;
use bounce_classify::BounceClass;
use chrono::{DateTime, Utc};
use kumo_address::host::HostAddress;
use kumo_address::socket::SocketAddress;
use rfc5321::Response;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::borrow::Cow;
use std::collections::HashMap;
use std::net::SocketAddr;
use uuid::Uuid;

pub mod rfc3464;
pub mod rfc5965;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedAddress {
    pub name: String,
    pub addr: HostAddress,
}

impl std::fmt::Display for ResolvedAddress {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let addr = format!("{}", self.addr);
        if addr == self.name {
            // likely: unix domain socket path
            write!(fmt, "{addr}")
        } else {
            write!(fmt, "{}/{addr}", self.name)
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum RecordType {
    /// Recorded by a receiving listener
    Reception,
    /// Recorded by the delivery side, most likely as a
    /// result of attempting a delivery to a remote host
    Delivery,
    Bounce,
    TransientFailure,
    /// Recorded when a message is expiring from the queue
    Expiration,
    /// Administratively failed
    AdminBounce,
    /// Contains information about an OOB bounce
    OOB,
    /// Contains a feedback report
    Feedback,

    /// SMTP Listener responded with a 4xx or 5xx
    Rejection,

    /// Administratively rebound from one queue to another
    AdminRebind,

    /// Moved from the special deferred injection queue
    /// and into some other queue
    DeferredInjectionRebind,

    /// Special for matching anything in the logging config
    Any,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JsonLogRecord {
    /// What kind of record this is
    #[serde(rename = "type")]
    pub kind: RecordType,
    /// The message id
    pub id: String,
    /// The envelope sender
    pub sender: String,
    /// The envelope recipient
    pub recipient: String,
    /// Which named queue the message was associated with
    pub queue: String,
    /// Which MX site the message was being delivered to
    pub site: String,
    /// The size of the message, in bytes
    pub size: u64,
    /// The response from/to the peer
    pub response: Response,
    /// The address of the peer, and our sense of its
    /// hostname or EHLO domain
    pub peer_address: Option<ResolvedAddress>,
    /// The time at which we are logging this event
    #[serde(with = "chrono::serde::ts_seconds")]
    pub timestamp: DateTime<Utc>,
    /// The time at which the message was initially received and created
    #[serde(with = "chrono::serde::ts_seconds")]
    pub created: DateTime<Utc>,
    /// The number of delivery attempts that have been made.
    /// Note that this may be approximate after a restart; use the
    /// number of logged events to determine the true number
    pub num_attempts: u16,

    pub bounce_classification: BounceClass,

    pub egress_pool: Option<String>,
    pub egress_source: Option<String>,
    pub source_address: Option<MaybeProxiedSourceAddress>,

    pub feedback_report: Option<Box<ARFReport>>,

    pub meta: HashMap<String, Value>,
    pub headers: HashMap<String, Value>,

    /// The protocol used to deliver, or attempt to deliver, this message
    pub delivery_protocol: Option<String>,

    /// The protocol used to receive this message
    pub reception_protocol: Option<String>,

    /// The id of the node on which the event occurred
    pub nodeid: Uuid,

    /// The TLS Cipher used, if applicable
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tls_cipher: Option<String>,

    /// The TLS protocol version used, if applicable
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tls_protocol_version: Option<String>,

    /// The Subject Name from the peer TLS certificate, if applicable
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tls_peer_subject_name: Option<Vec<String>>,

    /// The provider name, if any.
    /// This is a way of grouping destination sites operated
    /// by the same provider.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider_name: Option<String>,

    /// Uuid identifying a connection/session for either inbound
    /// or outbound (depending on the type of the record).
    /// This is useful when correlating a series of messages to
    /// the same connection for either ingress or egress
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_id: Option<Uuid>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaybeProxiedSourceAddress {
    pub address: SocketAddress,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub server: Option<SocketAddr>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub protocol: Option<Cow<'static, str>>,
}

#[cfg(all(test, target_pointer_width = "64"))]
#[test]
fn sizes() {
    assert_eq!(std::mem::size_of::<JsonLogRecord>(), 712);
}