mail_auth/common/
message.rs1use mail_parser::{parsers::MessageStream, Address, HeaderName, HeaderValue, Message};
8
9use crate::{arc, common::crypto::HashAlgorithm, dkim, AuthenticatedMessage};
10
11use super::headers::{AuthenticatedHeader, Header, HeaderParser};
12
13impl<'x> AuthenticatedMessage<'x> {
14 pub fn parse(raw_message: &'x [u8]) -> Option<Self> {
15 Self::parse_with_opts(raw_message, true)
16 }
17
18 pub fn from_parsed(parsed: &'x Message<'x>, strict: bool) -> Self {
19 let root = parsed.root_part();
20 let mut message = AuthenticatedMessage {
21 raw_message: parsed.raw_message(),
22 body_offset: root.raw_body_offset(),
23 headers: Vec::with_capacity(root.headers.len()),
24 ..Default::default()
25 };
26
27 for header in root.headers() {
28 let name =
29 &parsed.raw_message[header.offset_field as usize..header.offset_start as usize - 1];
30 let value =
31 &parsed.raw_message[header.offset_start as usize..header.offset_end as usize];
32
33 match &header.name {
34 HeaderName::From => {
35 message.parse_from(&header.value);
36 }
37 HeaderName::Date => {
38 message.date_header_present = true;
39 }
40 HeaderName::Received => {
41 message.received_headers_count += 1;
42 }
43 HeaderName::MessageId => {
44 message.message_id_header_present = true;
45 }
46 HeaderName::DkimSignature => {
47 message.parse_dkim(name, value, strict);
48 }
49 HeaderName::ArcAuthenticationResults => {
50 message.parse_aar(name, value);
51 }
52 HeaderName::ArcSeal => {
53 message.parse_as(name, value);
54 }
55 HeaderName::ArcMessageSignature => {
56 message.parse_ams(name, value, strict);
57 }
58 _ => (),
59 }
60
61 message.headers.push((name, value))
62 }
63
64 message.finalize()
65 }
66
67 pub fn parse_with_opts(raw_message: &'x [u8], strict: bool) -> Option<Self> {
68 let mut message = AuthenticatedMessage {
69 raw_message,
70 ..Default::default()
71 };
72
73 let mut headers = HeaderParser::new(raw_message);
74
75 for (header, value) in &mut headers {
76 let name = match header {
77 AuthenticatedHeader::Ds(name) => {
78 message.parse_dkim(name, value, strict);
79 name
80 }
81 AuthenticatedHeader::Aar(name) => {
82 message.parse_aar(name, value);
83 name
84 }
85 AuthenticatedHeader::Ams(name) => {
86 message.parse_ams(name, value, strict);
87 name
88 }
89 AuthenticatedHeader::As(name) => {
90 message.parse_as(name, value);
91 name
92 }
93 AuthenticatedHeader::From(name) => {
94 message.parse_from(&MessageStream::new(value).parse_address());
95 name
96 }
97 AuthenticatedHeader::Other(name) => name,
98 };
99
100 message.headers.push((name, value));
101 }
102
103 if !message.headers.is_empty() {
104 message.received_headers_count = headers.num_received;
106 message.message_id_header_present = headers.has_message_id;
107 message.date_header_present = headers.has_date;
108
109 if let Some(offset) = headers.body_offset() {
111 message.body_offset = offset as u32;
112 } else {
113 message.body_offset = raw_message.len() as u32;
114 }
115 Some(message.finalize())
116 } else {
117 None
118 }
119 }
120
121 fn parse_dkim(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
122 let signature = match dkim::Signature::parse(value) {
123 Ok(signature) if signature.l == 0 || !strict => {
124 let ha = HashAlgorithm::from(signature.a);
125 if !self
126 .body_hashes
127 .iter()
128 .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
129 {
130 self.body_hashes
131 .push((signature.cb, ha, signature.l, Vec::new()));
132 }
133 Ok(signature)
134 }
135 Ok(_) => Err(crate::Error::SignatureLength),
136 Err(err) => Err(err),
137 };
138
139 self.dkim_headers.push(Header::new(name, value, signature));
140 }
141
142 fn parse_aar(&mut self, name: &'x [u8], value: &'x [u8]) {
143 let results = arc::Results::parse(value);
144 if !self.has_arc_errors {
145 self.has_arc_errors = results.is_err();
146 }
147 self.aar_headers.push(Header::new(name, value, results));
148 }
149
150 fn parse_ams(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
151 let signature = match arc::Signature::parse(value) {
152 Ok(signature) if signature.l == 0 || !strict => {
153 let ha = HashAlgorithm::from(signature.a);
154 if !self
155 .body_hashes
156 .iter()
157 .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
158 {
159 self.body_hashes
160 .push((signature.cb, ha, signature.l, Vec::new()));
161 }
162 Ok(signature)
163 }
164 Ok(_) => {
165 self.has_arc_errors = true;
166 Err(crate::Error::SignatureLength)
167 }
168 Err(err) => {
169 self.has_arc_errors = true;
170 Err(err)
171 }
172 };
173
174 self.ams_headers.push(Header::new(name, value, signature));
175 }
176
177 fn parse_as(&mut self, name: &'x [u8], value: &'x [u8]) {
178 let seal = arc::Seal::parse(value);
179 if !self.has_arc_errors {
180 self.has_arc_errors = seal.is_err();
181 }
182 self.as_headers.push(Header::new(name, value, seal));
183 }
184
185 fn parse_from(&mut self, value: &HeaderValue<'x>) {
186 match value {
187 HeaderValue::Address(Address::List(list)) => {
188 self.from.extend(
189 list.iter()
190 .filter_map(|a| a.address.as_ref().map(|a| a.to_lowercase())),
191 );
192 }
193 HeaderValue::Address(Address::Group(group_list)) => {
194 self.from.extend(group_list.iter().flat_map(|group| {
195 group
196 .addresses
197 .iter()
198 .filter_map(|a| a.address.as_ref().map(|a| a.to_lowercase()))
199 }))
200 }
201 _ => (),
202 }
203 }
204
205 fn finalize(mut self) -> Self {
206 let body = self
207 .raw_message
208 .get(self.body_offset as usize..)
209 .unwrap_or_default();
210
211 for (cb, ha, l, bh) in &mut self.body_hashes {
213 *bh = ha.hash(cb.canonical_body(body, *l)).as_ref().to_vec();
214 }
215
216 if !self.as_headers.is_empty() && !self.has_arc_errors {
218 self.as_headers.sort_unstable_by(|a, b| {
219 a.header
220 .as_ref()
221 .unwrap()
222 .i
223 .cmp(&b.header.as_ref().unwrap().i)
224 });
225 self.ams_headers.sort_unstable_by(|a, b| {
226 a.header
227 .as_ref()
228 .unwrap()
229 .i
230 .cmp(&b.header.as_ref().unwrap().i)
231 });
232 self.aar_headers.sort_unstable_by(|a, b| {
233 a.header
234 .as_ref()
235 .unwrap()
236 .i
237 .cmp(&b.header.as_ref().unwrap().i)
238 });
239 }
240
241 self
242 }
243
244 pub fn received_headers_count(&self) -> usize {
245 self.received_headers_count
246 }
247
248 pub fn has_message_id_header(&self) -> bool {
249 self.message_id_header_present
250 }
251
252 pub fn has_date_header(&self) -> bool {
253 self.date_header_present
254 }
255
256 pub fn raw_message(&self) -> &[u8] {
257 self.raw_message
258 }
259
260 pub fn raw_headers(&self) -> &[u8] {
261 self.raw_message
262 .get(..self.body_offset as usize)
263 .unwrap_or_default()
264 }
265
266 pub fn raw_parsed_headers(&self) -> &[(&[u8], &[u8])] {
267 &self.headers
268 }
269
270 pub fn raw_body(&self) -> &[u8] {
271 self.raw_message
272 .get(self.body_offset as usize..)
273 .unwrap_or_default()
274 }
275
276 pub fn body_offset(&self) -> usize {
277 self.body_offset as usize
278 }
279
280 pub fn froms(&self) -> &[String] {
281 &self.from
282 }
283
284 pub fn from(&self) -> &str {
285 self.from.first().map_or("", |f| f.as_str())
286 }
287}