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