1use crate::{
8 DateTime, Greeting, HeaderValue, Host, Protocol, Received, TlsVersion, parsers::MessageStream,
9};
10use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13enum Token {
14 BracketOpen,
15 BracketClose,
16 AngleOpen,
17 AngleClose,
18 ParenthesisOpen,
19 ParenthesisClose,
20 Semicolon,
21 Colon,
22 Equal,
23 Slash,
24 Quote,
25 Comma,
26 IpAddr(IpAddr),
27 Integer(i64),
28 Text,
29 Domain,
30 Email,
31 Month(Month),
32 Protocol(Protocol),
33 Greeting(Greeting),
34 TlsVersion(TlsVersion),
35 Cipher,
36 By,
37 For,
38 From,
39 Id,
40 Via,
41 With,
42 Ident,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46struct TokenData<'x> {
47 token: Token,
48 text: &'x str,
49 comment_depth: u32,
50 bracket_depth: u32,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54enum Month {
55 Jan,
56 Feb,
57 Mar,
58 Apr,
59 May,
60 Jun,
61 Jul,
62 Aug,
63 Sep,
64 Oct,
65 Nov,
66 Dec,
67}
68
69struct Tokenizer<'x, 'y> {
70 stream: &'y mut MessageStream<'x>,
71 next_token: Option<TokenData<'x>>,
72 eof: bool,
73 in_quote: bool,
74 bracket_depth: u32,
75 comment_depth: u32,
76 in_date: bool,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80enum State {
81 From,
82 By,
83 For,
84 Id,
85 With,
86 Via,
87 Date,
88 None,
89}
90
91impl<'x> MessageStream<'x> {
92 pub fn parse_received(&mut self) -> HeaderValue<'x> {
93 let mut tokenizer = Tokenizer::new(self).peekable();
96 let mut received = Received::default();
97
98 let mut state = State::None;
99 let mut date = [i64::MAX; 7];
100 let mut date_part = date.iter_mut();
101
102 while let Some(token) = tokenizer.next() {
103 match token.token {
104 Token::From if received.from.is_none() => {
105 while let Some(token) = tokenizer.peek() {
107 match token.token {
108 Token::BracketOpen => {
109 tokenizer.next();
110 }
111 Token::IpAddr(ip) => {
112 tokenizer.next();
113 received.from = Some(Host::IpAddr(ip));
114 break;
115 }
116 _ => {
117 if !token.token.is_separator() {
118 received.from =
119 Some(Host::Name(tokenizer.next().unwrap().text.into()));
120 }
121 break;
122 }
123 }
124 }
125 state = State::From;
126 }
127 Token::By if token.comment_depth == 0 => {
128 while let Some(token) = tokenizer.peek() {
130 match token.token {
131 Token::BracketOpen | Token::AngleOpen => {
132 tokenizer.next();
133 }
134 Token::IpAddr(ip) => {
135 tokenizer.next();
136 received.by = Some(Host::IpAddr(ip));
137 break;
138 }
139 _ => {
140 if !token.token.is_separator() {
141 received.by =
142 Some(Host::Name(tokenizer.next().unwrap().text.into()));
143 }
144 break;
145 }
146 }
147 }
148 state = State::By;
149 }
150 Token::For if token.comment_depth == 0 => {
151 while let Some(token) = tokenizer.peek() {
152 match token.token {
153 Token::Equal | Token::AngleOpen => {
154 tokenizer.next();
155 }
156 Token::Email => {
157 received.for_ = Some(tokenizer.next().unwrap().text.into());
158 break;
159 }
160 _ => {
161 break;
162 }
163 }
164 }
165 state = State::For;
166 }
167 Token::Semicolon if token.comment_depth == 0 => {
168 state = State::Date;
169 }
170 Token::Id if token.comment_depth == 0 => {
171 while let Some(token) = tokenizer.peek() {
172 match token.token {
173 Token::Equal | Token::AngleOpen | Token::BracketOpen | Token::Colon => {
174 tokenizer.next();
175 }
176 _ => {
177 if !token.token.is_separator() {
178 received.id = Some(tokenizer.next().unwrap().text.into());
179 }
180 break;
181 }
182 }
183 }
184 state = State::Id;
185 }
186 Token::With if token.comment_depth == 0 => {
187 while let Some(token) = tokenizer.peek() {
188 match token.token {
189 Token::Protocol(proto) => {
190 tokenizer.next();
191 received.with = Some(proto);
192 break;
193 }
194 Token::Semicolon
195 | Token::TlsVersion(_)
196 | Token::By
197 | Token::For
198 | Token::From
199 | Token::Id
200 | Token::Via
201 | Token::With => {
202 break;
203 }
204 _ => {
205 tokenizer.next();
206 }
207 }
208 }
209 state = State::With;
210 }
211 Token::Via if token.comment_depth == 0 => {
212 while let Some(token) = tokenizer.peek() {
213 match token.token {
214 Token::Equal => {
215 tokenizer.next();
216 }
217 _ => {
218 if !token.token.is_separator() {
219 received.via = Some(tokenizer.next().unwrap().text.into());
220 }
221 break;
222 }
223 }
224 }
225 state = State::Via;
226 }
227 Token::Ident if token.comment_depth > 0 => {
228 while let Some(token) = tokenizer.peek() {
229 match token.token {
230 Token::Equal | Token::AngleOpen | Token::BracketOpen | Token::Colon => {
231 tokenizer.next();
232 }
233 _ => {
234 if !token.token.is_separator() {
235 received.ident = Some(tokenizer.next().unwrap().text.into());
236 }
237 break;
238 }
239 }
240 }
241 }
242 Token::Greeting(greeting) if state == State::From && token.comment_depth > 0 => {
243 received.helo_cmd = Some(greeting);
245 while let Some(token) = tokenizer.peek() {
246 match token.token {
247 Token::Equal | Token::BracketOpen | Token::Colon => {
248 tokenizer.next();
249 }
250 Token::IpAddr(ip) => {
251 tokenizer.next();
252 received.helo = Some(Host::IpAddr(ip));
253 break;
254 }
255 _ => {
256 if !token.token.is_separator() {
257 received.helo =
258 Some(Host::Name(tokenizer.next().unwrap().text.into()));
259 }
260 break;
261 }
262 }
263 }
264 }
265 Token::IpAddr(ip)
266 if state == State::From
267 && (token.bracket_depth > 0
268 || (token.comment_depth > 0 && received.from_ip.is_none())) =>
269 {
270 received.from_ip = Some(ip);
271 }
272 Token::Domain if state == State::From && token.comment_depth > 0 => {
273 received.from_iprev = Some(token.text.into());
274 }
275 Token::Email if state == State::From => {
276 received.ident =
277 Some(token.text.strip_suffix('@').unwrap_or(token.text).into());
278 }
279 Token::Integer(num) if state == State::Date => {
280 if let Some(part) = date_part.next() {
281 *part = num;
282 }
283 }
284 Token::Month(month) if state == State::Date => {
285 if let Some(part) = date_part.next() {
286 *part = month.to_number();
287 }
288 }
289 Token::Cipher if token.comment_depth > 0 || received.tls_cipher.is_none() => {
290 received.tls_cipher = Some(token.text.into());
291 }
292 Token::TlsVersion(tls)
293 if token.comment_depth > 0 && received.tls_version.is_none() =>
294 {
295 received.tls_version = Some(tls);
296 }
297 _ => (),
298 }
299 }
300
301 if date[5] != i64::MAX {
302 let (tz, is_plus) = if date[6] != i64::MAX {
303 if date[6] < 0 {
304 (date[6].abs(), false)
305 } else {
306 (date[6], true)
307 }
308 } else {
309 (0, false)
310 };
311 received.date = DateTime {
312 year: if (1..=99).contains(&date[2]) {
313 date[2] + 1900
314 } else {
315 date[2]
316 } as u16,
317 month: date[1] as u8,
318 day: date[0] as u8,
319 hour: date[3] as u8,
320 minute: date[4] as u8,
321 second: date[5] as u8,
322 tz_hour: (tz / 100) as u8,
323 tz_minute: (tz % 100) as u8,
324 tz_before_gmt: !is_plus,
325 }
326 .into();
327 }
328
329 if received.from.is_some()
330 || received.from_ip.is_some()
331 || received.from_iprev.is_some()
332 || received.by.is_some()
333 || received.for_.is_some()
334 || received.with.is_some()
335 || received.tls_version.is_some()
336 || received.tls_cipher.is_some()
337 || received.id.is_some()
338 || received.ident.is_some()
339 || received.helo.is_some()
340 || received.helo_cmd.is_some()
341 || received.via.is_some()
342 || received.date.is_some()
343 {
344 HeaderValue::Received(Box::new(received))
345 } else {
346 HeaderValue::Empty
347 }
348 }
349}
350
351impl<'x> Iterator for Tokenizer<'x, '_> {
352 type Item = TokenData<'x>;
353
354 fn next(&mut self) -> Option<Self::Item> {
355 if let Some(next_token) = self.next_token.take() {
356 return Some(next_token);
357 } else if self.eof {
358 return None;
359 }
360 let mut n_alpha = 0; let mut n_digit = 0; let mut n_hex = 0; let mut n_dot = 0; let mut n_at = 0; let mut n_other = 0; let mut n_colon = 0; let mut n_plus = 0; let mut n_minus = 0; let mut n_utf = 0; let mut n_uppercase = 0;
371 let mut n_underscore = 0;
372
373 let mut n_total = 0;
374
375 let mut hash: u128 = 0;
376 let mut hash_shift = 0;
377
378 let comment_depth = self.comment_depth;
379 let bracket_depth = self.bracket_depth;
380
381 let mut start_pos = self.stream.offset();
382 let mut end_pos = start_pos;
383
384 while let Some(ch) = self.stream.next() {
385 match ch {
386 b'0'..=b'9' => {
387 n_digit += 1;
388 if hash_shift < 128 {
389 hash |= (*ch as u128) << hash_shift;
390 hash_shift += 8;
391 }
392 }
393 b'a'..=b'f' => {
394 n_hex += 1;
395 if hash_shift < 128 {
396 hash |= (*ch as u128) << hash_shift;
397 hash_shift += 8;
398 }
399 }
400 b'g'..=b'z' => {
401 n_alpha += 1;
402 if hash_shift < 128 {
403 hash |= (*ch as u128) << hash_shift;
404 hash_shift += 8;
405 }
406 }
407 b'A'..=b'F' => {
408 n_hex += 1;
409 n_uppercase += 1;
410 if hash_shift < 128 {
411 hash |= ((*ch - b'A' + b'a') as u128) << hash_shift;
412 hash_shift += 8;
413 }
414 }
415 b'G'..=b'Z' => {
416 n_alpha += 1;
417 n_uppercase += 1;
418 if hash_shift < 128 {
419 hash |= ((*ch - b'A' + b'a') as u128) << hash_shift;
420 hash_shift += 8;
421 }
422 }
423 b'@' => {
424 n_at += 1;
425 }
426 b'.' => {
427 n_dot += 1;
428 }
429 b'+' => {
430 n_plus += 1;
431 }
432 b'-' => {
433 n_minus += 1;
434 }
435 b'\n' => {
436 if !self.stream.try_next_is_space() {
437 self.eof = true;
438 break;
439 } else if n_total > 0 {
440 break;
441 } else {
442 start_pos = self.stream.offset();
443 end_pos = start_pos;
444 continue;
445 }
446 }
447 b'(' => {
448 if !self.in_quote {
449 self.comment_depth = self.comment_depth.saturating_add(1);
450 }
451 self.next_token = Some(Token::ParenthesisOpen.into());
452 break;
453 }
454 b')' => {
455 if !self.in_quote {
456 self.comment_depth = self.comment_depth.saturating_sub(1);
457 }
458 self.next_token = Some(Token::ParenthesisClose.into());
459 break;
460 }
461 b'<' => {
462 self.next_token = Some(Token::AngleOpen.into());
463 break;
464 }
465 b'>' => {
466 self.next_token = Some(Token::AngleClose.into());
467 break;
468 }
469 b'[' => {
470 if !self.in_quote {
471 self.bracket_depth = self.comment_depth.saturating_add(1);
472 }
473 self.next_token = Some(Token::BracketOpen.into());
474 break;
475 }
476 b']' => {
477 if !self.in_quote {
478 self.bracket_depth = self.comment_depth.saturating_sub(1);
479 }
480 self.next_token = Some(Token::BracketClose.into());
481 break;
482 }
483 b':' => {
484 if self.in_date
486 || n_at > 0
487 || n_dot > 0
488 || n_alpha > 0
489 || n_other > 0
490 || n_plus > 0
491 || n_minus > 0
492 || n_utf > 0
493 || n_colon == 7
494 {
495 self.next_token = Some(Token::Colon.into());
496 break;
497 } else {
498 n_colon += 1;
499 }
500 }
501 b'=' => {
502 self.next_token = Some(Token::Equal.into());
503 break;
504 }
505 b';' => {
506 if self.comment_depth == 0 {
507 self.in_date = true;
508 }
509 self.next_token = Some(Token::Semicolon.into());
510 break;
511 }
512 b'/' => {
513 self.next_token = Some(Token::Slash.into());
514 break;
515 }
516 b'"' => {
517 self.in_quote = !self.in_quote;
518 self.next_token = Some(Token::Quote.into());
519 break;
520 }
521 b',' => {
522 self.next_token = Some(Token::Comma.into());
523 break;
524 }
525 b' ' | b'\t' | b'\r' => {
526 if n_total > 0 {
527 break;
528 } else {
529 start_pos += 1;
530 end_pos = start_pos;
531 continue;
532 }
533 }
534 0x7f..=u8::MAX => {
535 n_utf += 1;
536 }
537 b'_' => {
538 n_underscore += 1;
539 n_other += 1;
540 }
541 _ => {
542 n_other += 1;
543 }
544 }
545
546 n_total += 1;
547 end_pos = self.stream.offset();
548 }
549
550 if n_total == 0 {
551 return self.next_token.take();
552 }
553
554 let text = std::str::from_utf8(self.stream.bytes(start_pos..end_pos)).unwrap_or_default();
555
556 let token = match (
557 n_alpha, n_digit, n_hex, n_dot, n_at, n_other, n_colon, n_plus, n_minus, n_utf, hash,
558 ) {
559 (0, 4..=12, 0, 3, 0, 0, 0, 0, 0, 0, _) => {
560 text.parse::<Ipv4Addr>()
562 .map(|ip| Token::IpAddr(IpAddr::V4(ip)))
563 .unwrap_or(Token::Text)
564 }
565 (0, _, 1..=32, 0, 0, 0, 2.., 0, 0, 0, _)
566 | (0, 1..=32, _, 0, 0, 0, 2.., 0, 0, 0, _)
567 | (0, 4..=12, 4, 3, 0, 0, 3, 0, 0, 0, _) => {
568 text.parse::<Ipv6Addr>()
570 .map(|ip| Token::IpAddr(IpAddr::V6(ip)))
571 .unwrap_or(Token::Text)
572 }
573 (0, 1.., 0, 0, 0, 0, 0, 0, 0, 0, _)
574 | (0, 1.., 0, 0, 0, 0, 0, 0, 1, 0, _)
575 | (0, 1.., 0, 0, 0, 0, 0, 1, 0, 0, _) => {
576 text.parse::<i64>()
578 .map(Token::Integer)
579 .unwrap_or(Token::Text)
580 }
581 (1.., _, _, _, 1, _, _, _, _, _, _) | (_, _, 1.., _, 1, _, _, _, _, _, _) => {
582 Token::Email
584 }
585 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x727061) => Token::Month(Month::Apr),
586 (4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x70746d7362) => Token::Protocol(Protocol::SMTP),
587 (1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x7962) => Token::By,
588 (0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0x636564) => Token::Month(Month::Dec),
589 (3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6f6c6865) => Token::Greeting(Greeting::Ehlo),
590 (4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x70746d7365) => Token::Protocol(Protocol::ESMTP),
591 (4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0x6170746d7365) => Token::Protocol(Protocol::ESMTPA),
592 (5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x7370746d7365) => Token::Protocol(Protocol::ESMTPS),
593 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x726f66) => Token::For,
594 (3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6d6f7266) => Token::From,
595 (3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6f6c6568) => Token::Greeting(Greeting::Helo),
596 (4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x70747468) => Token::Protocol(Protocol::HTTP),
597 (7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x7473657270747468) => Token::Protocol(Protocol::HTTP),
598 (1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6469) => Token::Id,
599 (3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x70616d69) => Token::Protocol(Protocol::IMAP),
600 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6e616a) => Token::Month(Month::Jan),
601 (3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x6c756a) => Token::Month(Month::Jul),
602 (3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x6e756a) => Token::Month(Month::Jun),
603 (4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x6f6c686c) => Token::Greeting(Greeting::Lhlo),
604 (4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x70746d6c) => Token::Protocol(Protocol::LMTP),
605 (4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6170746d6c) => Token::Protocol(Protocol::LMTPA),
606 (3, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0x6c61636f6c) => Token::Protocol(Protocol::Local),
607 (5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x70746d736c) => Token::Protocol(Protocol::LMTP),
608 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x72616d) => Token::Month(Month::Mar),
609 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x79616d) => Token::Month(Month::May),
610 (3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x766f6e) => Token::Month(Month::Nov),
611 (3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x33706f70) => Token::Protocol(Protocol::POP3),
612 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x706573) => Token::Month(Month::Sep),
613 (4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x70746d73) => Token::Protocol(Protocol::SMTP),
614 (4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6470746d73) => Token::Protocol(Protocol::SMTP),
615 (6, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x63767370746d73) => Token::Protocol(Protocol::SMTP),
616 (4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0x74656b636f73) => Token::Protocol(Protocol::Local),
617 (4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x6e69647473) => Token::Protocol(Protocol::Local),
618 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x616976) => Token::Via,
619 (0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0x626566) => Token::Month(Month::Feb),
620 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x677561) => Token::Month(Month::Aug),
621 (2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x74636f) => Token::Month(Month::Oct),
622 (4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x68746977) => Token::With,
623 (4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x70746d7361) => Token::Protocol(Protocol::ESMTPA),
624 (5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7570747468) => Token::Protocol(Protocol::HTTP),
625 (5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7370747468) => Token::Protocol(Protocol::HTTPS),
626 (3, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0x746e656469) => Token::Ident,
627 (5, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0x617370746d7365) => Token::Protocol(Protocol::ESMTPSA),
628 (5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7370746d6c) => Token::Protocol(Protocol::LMTPS),
629 (5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x617370746d6c) => Token::Protocol(Protocol::LMTPSA),
630 (3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x736d6d) => Token::Protocol(Protocol::MMS),
631 (6, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0x70746d7338667475) => {
632 Token::Protocol(Protocol::UTF8SMTP)
633 }
634 (6, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0x6170746d7338667475) => {
635 Token::Protocol(Protocol::UTF8SMTPA)
636 }
637 (7, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0x7370746d7338667475) => {
638 Token::Protocol(Protocol::UTF8SMTPS)
639 }
640 (7, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0x617370746d7338667475) => {
641 Token::Protocol(Protocol::UTF8SMTPSA)
642 }
643 (6, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0x70746d6c38667475) => {
644 Token::Protocol(Protocol::UTF8LMTP)
645 }
646 (6, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0x6170746d6c38667475) => {
647 Token::Protocol(Protocol::UTF8LMTPA)
648 }
649 (7, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0x7370746d6c38667475) => {
650 Token::Protocol(Protocol::UTF8LMTPS)
651 }
652 (7, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0x617370746d6c38667475) => {
653 Token::Protocol(Protocol::UTF8LMTPSA)
654 }
655 (7, 0, 3, 0, 0, 0, 0, 0, _, 0, 0x70746d73656c61636f6c) => {
656 Token::Protocol(Protocol::ESMTP)
657 }
658 (8, 0, 3, 0, 0, 0, 0, 0, _, 0, 0x7370746d73656c61636f6c) => {
659 Token::Protocol(Protocol::ESMTPS)
660 }
661 (7, 0, 3, 0, 0, 0, 0, 0, _, 0, 0x70746d73626c61636f6c) => {
662 Token::Protocol(Protocol::SMTP)
663 }
664 (7, 0, 1, 0, 0, 0, 0, 0, _, 0, 0x736c7470746d7365) => Token::Protocol(Protocol::ESMTPS),
665 (3, 2, 0, _, 0, _, 0, 0, _, 0, 0x3031736c74) => Token::TlsVersion(TlsVersion::TLSv1_0),
666 (3, 2, 0, _, 0, _, 0, 0, _, 0, 0x3131736c74) => Token::TlsVersion(TlsVersion::TLSv1_1),
667 (3, 2, 0, _, 0, _, 0, 0, _, 0, 0x3231736c74) => Token::TlsVersion(TlsVersion::TLSv1_2),
668 (3, 2, 0, _, 0, _, 0, 0, _, 0, 0x3331736c74) => Token::TlsVersion(TlsVersion::TLSv1_3),
669 (4, 2, 0, _, 0, _, 0, 0, 0, 0, 0x303176736c74) => {
670 Token::TlsVersion(TlsVersion::TLSv1_0)
671 }
672 (4, 2, 0, _, 0, _, 0, 0, 0, 0, 0x313176736c74) => {
673 Token::TlsVersion(TlsVersion::TLSv1_1)
674 }
675 (4, 2, 0, _, 0, _, 0, 0, 0, 0, 0x323176736c74) => {
676 Token::TlsVersion(TlsVersion::TLSv1_2)
677 }
678 (4, 2, 0, _, 0, _, 0, 0, 0, 0, 0x333176736c74) => {
679 Token::TlsVersion(TlsVersion::TLSv1_3)
680 }
681 (3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x326c7373) => Token::TlsVersion(TlsVersion::SSLv2),
682 (3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x336c7373) => Token::TlsVersion(TlsVersion::SSLv3),
683 (4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x32766c7373) => Token::TlsVersion(TlsVersion::SSLv2),
684 (4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x33766c7373) => Token::TlsVersion(TlsVersion::SSLv3),
685 (3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x31736c74) => Token::TlsVersion(TlsVersion::TLSv1_0),
686 (4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x3176736c74) => Token::TlsVersion(TlsVersion::TLSv1_0),
687 (4, 2, 1, _, 0, _, 0, 0, 0, 0, 0x303176736c7464) => {
688 Token::TlsVersion(TlsVersion::DTLSv1_0)
689 }
690 (4, 2, 1, _, 0, _, 0, 0, 0, 0, 0x323176736c7464) => {
691 Token::TlsVersion(TlsVersion::DTLSv1_2)
692 }
693 (4, 2, 1, _, 0, _, 0, 0, 0, 0, 0x333176736c7464) => {
694 Token::TlsVersion(TlsVersion::DTLSv1_3)
695 }
696 (3, 2, 1, _, 0, _, 0, 0, 0, 0, 0x3031736c7464) => {
697 Token::TlsVersion(TlsVersion::DTLSv1_0)
698 }
699 (3, 2, 1, _, 0, _, 0, 0, 0, 0, 0x3231736c7464) => {
700 Token::TlsVersion(TlsVersion::DTLSv1_2)
701 }
702 (3, 2, 1, _, 0, _, 0, 0, 0, 0, 0x3331736c7464) => {
703 Token::TlsVersion(TlsVersion::DTLSv1_3)
704 }
705 (1.., _, _, 1.., 0, 0, 0, 0, _, _, _) | (_, _, 1.., 1.., 0, 0, 0, 0, _, _, _) => {
706 Token::Domain
708 }
709 _ => {
710 if n_alpha + n_hex == n_uppercase
712 && n_total > 6
713 && (n_underscore > 0 || n_minus > 0)
714 && n_digit > 0
715 && n_dot == 0
716 && n_at == 0
717 && n_plus == 0
718 && (n_other == 0 || n_other == n_underscore)
719 && n_colon == 0
720 && n_utf == 0
721 && [
722 0x617372, 0x646365, 0x646365, 0x656864, 0x6b7370, 0x707273, 0x736561,
723 0x736564, 0x736c74,
724 ]
725 .contains(&(hash & 0xffffff))
726 {
727 Token::Cipher
728 } else {
729 Token::Text
730 }
731 }
732 };
733
734 TokenData {
735 text,
736 token,
737 comment_depth,
738 bracket_depth,
739 }
740 .into()
741 }
742}
743
744impl<'x, 'y> Tokenizer<'x, 'y> {
745 fn new(stream: &'y mut MessageStream<'x>) -> Self {
746 Self {
747 stream,
748 next_token: None,
749 eof: false,
750 in_quote: false,
751 bracket_depth: 0,
752 comment_depth: 0,
753 in_date: false,
754 }
755 }
756}
757
758impl From<Token> for TokenData<'_> {
759 fn from(token: Token) -> Self {
760 Self {
761 token,
762 text: "",
763 comment_depth: 0,
764 bracket_depth: 0,
765 }
766 }
767}
768
769impl Month {
770 fn to_number(self) -> i64 {
771 match self {
772 Month::Jan => 1,
773 Month::Feb => 2,
774 Month::Mar => 3,
775 Month::Apr => 4,
776 Month::May => 5,
777 Month::Jun => 6,
778 Month::Jul => 7,
779 Month::Aug => 8,
780 Month::Sep => 9,
781 Month::Oct => 10,
782 Month::Nov => 11,
783 Month::Dec => 12,
784 }
785 }
786}
787
788impl Token {
789 fn is_separator(&self) -> bool {
790 matches!(
791 self,
792 Token::BracketOpen
793 | Token::BracketClose
794 | Token::AngleOpen
795 | Token::AngleClose
796 | Token::ParenthesisOpen
797 | Token::ParenthesisClose
798 | Token::Semicolon
799 | Token::Colon
800 | Token::Equal
801 | Token::Slash
802 | Token::Quote
803 | Token::Comma
804 )
805 }
806}
807
808#[cfg(test)]
809mod tests {
810
811 use crate::{
812 MessageParser,
813 parsers::{MessageStream, fields::load_tests},
814 };
815
816 #[test]
817 fn parse_received() {
818 for test in load_tests("received.json") {
819 assert_eq!(
820 MessageStream::new(test.header.as_bytes())
821 .parse_received()
822 .unwrap_received(),
823 test.expected,
824 "failed for {:?}",
825 test.header
826 );
827 }
828 }
829
830 #[test]
831 fn parse_received_truncated_fold() {
832 for message in [
833 &b"Received:\n\t"[..],
834 b"Received:\r\n ",
835 b"Received: x\r\n ",
836 b"Received: x\r\n\t",
837 b"Received: x\n ",
838 b"Received: x\n\t",
839 b"Received: x\r\n ",
840 b"Received: x\r\n \t",
841 b"Received: x\r\n \t ",
842 b"Received: x\r\n \r",
843 b"Received: x\r\n \r\n",
844 b"Received: x\r\n \r\nbody",
845 b"Received: x\r\n y\r\n ",
846 b"Received: x;\r\n ",
847 b"To: a@b\r\nReceived: x\r\n ",
848 b"Received: from x (y)\r\n\t",
849 b"Received: from x by y;\r\n\t",
850 b"Received: ",
851 b"Received:",
852 b"Received: x",
853 ] {
854 MessageParser::default().parse(message);
855 MessageParser::default().parse_headers(message);
856 }
857 }
858}