1#[cfg(feature = "arc")]
18use crate::arc;
19use crate::{
20 Error,
21 crypto::HashAlgorithm,
22 dkim::{self, Canonicalization},
23 dkim2,
24 headers::{AuthenticatedHeader, Header, HeaderParser},
25};
26use mail_parser::{AddressList, HeaderForm, HeaderName, Message};
27
28const EXPECTED_HEADER_COUNT: usize = 32;
29
30#[derive(Debug, Clone, Default, PartialEq, Eq)]
51pub struct AuthenticatedMessage<'x> {
52 pub(crate) headers: Vec<(&'x [u8], &'x [u8])>,
53 pub(crate) from: Vec<String>,
54 pub(crate) raw_message: &'x [u8],
55 pub(crate) body_offset: u32,
56 pub(crate) body_hashes: Vec<(Canonicalization, HashAlgorithm, u64, Vec<u8>)>,
57 pub(crate) dkim_headers: Vec<Header<'x, dkim::Signature>>,
58 pub(crate) dkim2_signatures: Vec<Header<'x, dkim2::Signature>>,
59 pub(crate) dkim2_instances: Vec<Header<'x, dkim2::MessageInstance>>,
60 #[cfg(feature = "arc")]
61 pub(crate) ams_headers: Vec<Header<'x, arc::Signature>>,
62 #[cfg(feature = "arc")]
63 pub(crate) as_headers: Vec<Header<'x, arc::Seal>>,
64 #[cfg(feature = "arc")]
65 pub(crate) aar_headers: Vec<Header<'x, arc::ArcAuthResults>>,
66 pub(crate) received_headers_count: usize,
67 pub(crate) date_header_present: bool,
68 pub(crate) message_id_header_present: bool,
69 pub(crate) errors: Vec<Header<'x, Error>>,
70 pub(crate) has_dkim_errors: bool,
71 #[cfg(feature = "arc")]
72 pub(crate) has_arc_errors: bool,
73 pub(crate) has_dkim2_errors: bool,
74}
75
76impl<'x> AsRef<AuthenticatedMessage<'x>> for AuthenticatedMessage<'x> {
77 fn as_ref(&self) -> &AuthenticatedMessage<'x> {
78 self
79 }
80}
81
82impl<'x> TryFrom<&'x [u8]> for AuthenticatedMessage<'x> {
83 type Error = Error;
84
85 fn try_from(value: &'x [u8]) -> Result<Self, Self::Error> {
86 AuthenticatedMessage::parse(value).ok_or(Error::Parse)
87 }
88}
89
90impl<'x> TryFrom<&'x Vec<u8>> for AuthenticatedMessage<'x> {
91 type Error = Error;
92
93 fn try_from(value: &'x Vec<u8>) -> Result<Self, Self::Error> {
94 AuthenticatedMessage::parse(value).ok_or(Error::Parse)
95 }
96}
97
98impl<'x> AuthenticatedMessage<'x> {
99 pub fn parse(raw_message: &'x [u8]) -> Option<Self> {
104 Self::parse_with_opts(raw_message, None, true)
105 }
106
107 pub fn from_parsed<'y>(parsed: &'y Message<'x>, raw_message: &'x [u8], strict: bool) -> Self {
114 let root = parsed.root_part();
115 let headers = root.headers();
116 let mut message = AuthenticatedMessage {
117 raw_message,
118 body_offset: root.offset_body(),
119 headers: Vec::with_capacity(headers.len()),
120 ..Default::default()
121 };
122 let raw_range = |from: u32, to: u32| {
123 raw_message
124 .get(from as usize..to as usize)
125 .unwrap_or_default()
126 };
127
128 for header in headers {
129 let name = raw_range(
130 header.offset_field(),
131 header.offset_start().saturating_sub(1),
132 );
133 let value = raw_range(header.offset_start(), header.offset_end());
134
135 match header.name() {
136 HeaderName::From => {
137 message.parse_from(header.value().as_address());
138 }
139 HeaderName::Date => {
140 message.date_header_present = true;
141 }
142 HeaderName::Received => {
143 message.received_headers_count += 1;
144 }
145 HeaderName::MessageId => {
146 message.message_id_header_present = true;
147 }
148 HeaderName::DkimSignature => {
149 message.parse_dkim(name, value, strict);
150 }
151 #[cfg(feature = "arc")]
152 HeaderName::ArcAuthenticationResults => {
153 message.parse_aar(name, value);
154 }
155 #[cfg(feature = "arc")]
156 HeaderName::ArcSeal => {
157 message.parse_as(name, value);
158 }
159 #[cfg(feature = "arc")]
160 HeaderName::ArcMessageSignature => {
161 message.parse_ams(name, value, strict);
162 }
163 HeaderName::Dkim2Signature => {
164 message.parse_dkim2_signature(name, value);
165 }
166 HeaderName::MessageInstance => {
167 message.parse_dkim2_instance(name, value);
168 }
169 _ => (),
170 }
171
172 message.headers.push((name, value))
173 }
174
175 message.finalize()
176 }
177
178 pub fn parse_with_opts(
193 raw_message: &'x [u8],
194 prepend_headers: Option<&'x [u8]>,
195 strict: bool,
196 ) -> Option<Self> {
197 let mut message = AuthenticatedMessage {
198 raw_message,
199 headers: Vec::with_capacity(EXPECTED_HEADER_COUNT),
200 ..Default::default()
201 };
202
203 if let Some(headers) = prepend_headers {
204 message.parse_headers(headers, strict);
205 }
206
207 let body_offset = message.parse_headers(raw_message, strict);
208
209 if !message.headers.is_empty() {
210 if let Some(offset) = body_offset {
211 message.body_offset = offset as u32;
212 } else {
213 message.body_offset = raw_message.len() as u32;
214 }
215 Some(message.finalize())
216 } else {
217 None
218 }
219 }
220
221 fn parse_headers(&mut self, headers: &'x [u8], strict: bool) -> Option<usize> {
222 let mut headers = HeaderParser::new(headers);
223
224 for (header, value) in &mut headers {
225 let name = match header {
226 AuthenticatedHeader::Ds(name) => {
227 self.parse_dkim(name, value, strict);
228 name
229 }
230 AuthenticatedHeader::D2s(name) => {
231 self.parse_dkim2_signature(name, value);
232 name
233 }
234 AuthenticatedHeader::D2i(name) => {
235 self.parse_dkim2_instance(name, value);
236 name
237 }
238 #[cfg(feature = "arc")]
239 AuthenticatedHeader::Aar(name) => {
240 self.parse_aar(name, value);
241 name
242 }
243 #[cfg(feature = "arc")]
244 AuthenticatedHeader::Ams(name) => {
245 self.parse_ams(name, value, strict);
246 name
247 }
248 #[cfg(feature = "arc")]
249 AuthenticatedHeader::As(name) => {
250 self.parse_as(name, value);
251 name
252 }
253 AuthenticatedHeader::From(name) => {
254 self.parse_from(HeaderForm::Addresses.parse(value).value().as_address());
255 name
256 }
257 AuthenticatedHeader::Other(name) => name,
258 };
259
260 self.headers.push((name, value));
261 }
262
263 self.received_headers_count += headers.num_received;
264 self.message_id_header_present |= headers.has_message_id;
265 self.date_header_present |= headers.has_date;
266
267 headers.body_offset()
268 }
269
270 fn parse_dkim(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
271 match dkim::Signature::parse(value) {
272 Ok(signature) if signature.l == 0 || !strict => {
273 let ha = HashAlgorithm::from(signature.a);
274 if !self
275 .body_hashes
276 .iter()
277 .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
278 {
279 self.body_hashes
280 .push((signature.cb, ha, signature.l, Vec::new()));
281 }
282 self.dkim_headers.push(Header::new(name, value, signature));
283 }
284 Ok(_) => {
285 self.push_dkim_error(name, value, Error::Dkim(dkim::DkimError::BodyLengthTag));
286 }
287 Err(err) => self.push_dkim_error(name, value, err),
288 }
289 }
290
291 fn parse_dkim2_signature(&mut self, name: &'x [u8], value: &'x [u8]) {
292 match dkim2::Signature::parse(value) {
293 Ok(signature) => self
294 .dkim2_signatures
295 .push(Header::new(name, value, signature)),
296 Err(err) => self.push_dkim2_error(name, value, err),
297 }
298 }
299
300 fn parse_dkim2_instance(&mut self, name: &'x [u8], value: &'x [u8]) {
301 match dkim2::MessageInstance::parse(value) {
302 Ok(instance) => self
303 .dkim2_instances
304 .push(Header::new(name, value, instance)),
305 Err(err) => self.push_dkim2_error(name, value, err),
306 }
307 }
308
309 #[cfg(feature = "arc")]
310 fn parse_aar(&mut self, name: &'x [u8], value: &'x [u8]) {
311 match arc::ArcAuthResults::parse(value) {
312 Ok(results) => self.aar_headers.push(Header::new(name, value, results)),
313 Err(err) => self.push_arc_error(name, value, err),
314 }
315 }
316
317 #[cfg(feature = "arc")]
318 fn parse_ams(&mut self, name: &'x [u8], value: &'x [u8], strict: bool) {
319 match arc::Signature::parse(value) {
320 Ok(signature) if signature.l == 0 || !strict => {
321 let ha = HashAlgorithm::from(signature.a);
322 if !self
323 .body_hashes
324 .iter()
325 .any(|(c, h, l, _)| c == &signature.cb && h == &ha && l == &signature.l)
326 {
327 self.body_hashes
328 .push((signature.cb, ha, signature.l, Vec::new()));
329 }
330 self.ams_headers.push(Header::new(name, value, signature));
331 }
332 Ok(_) => {
333 self.push_arc_error(name, value, Error::Arc(arc::ArcError::BodyLengthTag));
334 }
335 Err(err) => self.push_arc_error(name, value, err),
336 }
337 }
338
339 #[cfg(feature = "arc")]
340 fn parse_as(&mut self, name: &'x [u8], value: &'x [u8]) {
341 match arc::Seal::parse(value) {
342 Ok(seal) => self.as_headers.push(Header::new(name, value, seal)),
343 Err(err) => self.push_arc_error(name, value, err),
344 }
345 }
346
347 fn push_dkim_error(&mut self, name: &'x [u8], value: &'x [u8], err: Error) {
348 self.has_dkim_errors = true;
349 self.errors.push(Header::new(name, value, err));
350 }
351
352 fn push_dkim2_error(&mut self, name: &'x [u8], value: &'x [u8], err: Error) {
353 self.has_dkim2_errors = true;
354 self.errors.push(Header::new(name, value, err));
355 }
356
357 #[cfg(feature = "arc")]
358 fn push_arc_error(&mut self, name: &'x [u8], value: &'x [u8], err: Error) {
359 self.has_arc_errors = true;
360 self.errors.push(Header::new(name, value, err));
361 }
362
363 fn parse_from(&mut self, addresses: Option<AddressList<'_>>) {
364 if let Some(addresses) = addresses {
365 self.from.extend(
366 addresses
367 .mailboxes()
368 .filter_map(|mailbox| mailbox.address().map(str::to_lowercase)),
369 );
370 }
371 }
372
373 fn finalize(mut self) -> Self {
374 let body = self
375 .raw_message
376 .get(self.body_offset as usize..)
377 .unwrap_or_default();
378
379 for (cb, ha, l, bh) in &mut self.body_hashes {
380 *bh = ha.hash(cb.canonical_body(body, *l)).as_ref().to_vec();
381 }
382
383 #[cfg(feature = "arc")]
384 if !self.as_headers.is_empty() && !self.has_arc_errors {
385 self.as_headers.sort_unstable_by_key(|h| h.header.i);
386 self.ams_headers.sort_unstable_by_key(|h| h.header.i);
387 self.aar_headers.sort_unstable_by_key(|h| h.header.i);
388 }
389
390 self.dkim2_signatures.sort_unstable_by_key(|h| h.header.i);
391 self.dkim2_instances.sort_unstable_by_key(|h| h.header.m);
392
393 self
394 }
395
396 pub fn received_headers_count(&self) -> usize {
398 self.received_headers_count
399 }
400
401 pub fn has_message_id_header(&self) -> bool {
403 self.message_id_header_present
404 }
405
406 pub fn has_date_header(&self) -> bool {
408 self.date_header_present
409 }
410
411 pub fn raw_message(&self) -> &[u8] {
413 self.raw_message
414 }
415
416 pub fn raw_headers(&self) -> &[u8] {
420 self.raw_message
421 .get(..self.body_offset as usize)
422 .unwrap_or_default()
423 }
424
425 pub fn headers(&self) -> &[(&'x [u8], &'x [u8])] {
429 &self.headers
430 }
431
432 pub fn raw_body(&self) -> &[u8] {
434 self.raw_message
435 .get(self.body_offset as usize..)
436 .unwrap_or_default()
437 }
438
439 pub fn body_offset(&self) -> usize {
442 self.body_offset as usize
443 }
444
445 pub fn from_addresses(&self) -> &[String] {
448 &self.from
449 }
450
451 pub fn first_from_address(&self) -> &str {
454 self.from.first().map_or("", |f| f.as_str())
455 }
456
457 pub fn dkim_signatures(&self) -> &[Header<'x, dkim::Signature>] {
460 &self.dkim_headers
461 }
462
463 pub fn dkim2_signatures(&self) -> &[Header<'x, dkim2::Signature>] {
466 &self.dkim2_signatures
467 }
468
469 pub fn dkim2_instances(&self) -> &[Header<'x, dkim2::MessageInstance>] {
472 &self.dkim2_instances
473 }
474
475 #[cfg(feature = "arc")]
479 pub fn arc_message_signatures(&self) -> &[Header<'x, arc::Signature>] {
480 &self.ams_headers
481 }
482
483 #[cfg(feature = "arc")]
486 pub fn arc_seals(&self) -> &[Header<'x, arc::Seal>] {
487 &self.as_headers
488 }
489
490 #[cfg(feature = "arc")]
494 pub fn arc_authentication_results(&self) -> &[Header<'x, arc::ArcAuthResults>] {
495 &self.aar_headers
496 }
497
498 pub fn errors(&self) -> &[Header<'x, Error>] {
501 &self.errors
502 }
503
504 pub fn has_dkim_errors(&self) -> bool {
508 self.has_dkim_errors
509 }
510
511 pub fn has_dkim2_errors(&self) -> bool {
514 self.has_dkim2_errors
515 }
516
517 #[cfg(feature = "arc")]
520 pub fn has_arc_errors(&self) -> bool {
521 self.has_arc_errors
522 }
523
524 pub fn retain_dkim_signatures(
535 &mut self,
536 mut accept: impl FnMut(&dkim::Signature) -> Result<(), dkim::DkimError>,
537 ) {
538 let errors = &mut self.errors;
539 let mut rejected = false;
540 self.dkim_headers
541 .retain(|header| match accept(&header.header) {
542 Ok(()) => true,
543 Err(err) => {
544 errors.push(Header::new(header.name, header.value, Error::Dkim(err)));
545 rejected = true;
546 false
547 }
548 });
549 self.has_dkim_errors |= rejected;
550 }
551}