samotop_parser_nom/
lib.rs1use nom::{bytes::streaming::tag, Err};
9use rustyknife::{
10 rfc5321::mailbox,
11 rfc5321::Command,
12 rfc5321::ReversePath,
13 rfc5321::{ForwardPath, Path},
14 types::AddressLiteral,
15 types::DomainPart,
16};
17use samotop_core::smtp::{command::*, *};
18pub use samotop_core::smtp::{ParseError, ParseResult, Parser};
19use std::net::IpAddr;
20
21#[derive(Clone, Copy, Debug, Default)]
22pub struct SmtpParserNom;
23
24impl Parser<SmtpCommand> for SmtpParserNom {
25 fn parse(&self, input: &[u8], state: &SmtpContext) -> ParseResult<SmtpCommand> {
26 if input.is_empty() {
27 return Err(ParseError::Incomplete);
28 }
29 if let Some(mode) = state.session.mode {
30 return Err(ParseError::Mismatch(format!(
31 "NOM - not parsing cmd in {:?} mode",
32 mode
33 )));
34 }
35 match rustyknife::rfc5321::command::<rustyknife::behaviour::Intl>(input) {
36 Ok((i, cmd)) => Ok((i.len(), map_cmd(cmd))),
37 Err(e) => Err(map_error(e)),
38 }
39 }
40}
41
42impl SmtpParserNom {
43 pub fn forward_path(&self, input: &[u8]) -> ParseResult<SmtpPath> {
44 let len = input.len();
45 let (input, _) = tag("<")(input).map_err(map_error)?;
46 let (input, m) = mailbox::<rustyknife::behaviour::Intl>(input).map_err(map_error)?;
47 let (input, _) = tag(">")(input).map_err(map_error)?;
48 Ok((len - input.len(), map_path(Path(m, vec![]))))
49 }
50}
51
52fn map_error(e: Err<()>) -> ParseError {
53 match e {
54 Err::Incomplete(_) => ParseError::Incomplete,
55 Err::Error(()) => ParseError::Mismatch("nom recoverable error".into()),
56 Err::Failure(()) => ParseError::Failed("nom failure".into()),
57 }
58}
59fn map_cmd(cmd: Command) -> SmtpCommand {
60 match cmd {
61 Command::HELO(domain) => SmtpCommand::Helo(SmtpHelo {
62 verb: "HELO".to_owned(),
63 host: SmtpHost::Domain(domain.to_string()),
64 }),
65 Command::EHLO(host) => SmtpCommand::Helo(SmtpHelo {
66 verb: "EHLO".to_owned(),
67 host: map_host(host),
68 }),
69 Command::MAIL(path, params) => SmtpCommand::Mail(SmtpMail::Mail(
70 map_reverse_path(path),
71 params.into_iter().map(|p| p.to_string()).collect(),
72 )),
73 Command::RCPT(path, params) => SmtpCommand::Rcpt(SmtpRcpt(
74 map_forward_path(path),
75 params.into_iter().map(|p| p.to_string()).collect(),
76 )),
77 Command::DATA => SmtpCommand::Data,
78 Command::RSET => SmtpCommand::Rset,
79 Command::NOOP(param) => {
80 SmtpCommand::Noop(param.map(|s| vec![s.to_string()]).unwrap_or_default())
81 }
82 Command::QUIT => SmtpCommand::Quit,
83 Command::VRFY(param) => SmtpCommand::Vrfy(param.to_string()),
84 Command::EXPN(param) => SmtpCommand::Expn(param.to_string()),
85 Command::HELP(param) => {
86 SmtpCommand::Help(param.map(|s| vec![s.to_string()]).unwrap_or_default())
87 }
88 }
89}
90fn map_forward_path(path: ForwardPath) -> SmtpPath {
91 match path {
92 ForwardPath::Path(path) => map_path(path),
93 ForwardPath::PostMaster(None) => SmtpPath::Postmaster,
94 ForwardPath::PostMaster(Some(domain)) => SmtpPath::Mailbox {
95 name: "postmaster".to_string(),
96 host: SmtpHost::Domain(domain.to_string()),
97 relays: vec![],
98 },
99 }
100}
101fn map_reverse_path(path: ReversePath) -> SmtpPath {
102 match path {
103 ReversePath::Path(path) => map_path(path),
104 ReversePath::Null => SmtpPath::Null,
105 }
106}
107fn map_path(path: Path) -> SmtpPath {
108 let Path(mailbox, domains) = path;
109 let (local, domain) = mailbox.into_parts();
110 SmtpPath::Mailbox {
111 name: local.to_string(),
112 host: map_host(domain),
113 relays: domains
114 .into_iter()
115 .map(|d| SmtpHost::Domain(d.to_string()))
116 .collect(),
117 }
118}
119fn map_host(host: DomainPart) -> SmtpHost {
120 match host {
121 DomainPart::Domain(domain) => SmtpHost::Domain(domain.to_string()),
122 DomainPart::Address(AddressLiteral::IP(IpAddr::V4(ip))) => SmtpHost::Ipv4(ip),
123 DomainPart::Address(AddressLiteral::IP(IpAddr::V6(ip))) => SmtpHost::Ipv6(ip),
124 DomainPart::Address(AddressLiteral::Tagged(label, literal)) => {
125 SmtpHost::Other { label, literal }
126 }
127 DomainPart::Address(AddressLiteral::FreeForm(literal)) => SmtpHost::Invalid {
128 label: String::new(),
129 literal,
130 },
131 }
132}