Compare commits

..

10 commits

Author SHA1 Message Date
Rodolphe Bréard
847fbd6213 put project to sleep 2022-01-12 20:00:53 +01:00
Rodolphe Bréard
d9b89beb3f Merge branch 'main' of github.com:breard-r/rust-opensmtpd 2022-01-12 19:59:30 +01:00
Rodolphe Bréard
28e93f0353 Update the Travis-CI configuration 2021-05-10 18:59:37 +02:00
Rodolphe Bréard
a28b8846a2
Merge pull request #5 from breard-r/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-04-30 19:02:59 +02:00
Rodolphe Bréard
e0ab5ca6fa
Merge pull request #4 from breard-r/dependabot/cargo/simplelog-0.10
Update simplelog requirement from 0.9 to 0.10
2021-04-30 19:02:47 +02:00
dependabot-preview[bot]
20493e9dec
Upgrade to GitHub-native Dependabot 2021-04-29 20:13:35 +00:00
dependabot-preview[bot]
e8f99f957d
Update simplelog requirement from 0.9 to 0.10
Updates the requirements on [simplelog](https://github.com/drakulix/simplelog.rs) to permit the latest version.
- [Release notes](https://github.com/drakulix/simplelog.rs/releases)
- [Changelog](https://github.com/Drakulix/simplelog.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/drakulix/simplelog.rs/compare/v0.9.0...v0.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-29 04:26:16 +00:00
Rodolphe Bréard
0fe864e6db Replace the lasts space indents with tabs 2021-02-28 14:10:12 +01:00
Rodolphe Bréard
e83af3d3e5 Switch to tab indentation 2021-02-28 13:56:40 +01:00
Rodolphe Bréard
ecf0ca1191 Rust-OpenSMTPD v0.4.1 2020-12-21 15:43:56 +01:00
31 changed files with 1409 additions and 1390 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = tab
trim_trailing_whitespace = true

1
.rustfmt.toml Normal file
View file

@ -0,0 +1 @@
hard_tabs = true

View file

@ -10,6 +10,10 @@ rust:
- "1.46.0" - "1.46.0"
- "1.47.0" - "1.47.0"
- "1.48.0" - "1.48.0"
- "1.49.0"
- "1.50.0"
- "1.51.0"
- "1.52.1"
- "stable" - "stable"
- "beta" - "beta"
- "nightly" - "nightly"

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = [ members = [
"opensmtpd", "opensmtpd",
"opensmtpd-derive" "opensmtpd-derive"
] ]

View file

@ -17,3 +17,8 @@ Read the documentation on [docs.rs](https://docs.rs/opensmtpd/).
# Requirements # Requirements
Rust 1.43 or newer. Rust 1.43 or newer.
# Status
Abandoned (at least temporarily).

View file

@ -1,6 +1,6 @@
[package] [package]
name = "opensmtpd_derive" name = "opensmtpd_derive"
version = "0.4.0" version = "0.4.1"
authors = ["Rodolphe Bréard <rodolphe@what.tf>"] authors = ["Rodolphe Bréard <rodolphe@what.tf>"]
edition = "2018" edition = "2018"
description = "Interface for OpenSMTPD filters" description = "Interface for OpenSMTPD filters"

View file

@ -5,14 +5,14 @@ use syn::{parse_macro_input, Ident, ItemFn};
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn register(_attr: TokenStream, input: TokenStream) -> TokenStream { pub fn register(_attr: TokenStream, input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as ItemFn); let item = parse_macro_input!(input as ItemFn);
let fn_name = item.sig.ident.to_string().replacen("on_", "has_", 1); let fn_name = item.sig.ident.to_string().replacen("on_", "has_", 1);
let fn_name = Ident::new(&fn_name, Span::call_site()); let fn_name = Ident::new(&fn_name, Span::call_site());
let output = quote! { let output = quote! {
fn #fn_name(&self) -> bool { fn #fn_name(&self) -> bool {
true true
} }
#item #item
}; };
output.into() output.into()
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "opensmtpd" name = "opensmtpd"
version = "0.4.0" version = "0.4.1"
authors = ["Rodolphe Bréard <rodolphe@what.tf>"] authors = ["Rodolphe Bréard <rodolphe@what.tf>"]
edition = "2018" edition = "2018"
description = "Interface for OpenSMTPD filters" description = "Interface for OpenSMTPD filters"
@ -18,7 +18,7 @@ opensmtpd_derive = { version = "0.4", path = "../opensmtpd-derive" }
pretty-hex = "0.2" pretty-hex = "0.2"
[dev-dependencies] [dev-dependencies]
simplelog = "0.9" simplelog = "0.10"
[[example]] [[example]]
name = "counter" name = "counter"

View file

@ -8,48 +8,48 @@ pub const DEFAULT_LOG_FILE: &str = "/tmp/counter.log";
#[derive(Default)] #[derive(Default)]
struct MyCounter { struct MyCounter {
nb_connected: u64, nb_connected: u64,
nb_total: u64, nb_total: u64,
} }
impl Filter for MyCounter { impl Filter for MyCounter {
#[register] #[register]
fn on_report_link_connect( fn on_report_link_connect(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_rdns: &str, _rdns: &str,
_fcrdns: &str, _fcrdns: &str,
_src: &Address, _src: &Address,
_dest: &Address, _dest: &Address,
) { ) {
self.nb_connected += 1; self.nb_connected += 1;
self.nb_total += 1; self.nb_total += 1;
log::info!( log::info!(
"New client (connected: {}, total: {})", "New client (connected: {}, total: {})",
self.nb_connected, self.nb_connected,
self.nb_total self.nb_total
); );
} }
#[register] #[register]
fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) { fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) {
self.nb_connected -= 1; self.nb_connected -= 1;
log::info!( log::info!(
"Client left (connected: {}, total: {})", "Client left (connected: {}, total: {})",
self.nb_connected, self.nb_connected,
self.nb_total self.nb_total
); );
} }
} }
fn main() { fn main() {
let log_file = std::env::var("LOG_FILE").unwrap_or(String::from(DEFAULT_LOG_FILE)); let log_file = std::env::var("LOG_FILE").unwrap_or(String::from(DEFAULT_LOG_FILE));
WriteLogger::init( WriteLogger::init(
LevelFilter::Info, LevelFilter::Info,
Config::default(), Config::default(),
File::create(&log_file).unwrap(), File::create(&log_file).unwrap(),
) )
.unwrap(); .unwrap();
let mut my_counter: MyCounter = Default::default(); let mut my_counter: MyCounter = Default::default();
run_filter(&mut my_counter); run_filter(&mut my_counter);
} }

View file

@ -7,21 +7,21 @@ pub const HEADER_LEN: usize = 17;
struct RmXOriginatingIp {} struct RmXOriginatingIp {}
impl Filter for RmXOriginatingIp { impl Filter for RmXOriginatingIp {
#[register] #[register]
fn on_filter_data_line(&mut self, entry: &FilterEntry, data_line: &[u8]) { fn on_filter_data_line(&mut self, entry: &FilterEntry, data_line: &[u8]) {
if data_line.len() >= HEADER_LEN { if data_line.len() >= HEADER_LEN {
let head_start = data_line[..HEADER_LEN].to_vec(); let head_start = data_line[..HEADER_LEN].to_vec();
if let Ok(s) = String::from_utf8(head_start) { if let Ok(s) = String::from_utf8(head_start) {
if s.to_lowercase() == HEADER_NAME { if s.to_lowercase() == HEADER_NAME {
return; return;
} }
} }
} }
return_data_line(entry, data_line); return_data_line(entry, data_line);
} }
} }
fn main() { fn main() {
let mut my_filter = RmXOriginatingIp {}; let mut my_filter = RmXOriginatingIp {};
run_filter(&mut my_filter); run_filter(&mut my_filter);
} }

View file

@ -2,15 +2,15 @@ use crate::FilterEntry;
use std::io::{self, Write}; use std::io::{self, Write};
pub fn return_data_line(entry: &FilterEntry, data_line: &[u8]) { pub fn return_data_line(entry: &FilterEntry, data_line: &[u8]) {
let mut data_line = data_line.to_vec(); let mut data_line = data_line.to_vec();
data_line.retain(|&c| c != 0x0d && c != 0x0a); data_line.retain(|&c| c != 0x0d && c != 0x0a);
print!("filter-dataline|{}|{}|", entry.session_id, entry.token); print!("filter-dataline|{}|{}|", entry.session_id, entry.token);
io::stdout().write_all(&data_line).unwrap(); io::stdout().write_all(&data_line).unwrap();
println!(); println!();
log::trace!( log::trace!(
"Sent filter-dataline (session:id: {}, token: {}){}", "Sent filter-dataline (session:id: {}, token: {}){}",
entry.session_id, entry.session_id,
entry.token, entry.token,
crate::error::get_pretty_hex(&data_line) crate::error::get_pretty_hex(&data_line)
); );
} }

View file

@ -3,18 +3,18 @@ use std::path::PathBuf;
#[derive(Debug)] #[derive(Debug)]
pub enum Address { pub enum Address {
Ip(SocketAddr), Ip(SocketAddr),
UnixSocket(PathBuf), UnixSocket(PathBuf),
} }
impl ToString for Address { impl ToString for Address {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
Address::Ip(a) => a.to_string(), Address::Ip(a) => a.to_string(),
Address::UnixSocket(a) => match a.clone().into_os_string().into_string() { Address::UnixSocket(a) => match a.clone().into_os_string().into_string() {
Ok(s) => s, Ok(s) => s,
Err(_) => String::new(), Err(_) => String::new(),
}, },
} }
} }
} }

View file

@ -2,30 +2,30 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum AuthResult { pub enum AuthResult {
Pass, Pass,
Fail, Fail,
Error, Error,
} }
impl ToString for AuthResult { impl ToString for AuthResult {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
AuthResult::Pass => String::from("pass"), AuthResult::Pass => String::from("pass"),
AuthResult::Fail => String::from("fail"), AuthResult::Fail => String::from("fail"),
AuthResult::Error => String::from("error"), AuthResult::Error => String::from("error"),
} }
} }
} }
impl FromStr for AuthResult { impl FromStr for AuthResult {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"pass" => Ok(AuthResult::Pass), "pass" => Ok(AuthResult::Pass),
"fail" => Ok(AuthResult::Fail), "fail" => Ok(AuthResult::Fail),
"error" => Ok(AuthResult::Error), "error" => Ok(AuthResult::Error),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -2,78 +2,78 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum Event { pub enum Event {
LinkAuth, LinkAuth,
LinkConnect, LinkConnect,
LinkDisconnect, LinkDisconnect,
LinkGreeting, LinkGreeting,
LinkIdentify, LinkIdentify,
LinkTls, LinkTls,
TxBegin, TxBegin,
TxMail, TxMail,
TxReset, TxReset,
TxRcpt, TxRcpt,
TxEnvelope, TxEnvelope,
TxData, TxData,
TxCommit, TxCommit,
TxRollback, TxRollback,
ProtocolClient, ProtocolClient,
ProtocolServer, ProtocolServer,
FilterResponse, FilterResponse,
FilterReport, FilterReport,
Timeout, Timeout,
} }
impl ToString for Event { impl ToString for Event {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
Event::LinkAuth => String::from("link-auth"), Event::LinkAuth => String::from("link-auth"),
Event::LinkConnect => String::from("link-connect"), Event::LinkConnect => String::from("link-connect"),
Event::LinkDisconnect => String::from("link-disconnect"), Event::LinkDisconnect => String::from("link-disconnect"),
Event::LinkGreeting => String::from("link-greeting"), Event::LinkGreeting => String::from("link-greeting"),
Event::LinkIdentify => String::from("link-identify"), Event::LinkIdentify => String::from("link-identify"),
Event::LinkTls => String::from("link-tls"), Event::LinkTls => String::from("link-tls"),
Event::TxBegin => String::from("tx-begin"), Event::TxBegin => String::from("tx-begin"),
Event::TxMail => String::from("tx-mail"), Event::TxMail => String::from("tx-mail"),
Event::TxReset => String::from("tx-reset"), Event::TxReset => String::from("tx-reset"),
Event::TxRcpt => String::from("tx-rcpt"), Event::TxRcpt => String::from("tx-rcpt"),
Event::TxEnvelope => String::from("tx-envelope"), Event::TxEnvelope => String::from("tx-envelope"),
Event::TxData => String::from("tx-data"), Event::TxData => String::from("tx-data"),
Event::TxCommit => String::from("tx-commit"), Event::TxCommit => String::from("tx-commit"),
Event::TxRollback => String::from("tx-rollback"), Event::TxRollback => String::from("tx-rollback"),
Event::ProtocolClient => String::from("protocol-client"), Event::ProtocolClient => String::from("protocol-client"),
Event::ProtocolServer => String::from("protocol-server"), Event::ProtocolServer => String::from("protocol-server"),
Event::FilterResponse => String::from("filter-response"), Event::FilterResponse => String::from("filter-response"),
Event::FilterReport => String::from("filter-report"), Event::FilterReport => String::from("filter-report"),
Event::Timeout => String::from("timeout"), Event::Timeout => String::from("timeout"),
} }
} }
} }
impl FromStr for Event { impl FromStr for Event {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"link-auth" => Ok(Event::LinkAuth), "link-auth" => Ok(Event::LinkAuth),
"link-connect" => Ok(Event::LinkConnect), "link-connect" => Ok(Event::LinkConnect),
"link-disconnect" => Ok(Event::LinkDisconnect), "link-disconnect" => Ok(Event::LinkDisconnect),
"link-greeting" => Ok(Event::LinkGreeting), "link-greeting" => Ok(Event::LinkGreeting),
"link-identify" => Ok(Event::LinkIdentify), "link-identify" => Ok(Event::LinkIdentify),
"link-tls" => Ok(Event::LinkTls), "link-tls" => Ok(Event::LinkTls),
"tx-begin" => Ok(Event::TxBegin), "tx-begin" => Ok(Event::TxBegin),
"tx-mail" => Ok(Event::TxMail), "tx-mail" => Ok(Event::TxMail),
"tx-reset" => Ok(Event::TxReset), "tx-reset" => Ok(Event::TxReset),
"tx-rcpt" => Ok(Event::TxRcpt), "tx-rcpt" => Ok(Event::TxRcpt),
"tx-envelope" => Ok(Event::TxEnvelope), "tx-envelope" => Ok(Event::TxEnvelope),
"tx-data" => Ok(Event::TxData), "tx-data" => Ok(Event::TxData),
"tx-commit" => Ok(Event::TxCommit), "tx-commit" => Ok(Event::TxCommit),
"tx-rollback" => Ok(Event::TxRollback), "tx-rollback" => Ok(Event::TxRollback),
"protocol-client" => Ok(Event::ProtocolClient), "protocol-client" => Ok(Event::ProtocolClient),
"protocol-server" => Ok(Event::ProtocolServer), "protocol-server" => Ok(Event::ProtocolServer),
"filter-response" => Ok(Event::FilterResponse), "filter-response" => Ok(Event::FilterResponse),
"filter-report" => Ok(Event::FilterReport), "filter-report" => Ok(Event::FilterReport),
"timeout" => Ok(Event::Timeout), "timeout" => Ok(Event::Timeout),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -2,27 +2,27 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum FilterKind { pub enum FilterKind {
Builtin, Builtin,
Proc, Proc,
} }
impl ToString for FilterKind { impl ToString for FilterKind {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
FilterKind::Builtin => String::from("builtin"), FilterKind::Builtin => String::from("builtin"),
FilterKind::Proc => String::from("proc"), FilterKind::Proc => String::from("proc"),
} }
} }
} }
impl FromStr for FilterKind { impl FromStr for FilterKind {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"builtin" => Ok(FilterKind::Builtin), "builtin" => Ok(FilterKind::Builtin),
"proc" => Ok(FilterKind::Proc), "proc" => Ok(FilterKind::Proc),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -2,51 +2,51 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum FilterPhase { pub enum FilterPhase {
Connect, Connect,
Helo, Helo,
Ehlo, Ehlo,
StartTls, StartTls,
Auth, Auth,
MailFrom, MailFrom,
RcptTo, RcptTo,
Data, Data,
DataLine, DataLine,
Commit, Commit,
} }
impl ToString for FilterPhase { impl ToString for FilterPhase {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
FilterPhase::Connect => String::from("connect"), FilterPhase::Connect => String::from("connect"),
FilterPhase::Helo => String::from("helo"), FilterPhase::Helo => String::from("helo"),
FilterPhase::Ehlo => String::from("ehlo"), FilterPhase::Ehlo => String::from("ehlo"),
FilterPhase::StartTls => String::from("starttls"), FilterPhase::StartTls => String::from("starttls"),
FilterPhase::Auth => String::from("auth"), FilterPhase::Auth => String::from("auth"),
FilterPhase::MailFrom => String::from("mail-from"), FilterPhase::MailFrom => String::from("mail-from"),
FilterPhase::RcptTo => String::from("rcpt-to"), FilterPhase::RcptTo => String::from("rcpt-to"),
FilterPhase::Data => String::from("data"), FilterPhase::Data => String::from("data"),
FilterPhase::DataLine => String::from("data-line"), FilterPhase::DataLine => String::from("data-line"),
FilterPhase::Commit => String::from("commit"), FilterPhase::Commit => String::from("commit"),
} }
} }
} }
impl FromStr for FilterPhase { impl FromStr for FilterPhase {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"connect" => Ok(FilterPhase::Connect), "connect" => Ok(FilterPhase::Connect),
"helo" => Ok(FilterPhase::Helo), "helo" => Ok(FilterPhase::Helo),
"ehlo" => Ok(FilterPhase::Ehlo), "ehlo" => Ok(FilterPhase::Ehlo),
"starttls" => Ok(FilterPhase::StartTls), "starttls" => Ok(FilterPhase::StartTls),
"auth" => Ok(FilterPhase::Auth), "auth" => Ok(FilterPhase::Auth),
"mail-from" => Ok(FilterPhase::MailFrom), "mail-from" => Ok(FilterPhase::MailFrom),
"rcpt-to" => Ok(FilterPhase::RcptTo), "rcpt-to" => Ok(FilterPhase::RcptTo),
"data" => Ok(FilterPhase::Data), "data" => Ok(FilterPhase::Data),
"data-line" => Ok(FilterPhase::DataLine), "data-line" => Ok(FilterPhase::DataLine),
"commit" => Ok(FilterPhase::Commit), "commit" => Ok(FilterPhase::Commit),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -2,23 +2,23 @@ use crate::SmtpStatusCode;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum FilterResponse { pub enum FilterResponse {
Proceed, Proceed,
Junk, Junk,
Reject(SmtpStatusCode), Reject(SmtpStatusCode),
Disconnect(SmtpStatusCode), Disconnect(SmtpStatusCode),
Rewrite(String), Rewrite(String),
Report(String), Report(String),
} }
impl ToString for FilterResponse { impl ToString for FilterResponse {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
FilterResponse::Proceed => String::from("proceed"), FilterResponse::Proceed => String::from("proceed"),
FilterResponse::Junk => String::from("junk"), FilterResponse::Junk => String::from("junk"),
FilterResponse::Reject(e) => format!("reject|{}", e.to_string()), FilterResponse::Reject(e) => format!("reject|{}", e.to_string()),
FilterResponse::Disconnect(e) => format!("disconnect|{}", e.to_string()), FilterResponse::Disconnect(e) => format!("disconnect|{}", e.to_string()),
FilterResponse::Rewrite(s) => format!("rewrite|{}", s), FilterResponse::Rewrite(s) => format!("rewrite|{}", s),
FilterResponse::Report(s) => format!("report|{}", s), FilterResponse::Report(s) => format!("report|{}", s),
} }
} }
} }

View file

@ -2,30 +2,30 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum MailResult { pub enum MailResult {
Ok, Ok,
PermFail, PermFail,
TempFail, TempFail,
} }
impl ToString for MailResult { impl ToString for MailResult {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
MailResult::Ok => String::from("ok"), MailResult::Ok => String::from("ok"),
MailResult::PermFail => String::from("permfail"), MailResult::PermFail => String::from("permfail"),
MailResult::TempFail => String::from("tempfail"), MailResult::TempFail => String::from("tempfail"),
} }
} }
} }
impl FromStr for MailResult { impl FromStr for MailResult {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"ok" => Ok(MailResult::Ok), "ok" => Ok(MailResult::Ok),
"permfail" => Ok(MailResult::PermFail), "permfail" => Ok(MailResult::PermFail),
"tempfail" => Ok(MailResult::TempFail), "tempfail" => Ok(MailResult::TempFail),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -2,27 +2,27 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum Method { pub enum Method {
Helo, Helo,
Ehlo, Ehlo,
} }
impl ToString for Method { impl ToString for Method {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
Method::Helo => String::from("HELO"), Method::Helo => String::from("HELO"),
Method::Ehlo => String::from("EHLO"), Method::Ehlo => String::from("EHLO"),
} }
} }
} }
impl FromStr for Method { impl FromStr for Method {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"HELO" => Ok(Method::Helo), "HELO" => Ok(Method::Helo),
"EHLO" => Ok(Method::Ehlo), "EHLO" => Ok(Method::Ehlo),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -1,116 +1,116 @@
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SmtpStatusCode { pub struct SmtpStatusCode {
pub number: usize, pub number: usize,
pub text: String, pub text: String,
} }
impl SmtpStatusCode { impl SmtpStatusCode {
pub fn from_number(error_number: usize) -> Self { pub fn from_number(error_number: usize) -> Self {
match error_number { match error_number {
211 => SmtpStatusCode { 211 => SmtpStatusCode {
number: 211, number: 211,
text: String::from("System status"), text: String::from("System status"),
}, },
220 => SmtpStatusCode { 220 => SmtpStatusCode {
number: 220, number: 220,
text: String::from("Service ready"), text: String::from("Service ready"),
}, },
250 => SmtpStatusCode { 250 => SmtpStatusCode {
number: 250, number: 250,
text: String::from("Requested mail action okay, completed"), text: String::from("Requested mail action okay, completed"),
}, },
251 => SmtpStatusCode { 251 => SmtpStatusCode {
number: 251, number: 251,
text: String::from("User not local; will forward"), text: String::from("User not local; will forward"),
}, },
252 => SmtpStatusCode { 252 => SmtpStatusCode {
number: 252, number: 252,
text: String::from( text: String::from(
"Cannot verify the user, but it will try to deliver the message anyway", "Cannot verify the user, but it will try to deliver the message anyway",
), ),
}, },
354 => SmtpStatusCode { 354 => SmtpStatusCode {
number: 354, number: 354,
text: String::from("Start mail input"), text: String::from("Start mail input"),
}, },
421 => SmtpStatusCode { 421 => SmtpStatusCode {
number: 421, number: 421,
text: String::from("Service is unavailable because the server is shutting down"), text: String::from("Service is unavailable because the server is shutting down"),
}, },
450 => SmtpStatusCode { 450 => SmtpStatusCode {
number: 450, number: 450,
text: String::from("Requested mail action not taken: mailbox unavailable"), text: String::from("Requested mail action not taken: mailbox unavailable"),
}, },
451 => SmtpStatusCode { 451 => SmtpStatusCode {
number: 451, number: 451,
text: String::from("Requested action aborted: local error in processing"), text: String::from("Requested action aborted: local error in processing"),
}, },
452 => SmtpStatusCode { 452 => SmtpStatusCode {
number: 452, number: 452,
text: String::from("Requested action not taken: insufficient system storage"), text: String::from("Requested action not taken: insufficient system storage"),
}, },
455 => SmtpStatusCode { 455 => SmtpStatusCode {
number: 455, number: 455,
text: String::from("Server unable to accommodate parameters"), text: String::from("Server unable to accommodate parameters"),
}, },
500 => SmtpStatusCode { 500 => SmtpStatusCode {
number: 500, number: 500,
text: String::from("Syntax error, command unrecognized"), text: String::from("Syntax error, command unrecognized"),
}, },
501 => SmtpStatusCode { 501 => SmtpStatusCode {
number: 501, number: 501,
text: String::from("Syntax error in parameters or arguments"), text: String::from("Syntax error in parameters or arguments"),
}, },
502 => SmtpStatusCode { 502 => SmtpStatusCode {
number: 502, number: 502,
text: String::from("Command not implemented"), text: String::from("Command not implemented"),
}, },
503 => SmtpStatusCode { 503 => SmtpStatusCode {
number: 503, number: 503,
text: String::from("Bad sequence of commands"), text: String::from("Bad sequence of commands"),
}, },
504 => SmtpStatusCode { 504 => SmtpStatusCode {
number: 504, number: 504,
text: String::from("Command parameter is not implemented"), text: String::from("Command parameter is not implemented"),
}, },
521 => SmtpStatusCode { 521 => SmtpStatusCode {
number: 521, number: 521,
text: String::from("Server does not accept mail"), text: String::from("Server does not accept mail"),
}, },
523 => SmtpStatusCode { 523 => SmtpStatusCode {
number: 523, number: 523,
text: String::from("Encryption Needed"), text: String::from("Encryption Needed"),
}, },
550 => SmtpStatusCode { 550 => SmtpStatusCode {
number: 550, number: 550,
text: String::from("Requested action not taken: mailbox unavailable"), text: String::from("Requested action not taken: mailbox unavailable"),
}, },
552 => SmtpStatusCode { 552 => SmtpStatusCode {
number: 552, number: 552,
text: String::from("Requested mail action aborted: exceeded storage allocation"), text: String::from("Requested mail action aborted: exceeded storage allocation"),
}, },
553 => SmtpStatusCode { 553 => SmtpStatusCode {
number: 553, number: 553,
text: String::from("Requested action not taken: mailbox name not allowed"), text: String::from("Requested action not taken: mailbox name not allowed"),
}, },
554 => SmtpStatusCode { 554 => SmtpStatusCode {
number: 554, number: 554,
text: String::from("Transaction has failed"), text: String::from("Transaction has failed"),
}, },
556 => SmtpStatusCode { 556 => SmtpStatusCode {
number: 556, number: 556,
text: String::from("Domain does not accept mail"), text: String::from("Domain does not accept mail"),
}, },
nb => SmtpStatusCode { nb => SmtpStatusCode {
number: nb, number: nb,
text: String::new(), text: String::new(),
}, },
} }
} }
} }
impl ToString for SmtpStatusCode { impl ToString for SmtpStatusCode {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!("{} {}", self.number, self.text) format!("{} {}", self.number, self.text)
} }
} }

View file

@ -2,24 +2,24 @@ use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum SubSystem { pub enum SubSystem {
SmtpIn, SmtpIn,
} }
impl ToString for SubSystem { impl ToString for SubSystem {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
SubSystem::SmtpIn => String::from("smtp-in"), SubSystem::SmtpIn => String::from("smtp-in"),
} }
} }
} }
impl FromStr for SubSystem { impl FromStr for SubSystem {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"smtp-in" => Ok(SubSystem::SmtpIn), "smtp-in" => Ok(SubSystem::SmtpIn),
_ => Err(()), _ => Err(()),
} }
} }
} }

View file

@ -1,11 +1,11 @@
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct TimeVal { pub struct TimeVal {
pub sec: i64, pub sec: i64,
pub usec: i64, pub usec: i64,
} }
impl ToString for TimeVal { impl ToString for TimeVal {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!("{}.{}", self.sec, self.usec) format!("{}.{}", self.sec, self.usec)
} }
} }

View file

@ -3,25 +3,25 @@ use nom::Err;
use pretty_hex::pretty_hex; use pretty_hex::pretty_hex;
fn error_to_string(e: Error<&[u8]>) -> String { fn error_to_string(e: Error<&[u8]>) -> String {
format!( format!(
"parsing error: {:?}: input:{}", "parsing error: {:?}: input:{}",
e.code, e.code,
get_pretty_hex(&e.input) get_pretty_hex(&e.input)
) )
} }
pub(crate) fn get_pretty_hex(input: &[u8]) -> String { pub(crate) fn get_pretty_hex(input: &[u8]) -> String {
let mut s = String::new(); let mut s = String::new();
for l in pretty_hex(&input).split('\n') { for l in pretty_hex(&input).split('\n') {
s += &format!("\n{}", l); s += &format!("\n{}", l);
} }
s s
} }
pub(crate) fn nom_err_to_string(e: Err<Error<&[u8]>>) -> String { pub(crate) fn nom_err_to_string(e: Err<Error<&[u8]>>) -> String {
match e { match e {
Err::Incomplete(_) => e.to_string(), Err::Incomplete(_) => e.to_string(),
Err::Error(er) => error_to_string(er), Err::Error(er) => error_to_string(er),
Err::Failure(er) => error_to_string(er), Err::Failure(er) => error_to_string(er),
} }
} }

View file

@ -1,253 +1,253 @@
use crate::{ use crate::{
Address, AuthResult, FilterEntry, FilterKind, FilterPhase, FilterResponse, MailResult, Method, Address, AuthResult, FilterEntry, FilterKind, FilterPhase, FilterResponse, MailResult, Method,
ReportEntry, ReportEntry,
}; };
pub trait Filter { pub trait Filter {
fn on_filter_auth(&mut self, _entry: &FilterEntry, _auth: &str) -> FilterResponse { fn on_filter_auth(&mut self, _entry: &FilterEntry, _auth: &str) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_auth(&self) -> bool { fn has_filter_auth(&self) -> bool {
false false
} }
fn on_filter_commit(&mut self, _entry: &FilterEntry) -> FilterResponse { fn on_filter_commit(&mut self, _entry: &FilterEntry) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_commit(&self) -> bool { fn has_filter_commit(&self) -> bool {
false false
} }
fn on_filter_connect( fn on_filter_connect(
&mut self, &mut self,
_entry: &FilterEntry, _entry: &FilterEntry,
_rdns: &str, _rdns: &str,
_fcrdns: &str, _fcrdns: &str,
_src: &Address, _src: &Address,
_dest: &Address, _dest: &Address,
) -> FilterResponse { ) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_connect(&self) -> bool { fn has_filter_connect(&self) -> bool {
false false
} }
fn on_filter_data(&mut self, _entry: &FilterEntry) -> FilterResponse { fn on_filter_data(&mut self, _entry: &FilterEntry) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_data(&self) -> bool { fn has_filter_data(&self) -> bool {
false false
} }
fn on_filter_data_line(&mut self, _entry: &FilterEntry, _data_line: &[u8]) {} fn on_filter_data_line(&mut self, _entry: &FilterEntry, _data_line: &[u8]) {}
#[doc(hidden)] #[doc(hidden)]
fn has_filter_data_line(&self) -> bool { fn has_filter_data_line(&self) -> bool {
false false
} }
fn on_filter_ehlo(&mut self, _entry: &FilterEntry, _identity: &str) -> FilterResponse { fn on_filter_ehlo(&mut self, _entry: &FilterEntry, _identity: &str) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_ehlo(&self) -> bool { fn has_filter_ehlo(&self) -> bool {
false false
} }
fn on_filter_helo(&mut self, _entry: &FilterEntry, _identity: &str) -> FilterResponse { fn on_filter_helo(&mut self, _entry: &FilterEntry, _identity: &str) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_helo(&self) -> bool { fn has_filter_helo(&self) -> bool {
false false
} }
fn on_filter_mail_from(&mut self, _entry: &FilterEntry, _address: &str) -> FilterResponse { fn on_filter_mail_from(&mut self, _entry: &FilterEntry, _address: &str) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_mail_from(&self) -> bool { fn has_filter_mail_from(&self) -> bool {
false false
} }
fn on_filter_rcpt_to(&mut self, _entry: &FilterEntry, _address: &str) -> FilterResponse { fn on_filter_rcpt_to(&mut self, _entry: &FilterEntry, _address: &str) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_rcpt_to(&self) -> bool { fn has_filter_rcpt_to(&self) -> bool {
false false
} }
fn on_filter_starttls(&mut self, _entry: &FilterEntry, _tls_string: &str) -> FilterResponse { fn on_filter_starttls(&mut self, _entry: &FilterEntry, _tls_string: &str) -> FilterResponse {
FilterResponse::Proceed FilterResponse::Proceed
} }
#[doc(hidden)] #[doc(hidden)]
fn has_filter_starttls(&self) -> bool { fn has_filter_starttls(&self) -> bool {
false false
} }
fn on_report_link_auth(&mut self, _entry: &ReportEntry, _username: &str, _result: AuthResult) {} fn on_report_link_auth(&mut self, _entry: &ReportEntry, _username: &str, _result: AuthResult) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_link_auth(&self) -> bool { fn has_report_link_auth(&self) -> bool {
false false
} }
fn on_report_link_connect( fn on_report_link_connect(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_rdns: &str, _rdns: &str,
_fcrdns: &str, _fcrdns: &str,
_src: &Address, _src: &Address,
_dest: &Address, _dest: &Address,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_link_connect(&self) -> bool { fn has_report_link_connect(&self) -> bool {
false false
} }
fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) {} fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_link_disconnect(&self) -> bool { fn has_report_link_disconnect(&self) -> bool {
false false
} }
fn on_report_link_greeting(&mut self, _entry: &ReportEntry, _hostname: &str) {} fn on_report_link_greeting(&mut self, _entry: &ReportEntry, _hostname: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_link_greeting(&self) -> bool { fn has_report_link_greeting(&self) -> bool {
false false
} }
fn on_report_link_identify(&mut self, _entry: &ReportEntry, _method: Method, _identity: &str) {} fn on_report_link_identify(&mut self, _entry: &ReportEntry, _method: Method, _identity: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_link_identify(&self) -> bool { fn has_report_link_identify(&self) -> bool {
false false
} }
fn on_report_link_tls(&mut self, _entry: &ReportEntry, _tls_string: &str) {} fn on_report_link_tls(&mut self, _entry: &ReportEntry, _tls_string: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_link_tls(&self) -> bool { fn has_report_link_tls(&self) -> bool {
false false
} }
fn on_report_tx_begin(&mut self, _entry: &ReportEntry, _message_id: &str) {} fn on_report_tx_begin(&mut self, _entry: &ReportEntry, _message_id: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_begin(&self) -> bool { fn has_report_tx_begin(&self) -> bool {
false false
} }
fn on_report_tx_mail( fn on_report_tx_mail(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_message_id: &str, _message_id: &str,
_result: MailResult, _result: MailResult,
_address: &str, _address: &str,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_mail(&self) -> bool { fn has_report_tx_mail(&self) -> bool {
false false
} }
fn on_report_tx_reset(&mut self, _entry: &ReportEntry, _message_id: &Option<String>) {} fn on_report_tx_reset(&mut self, _entry: &ReportEntry, _message_id: &Option<String>) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_reset(&self) -> bool { fn has_report_tx_reset(&self) -> bool {
false false
} }
fn on_report_tx_rcpt( fn on_report_tx_rcpt(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_message_id: &str, _message_id: &str,
_result: MailResult, _result: MailResult,
_address: &str, _address: &str,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_rcpt(&self) -> bool { fn has_report_tx_rcpt(&self) -> bool {
false false
} }
fn on_report_tx_envelope( fn on_report_tx_envelope(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_message_id: &str, _message_id: &str,
_envelope_id: &str, _envelope_id: &str,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_envelope(&self) -> bool { fn has_report_tx_envelope(&self) -> bool {
false false
} }
fn on_report_tx_data(&mut self, _entry: &ReportEntry, _message_id: &str, _result: MailResult) {} fn on_report_tx_data(&mut self, _entry: &ReportEntry, _message_id: &str, _result: MailResult) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_data(&self) -> bool { fn has_report_tx_data(&self) -> bool {
false false
} }
fn on_report_tx_commit( fn on_report_tx_commit(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_message_id: &str, _message_id: &str,
_message_size: usize, _message_size: usize,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_commit(&self) -> bool { fn has_report_tx_commit(&self) -> bool {
false false
} }
fn on_report_tx_rollback(&mut self, _entry: &ReportEntry, _message_id: &str) {} fn on_report_tx_rollback(&mut self, _entry: &ReportEntry, _message_id: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_tx_rollback(&self) -> bool { fn has_report_tx_rollback(&self) -> bool {
false false
} }
fn on_report_protocol_client(&mut self, _entry: &ReportEntry, _command: &str) {} fn on_report_protocol_client(&mut self, _entry: &ReportEntry, _command: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_protocol_client(&self) -> bool { fn has_report_protocol_client(&self) -> bool {
false false
} }
fn on_report_protocol_server(&mut self, _entry: &ReportEntry, _response: &str) {} fn on_report_protocol_server(&mut self, _entry: &ReportEntry, _response: &str) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_protocol_server(&self) -> bool { fn has_report_protocol_server(&self) -> bool {
false false
} }
fn on_report_filter_response( fn on_report_filter_response(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_phase: FilterPhase, _phase: FilterPhase,
_response: &str, _response: &str,
_param: &Option<String>, _param: &Option<String>,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_filter_response(&self) -> bool { fn has_report_filter_response(&self) -> bool {
false false
} }
fn on_report_filter_report( fn on_report_filter_report(
&mut self, &mut self,
_entry: &ReportEntry, _entry: &ReportEntry,
_filter_kind: FilterKind, _filter_kind: FilterKind,
_name: &str, _name: &str,
_message: &str, _message: &str,
) { ) {
} }
#[doc(hidden)] #[doc(hidden)]
fn has_report_filter_report(&self) -> bool { fn has_report_filter_report(&self) -> bool {
false false
} }
fn on_report_timeout(&mut self, _entry: &ReportEntry) {} fn on_report_timeout(&mut self, _entry: &ReportEntry) {}
#[doc(hidden)] #[doc(hidden)]
fn has_report_timeout(&self) -> bool { fn has_report_timeout(&self) -> bool {
false false
} }
} }

View file

@ -3,39 +3,39 @@ use std::io::{self, ErrorKind, Read};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
pub(crate) fn read_stdin(tx: &Sender<Vec<u8>>) { pub(crate) fn read_stdin(tx: &Sender<Vec<u8>>) {
if let Err(e) = do_read_stdin(tx) { if let Err(e) = do_read_stdin(tx) {
log::error!("{}", e); log::error!("{}", e);
} }
} }
fn do_read_stdin(tx: &Sender<Vec<u8>>) -> Result<(), String> { fn do_read_stdin(tx: &Sender<Vec<u8>>) -> Result<(), String> {
let mut read_buffer: [u8; crate::BUFFER_SIZE] = [0; crate::BUFFER_SIZE]; let mut read_buffer: [u8; crate::BUFFER_SIZE] = [0; crate::BUFFER_SIZE];
let mut line_buffer: Vec<u8> = Vec::with_capacity(crate::BUFFER_SIZE); let mut line_buffer: Vec<u8> = Vec::with_capacity(crate::BUFFER_SIZE);
let mut stdin = io::stdin(); let mut stdin = io::stdin();
loop { loop {
read_buffer.copy_from_slice(&[0; crate::BUFFER_SIZE]); read_buffer.copy_from_slice(&[0; crate::BUFFER_SIZE]);
let len = match stdin.read(&mut read_buffer) { let len = match stdin.read(&mut read_buffer) {
Ok(n) => n, Ok(n) => n,
Err(e) => match e.kind() { Err(e) => match e.kind() {
ErrorKind::Interrupted => { ErrorKind::Interrupted => {
continue; continue;
} }
_ => { _ => {
return Err(e.to_string()); return Err(e.to_string());
} }
}, },
}; };
if len == 0 { if len == 0 {
return Err(String::from("unable to read on stdin")); return Err(String::from("unable to read on stdin"));
} }
line_buffer.extend_from_slice(&read_buffer[..len]); line_buffer.extend_from_slice(&read_buffer[..len]);
while let Some(id) = line_buffer.iter().position(|i| *i == b'\n') { while let Some(id) = line_buffer.iter().position(|i| *i == b'\n') {
let pos = id + 1; let pos = id + 1;
let mut line = Vec::with_capacity(pos); let mut line = Vec::with_capacity(pos);
line.extend_from_slice(&line_buffer[..pos]); line.extend_from_slice(&line_buffer[..pos]);
log::trace!("new line:{}", get_pretty_hex(&line)); log::trace!("new line:{}", get_pretty_hex(&line));
tx.send(line).unwrap(); tx.send(line).unwrap();
line_buffer.drain(..pos); line_buffer.drain(..pos);
} }
} }
} }

View file

@ -56,19 +56,19 @@
//! use opensmtpd_derive::register; //! use opensmtpd_derive::register;
//! //!
//! struct MyCounter { //! struct MyCounter {
//! nb: u64, //! nb: u64,
//! } //! }
//! //!
//! impl Filter for MyCounter { //! impl Filter for MyCounter {
//! #[register] //! #[register]
//! fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) { //! fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) {
//! self.nb + 1; //! self.nb + 1;
//! } //! }
//! } //! }
//! //!
//! fn main() { //! fn main() {
//! let mut my_counter = MyCounter { nb: 0, }; //! let mut my_counter = MyCounter { nb: 0, };
//! run_filter(&mut my_counter); //! run_filter(&mut my_counter);
//! } //! }
//! ``` //! ```
//! //!
@ -88,23 +88,23 @@
//! struct RmXOriginatingIp {} //! struct RmXOriginatingIp {}
//! //!
//! impl Filter for RmXOriginatingIp { //! impl Filter for RmXOriginatingIp {
//! #[register] //! #[register]
//! fn on_filter_data_line(&mut self, entry: &FilterEntry, data_line: &[u8]) { //! fn on_filter_data_line(&mut self, entry: &FilterEntry, data_line: &[u8]) {
//! if data_line.len() >= HEADER_LEN { //! if data_line.len() >= HEADER_LEN {
//! let head_start = data_line[..HEADER_LEN].to_vec(); //! let head_start = data_line[..HEADER_LEN].to_vec();
//! if let Ok(s) = String::from_utf8(head_start) { //! if let Ok(s) = String::from_utf8(head_start) {
//! if s.to_lowercase() == HEADER_NAME { //! if s.to_lowercase() == HEADER_NAME {
//! return; //! return;
//! } //! }
//! } //! }
//! } //! }
//! return_data_line(entry, data_line); //! return_data_line(entry, data_line);
//! } //! }
//! } //! }
//! //!
//! fn main() { //! fn main() {
//! let mut my_filter = RmXOriginatingIp {}; //! let mut my_filter = RmXOriginatingIp {};
//! run_filter(&mut my_filter); //! run_filter(&mut my_filter);
//! } //! }
//! ``` //! ```
//! //!
@ -162,123 +162,123 @@ use std::thread;
const BUFFER_SIZE: usize = 4096; const BUFFER_SIZE: usize = 4096;
macro_rules! recv { macro_rules! recv {
($rx: ident) => { ($rx: ident) => {
match $rx.recv() { match $rx.recv() {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
log::error!("{}", e); log::error!("{}", e);
return; return;
} }
} }
}; };
} }
pub fn run_filter<T>(user_object: &mut T) pub fn run_filter<T>(user_object: &mut T)
where where
T: Filter, T: Filter,
{ {
// IO init // IO init
let (tx, rx) = channel::<Vec<u8>>(); let (tx, rx) = channel::<Vec<u8>>();
thread::spawn(move || { thread::spawn(move || {
io::read_stdin(&tx); io::read_stdin(&tx);
}); });
// Handshake // Handshake
let mut handshake_buffer: Vec<u8> = Vec::with_capacity(BUFFER_SIZE); let mut handshake_buffer: Vec<u8> = Vec::with_capacity(BUFFER_SIZE);
let handshake = loop { let handshake = loop {
let buffer = recv!(rx); let buffer = recv!(rx);
handshake_buffer.extend_from_slice(&buffer); handshake_buffer.extend_from_slice(&buffer);
if let Ok((_, handshake)) = parse_handshake(&handshake_buffer) { if let Ok((_, handshake)) = parse_handshake(&handshake_buffer) {
break handshake; break handshake;
} }
}; };
handshake_reply(user_object, handshake.subsystem); handshake_reply(user_object, handshake.subsystem);
// Read and process input // Read and process input
loop { loop {
let buffer = recv!(rx); let buffer = recv!(rx);
if let Err(msg) = process::line(user_object, &buffer) { if let Err(msg) = process::line(user_object, &buffer) {
log::error!("{}", msg); log::error!("{}", msg);
} }
} }
} }
macro_rules! handshake_register { macro_rules! handshake_register {
($obj: ident, $func: ident, $subsystem: expr, $type: expr, $name: expr) => { ($obj: ident, $func: ident, $subsystem: expr, $type: expr, $name: expr) => {
if $obj.$func() { if $obj.$func() {
println!("register|{}|{}|{}", $type, $subsystem.to_string(), $name); println!("register|{}|{}|{}", $type, $subsystem.to_string(), $name);
log::trace!( log::trace!(
"{} {} for {} registered", "{} {} for {} registered",
$type, $type,
$name, $name,
$subsystem.to_string() $subsystem.to_string()
); );
} }
}; };
} }
fn handshake_reply<T>(obj: &mut T, ss: SubSystem) fn handshake_reply<T>(obj: &mut T, ss: SubSystem)
where where
T: Filter, T: Filter,
{ {
// Filters // Filters
handshake_register!(obj, has_filter_auth, ss, "filter", "auth"); handshake_register!(obj, has_filter_auth, ss, "filter", "auth");
handshake_register!(obj, has_filter_commit, ss, "filter", "commit"); handshake_register!(obj, has_filter_commit, ss, "filter", "commit");
handshake_register!(obj, has_filter_connect, ss, "filter", "connect"); handshake_register!(obj, has_filter_connect, ss, "filter", "connect");
handshake_register!(obj, has_filter_data, ss, "filter", "data"); handshake_register!(obj, has_filter_data, ss, "filter", "data");
handshake_register!(obj, has_filter_data_line, ss, "filter", "data-line"); handshake_register!(obj, has_filter_data_line, ss, "filter", "data-line");
handshake_register!(obj, has_filter_ehlo, ss, "filter", "ehlo"); handshake_register!(obj, has_filter_ehlo, ss, "filter", "ehlo");
handshake_register!(obj, has_filter_helo, ss, "filter", "helo"); handshake_register!(obj, has_filter_helo, ss, "filter", "helo");
handshake_register!(obj, has_filter_mail_from, ss, "filter", "mail-from"); handshake_register!(obj, has_filter_mail_from, ss, "filter", "mail-from");
handshake_register!(obj, has_filter_rcpt_to, ss, "filter", "rcpt-to"); handshake_register!(obj, has_filter_rcpt_to, ss, "filter", "rcpt-to");
handshake_register!(obj, has_filter_starttls, ss, "filter", "starttls"); handshake_register!(obj, has_filter_starttls, ss, "filter", "starttls");
// Reports // Reports
handshake_register!(obj, has_report_link_auth, ss, "report", "link-auth"); handshake_register!(obj, has_report_link_auth, ss, "report", "link-auth");
handshake_register!(obj, has_report_link_connect, ss, "report", "link-connect"); handshake_register!(obj, has_report_link_connect, ss, "report", "link-connect");
handshake_register!( handshake_register!(
obj, obj,
has_report_link_disconnect, has_report_link_disconnect,
ss, ss,
"report", "report",
"link-disconnect" "link-disconnect"
); );
handshake_register!(obj, has_report_link_greeting, ss, "report", "link-greeting"); handshake_register!(obj, has_report_link_greeting, ss, "report", "link-greeting");
handshake_register!(obj, has_report_link_identify, ss, "report", "link-identify"); handshake_register!(obj, has_report_link_identify, ss, "report", "link-identify");
handshake_register!(obj, has_report_link_tls, ss, "report", "link-tls"); handshake_register!(obj, has_report_link_tls, ss, "report", "link-tls");
handshake_register!(obj, has_report_tx_begin, ss, "report", "tx-begin"); handshake_register!(obj, has_report_tx_begin, ss, "report", "tx-begin");
handshake_register!(obj, has_report_tx_mail, ss, "report", "tx-mail"); handshake_register!(obj, has_report_tx_mail, ss, "report", "tx-mail");
handshake_register!(obj, has_report_tx_reset, ss, "report", "tx-reset"); handshake_register!(obj, has_report_tx_reset, ss, "report", "tx-reset");
handshake_register!(obj, has_report_tx_rcpt, ss, "report", "tx-rcpt"); handshake_register!(obj, has_report_tx_rcpt, ss, "report", "tx-rcpt");
handshake_register!(obj, has_report_tx_envelope, ss, "report", "tx-envelope"); handshake_register!(obj, has_report_tx_envelope, ss, "report", "tx-envelope");
handshake_register!(obj, has_report_tx_data, ss, "report", "tx-data"); handshake_register!(obj, has_report_tx_data, ss, "report", "tx-data");
handshake_register!(obj, has_report_tx_commit, ss, "report", "tx-commit"); handshake_register!(obj, has_report_tx_commit, ss, "report", "tx-commit");
handshake_register!(obj, has_report_tx_rollback, ss, "report", "tx-rollback"); handshake_register!(obj, has_report_tx_rollback, ss, "report", "tx-rollback");
handshake_register!( handshake_register!(
obj, obj,
has_report_protocol_client, has_report_protocol_client,
ss, ss,
"report", "report",
"protocol-client" "protocol-client"
); );
handshake_register!( handshake_register!(
obj, obj,
has_report_protocol_server, has_report_protocol_server,
ss, ss,
"report", "report",
"protocol-server" "protocol-server"
); );
handshake_register!( handshake_register!(
obj, obj,
has_report_filter_response, has_report_filter_response,
ss, ss,
"report", "report",
"filter-response" "filter-response"
); );
handshake_register!(obj, has_report_filter_report, ss, "report", "filter-report"); handshake_register!(obj, has_report_filter_report, ss, "report", "filter-report");
handshake_register!(obj, has_report_timeout, ss, "report", "timeout"); handshake_register!(obj, has_report_timeout, ss, "report", "timeout");
// Ready // Ready
println!("register|ready"); println!("register|ready");
log::trace!("register ready"); log::trace!("register ready");
} }

View file

@ -10,126 +10,126 @@ use nom::combinator::map_res;
use nom::IResult; use nom::IResult;
pub struct ReportEntry { pub struct ReportEntry {
pub version: String, pub version: String,
pub timestamp: TimeVal, pub timestamp: TimeVal,
pub subsystem: SubSystem, pub subsystem: SubSystem,
pub event: Event, pub event: Event,
pub session_id: String, pub session_id: String,
} }
pub struct FilterEntry { pub struct FilterEntry {
pub version: String, pub version: String,
pub timestamp: TimeVal, pub timestamp: TimeVal,
pub subsystem: SubSystem, pub subsystem: SubSystem,
pub phase: FilterPhase, pub phase: FilterPhase,
pub session_id: String, pub session_id: String,
pub token: String, pub token: String,
} }
pub(crate) enum EntryOption { pub(crate) enum EntryOption {
Report(ReportEntry), Report(ReportEntry),
Filter(FilterEntry), Filter(FilterEntry),
} }
pub(crate) fn parse_entry(input: &[u8]) -> IResult<&[u8], EntryOption> { pub(crate) fn parse_entry(input: &[u8]) -> IResult<&[u8], EntryOption> {
let (input, entry) = alt((parse_report_entry_meta, parse_filter_entry_meta))(input)?; let (input, entry) = alt((parse_report_entry_meta, parse_filter_entry_meta))(input)?;
Ok((input, entry)) Ok((input, entry))
} }
fn parse_report_entry_meta(input: &[u8]) -> IResult<&[u8], EntryOption> { fn parse_report_entry_meta(input: &[u8]) -> IResult<&[u8], EntryOption> {
let (input, _) = tag("report")(input)?; let (input, _) = tag("report")(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, entry) = parse_report_entry(input)?; let (input, entry) = parse_report_entry(input)?;
Ok((input, EntryOption::Report(entry))) Ok((input, EntryOption::Report(entry)))
} }
fn parse_filter_entry_meta(input: &[u8]) -> IResult<&[u8], EntryOption> { fn parse_filter_entry_meta(input: &[u8]) -> IResult<&[u8], EntryOption> {
let (input, _) = tag("filter")(input)?; let (input, _) = tag("filter")(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, entry) = parse_filter_entry(input)?; let (input, entry) = parse_filter_entry(input)?;
Ok((input, EntryOption::Filter(entry))) Ok((input, EntryOption::Filter(entry)))
} }
fn parse_report_entry(input: &[u8]) -> IResult<&[u8], ReportEntry> { fn parse_report_entry(input: &[u8]) -> IResult<&[u8], ReportEntry> {
let (input, version) = parse_string_parameter(input)?; let (input, version) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, timestamp) = parse_timestamp(input)?; let (input, timestamp) = parse_timestamp(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, subsystem) = parse_data_structure::<SubSystem>(input)?; let (input, subsystem) = parse_data_structure::<SubSystem>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, event) = parse_data_structure::<Event>(input)?; let (input, event) = parse_data_structure::<Event>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, session_id) = parse_string_parameter(input)?; let (input, session_id) = parse_string_parameter(input)?;
let entry = ReportEntry { let entry = ReportEntry {
version, version,
timestamp, timestamp,
subsystem, subsystem,
event, event,
session_id, session_id,
}; };
Ok((input, entry)) Ok((input, entry))
} }
fn parse_filter_entry(input: &[u8]) -> IResult<&[u8], FilterEntry> { fn parse_filter_entry(input: &[u8]) -> IResult<&[u8], FilterEntry> {
let (input, version) = parse_string_parameter(input)?; let (input, version) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, timestamp) = parse_timestamp(input)?; let (input, timestamp) = parse_timestamp(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, subsystem) = parse_data_structure::<SubSystem>(input)?; let (input, subsystem) = parse_data_structure::<SubSystem>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, phase) = parse_data_structure::<FilterPhase>(input)?; let (input, phase) = parse_data_structure::<FilterPhase>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, session_id) = parse_string_parameter(input)?; let (input, session_id) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, token) = parse_string_parameter(input)?; let (input, token) = parse_string_parameter(input)?;
let entry = FilterEntry { let entry = FilterEntry {
version, version,
timestamp, timestamp,
subsystem, subsystem,
phase, phase,
session_id, session_id,
token, token,
}; };
Ok((input, entry)) Ok((input, entry))
} }
fn parse_timestamp(input: &[u8]) -> IResult<&[u8], TimeVal> { fn parse_timestamp(input: &[u8]) -> IResult<&[u8], TimeVal> {
let (input, sec) = map_res(digit1, |s| String::from_utf8_lossy(s).parse::<i64>())(input)?; let (input, sec) = map_res(digit1, |s| String::from_utf8_lossy(s).parse::<i64>())(input)?;
let (input, _) = tag(".")(input)?; let (input, _) = tag(".")(input)?;
let (input, usec) = map_res(digit1, |s| { let (input, usec) = map_res(digit1, |s| {
format!("{:0<6}", String::from_utf8_lossy(s)).parse::<i64>() format!("{:0<6}", String::from_utf8_lossy(s)).parse::<i64>()
})(input)?; })(input)?;
let timestamp = TimeVal { sec, usec }; let timestamp = TimeVal { sec, usec };
Ok((input, timestamp)) Ok((input, timestamp))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_report_link_connect() { fn test_report_link_connect() {
let input = b"report|0.5|1576146008.06099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25"; let input = b"report|0.5|1576146008.06099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25";
let res = parse_entry(input); let res = parse_entry(input);
assert!(res.is_ok()); assert!(res.is_ok());
let (_, res) = res.unwrap(); let (_, res) = res.unwrap();
let res = match res { let res = match res {
EntryOption::Report(r) => r, EntryOption::Report(r) => r,
_ => { _ => {
assert!(false); assert!(false);
return; return;
} }
}; };
assert_eq!(res.version, String::from("0.5")); assert_eq!(res.version, String::from("0.5"));
assert_eq!( assert_eq!(
res.timestamp, res.timestamp,
TimeVal { TimeVal {
sec: 1576146008, sec: 1576146008,
usec: 60990 usec: 60990
} }
); );
assert_eq!(res.subsystem, SubSystem::SmtpIn); assert_eq!(res.subsystem, SubSystem::SmtpIn);
assert_eq!(res.event, Event::LinkConnect); assert_eq!(res.event, Event::LinkConnect);
assert_eq!(res.session_id, String::from("7641df9771b4ed00")); assert_eq!(res.session_id, String::from("7641df9771b4ed00"));
} }
} }

View file

@ -1,5 +1,5 @@
use super::{ use super::{
parse_data_structure, parse_delimiter, parse_eol, parse_string_parameter, parse_usize, parse_data_structure, parse_delimiter, parse_eol, parse_string_parameter, parse_usize,
}; };
use crate::SubSystem; use crate::SubSystem;
use nom::bytes::streaming::tag; use nom::bytes::streaming::tag;
@ -7,116 +7,116 @@ use nom::IResult;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Handshake { pub(crate) struct Handshake {
pub(crate) smtpd_version: String, pub(crate) smtpd_version: String,
pub(crate) smtp_session_timeout: usize, pub(crate) smtp_session_timeout: usize,
pub(crate) subsystem: SubSystem, pub(crate) subsystem: SubSystem,
} }
pub(crate) fn parse_handshake(input: &[u8]) -> IResult<&[u8], Handshake> { pub(crate) fn parse_handshake(input: &[u8]) -> IResult<&[u8], Handshake> {
let (input, smtpd_version) = parse_smtpd_version(input)?; let (input, smtpd_version) = parse_smtpd_version(input)?;
let (input, smtp_session_timeout) = parse_smtp_session_timeout(input)?; let (input, smtp_session_timeout) = parse_smtp_session_timeout(input)?;
let (input, subsystem) = parse_subsystem(input)?; let (input, subsystem) = parse_subsystem(input)?;
let (input, _) = parse_ready(input)?; let (input, _) = parse_ready(input)?;
let handshake = Handshake { let handshake = Handshake {
smtpd_version, smtpd_version,
smtp_session_timeout, smtp_session_timeout,
subsystem, subsystem,
}; };
Ok((input, handshake)) Ok((input, handshake))
} }
fn parse_smtpd_version(input: &[u8]) -> IResult<&[u8], String> { fn parse_smtpd_version(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_config_initial(input)?; let (input, _) = parse_config_initial(input)?;
let (input, _) = tag("smtpd-version")(input)?; let (input, _) = tag("smtpd-version")(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, version) = parse_string_parameter(input)?; let (input, version) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, version)) Ok((input, version))
} }
fn parse_smtp_session_timeout(input: &[u8]) -> IResult<&[u8], usize> { fn parse_smtp_session_timeout(input: &[u8]) -> IResult<&[u8], usize> {
let (input, _) = parse_config_initial(input)?; let (input, _) = parse_config_initial(input)?;
let (input, _) = tag("smtp-session-timeout")(input)?; let (input, _) = tag("smtp-session-timeout")(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, timeout) = parse_usize(input)?; let (input, timeout) = parse_usize(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, timeout)) Ok((input, timeout))
} }
fn parse_subsystem(input: &[u8]) -> IResult<&[u8], SubSystem> { fn parse_subsystem(input: &[u8]) -> IResult<&[u8], SubSystem> {
let (input, _) = parse_config_initial(input)?; let (input, _) = parse_config_initial(input)?;
let (input, _) = tag("subsystem")(input)?; let (input, _) = tag("subsystem")(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, subsystem) = parse_data_structure::<SubSystem>(input)?; let (input, subsystem) = parse_data_structure::<SubSystem>(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, subsystem)) Ok((input, subsystem))
} }
fn parse_ready(input: &[u8]) -> IResult<&[u8], ()> { fn parse_ready(input: &[u8]) -> IResult<&[u8], ()> {
let (input, _) = parse_config_initial(input)?; let (input, _) = parse_config_initial(input)?;
let (input, _) = tag("ready")(input)?; let (input, _) = tag("ready")(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, ())) Ok((input, ()))
} }
fn parse_config_initial(input: &[u8]) -> IResult<&[u8], ()> { fn parse_config_initial(input: &[u8]) -> IResult<&[u8], ()> {
let (input, _) = tag("config")(input)?; let (input, _) = tag("config")(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
Ok((input, ())) Ok((input, ()))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::parse_handshake; use super::parse_handshake;
use crate::SubSystem; use crate::SubSystem;
#[test] #[test]
fn test_valid_handshake_nl() { fn test_valid_handshake_nl() {
let input = b"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready\n"; let input = b"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready\n";
let r = parse_handshake(input); let r = parse_handshake(input);
assert!(r.is_ok()); assert!(r.is_ok());
let (r, h) = r.unwrap(); let (r, h) = r.unwrap();
assert_eq!(r, b""); assert_eq!(r, b"");
assert_eq!(h.smtpd_version, "6.6.1"); assert_eq!(h.smtpd_version, "6.6.1");
assert_eq!(h.smtp_session_timeout, 300); assert_eq!(h.smtp_session_timeout, 300);
assert_eq!(h.subsystem, SubSystem::SmtpIn); assert_eq!(h.subsystem, SubSystem::SmtpIn);
} }
#[test] #[test]
fn test_valid_handshake_crnl() { fn test_valid_handshake_crnl() {
let input = b"config|smtpd-version|6.6.1\r\nconfig|smtp-session-timeout|300\r\nconfig|subsystem|smtp-in\r\nconfig|ready\r\n"; let input = b"config|smtpd-version|6.6.1\r\nconfig|smtp-session-timeout|300\r\nconfig|subsystem|smtp-in\r\nconfig|ready\r\n";
let r = parse_handshake(input); let r = parse_handshake(input);
assert!(r.is_ok()); assert!(r.is_ok());
let (r, h) = r.unwrap(); let (r, h) = r.unwrap();
assert_eq!(r, b""); assert_eq!(r, b"");
assert_eq!(h.smtpd_version, "6.6.1"); assert_eq!(h.smtpd_version, "6.6.1");
assert_eq!(h.smtp_session_timeout, 300); assert_eq!(h.smtp_session_timeout, 300);
assert_eq!(h.subsystem, SubSystem::SmtpIn); assert_eq!(h.subsystem, SubSystem::SmtpIn);
} }
#[test] #[test]
fn test_valid_handshake_over() { fn test_valid_handshake_over() {
let input = b"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready\nplop"; let input = b"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready\nplop";
let r = parse_handshake(input); let r = parse_handshake(input);
assert!(r.is_ok()); assert!(r.is_ok());
let (r, h) = r.unwrap(); let (r, h) = r.unwrap();
assert_eq!(r, b"plop"); assert_eq!(r, b"plop");
assert_eq!(h.smtpd_version, "6.6.1"); assert_eq!(h.smtpd_version, "6.6.1");
assert_eq!(h.smtp_session_timeout, 300); assert_eq!(h.smtp_session_timeout, 300);
assert_eq!(h.subsystem, SubSystem::SmtpIn); assert_eq!(h.subsystem, SubSystem::SmtpIn);
} }
#[test] #[test]
fn test_invalid_handshakes() { fn test_invalid_handshakes() {
let test_vectors = vec![ let test_vectors = vec![
"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|\nconfig|subsystem|smtp-in\nconfig|ready\n", "config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|\nconfig|subsystem|smtp-in\nconfig|ready\n",
"config|smtp-session-timeout|300\nconfig|smtpd-version|6.6.1\nconfig|subsystem|smtp-in\nconfig|ready\n", "config|smtp-session-timeout|300\nconfig|smtpd-version|6.6.1\nconfig|subsystem|smtp-in\nconfig|ready\n",
"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready", "config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready",
"config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready\r", "config|smtpd-version|6.6.1\nconfig|smtp-session-timeout|300\nconfig|subsystem|smtp-in\nconfig|ready\r",
]; ];
for input in test_vectors { for input in test_vectors {
let r = parse_handshake(input.as_bytes()); let r = parse_handshake(input.as_bytes());
assert!(r.is_err()); assert!(r.is_err());
} }
} }
} }

View file

@ -9,58 +9,58 @@ use nom::IResult;
use std::str::FromStr; use std::str::FromStr;
fn is_body_char(c: u8) -> bool { fn is_body_char(c: u8) -> bool {
!(c as char).is_control() !(c as char).is_control()
} }
fn is_parameter_char(c: u8) -> bool { fn is_parameter_char(c: u8) -> bool {
is_body_char(c) && (c as char) != '|' is_body_char(c) && (c as char) != '|'
} }
fn parse_string_parameter(input: &[u8]) -> IResult<&[u8], String> { fn parse_string_parameter(input: &[u8]) -> IResult<&[u8], String> {
let (input, s) = take_while1(is_parameter_char)(input)?; let (input, s) = take_while1(is_parameter_char)(input)?;
Ok((input, String::from_utf8(s.to_vec()).unwrap())) Ok((input, String::from_utf8(s.to_vec()).unwrap()))
} }
fn parse_data_structure<T>(input: &[u8]) -> IResult<&[u8], T> fn parse_data_structure<T>(input: &[u8]) -> IResult<&[u8], T>
where where
T: FromStr, T: FromStr,
{ {
map_res(take_while1(is_parameter_char), |s: &[u8]| { map_res(take_while1(is_parameter_char), |s: &[u8]| {
T::from_str(&String::from_utf8_lossy(s)) T::from_str(&String::from_utf8_lossy(s))
})(input) })(input)
} }
fn parse_delimiter(input: &[u8]) -> IResult<&[u8], &[u8]> { fn parse_delimiter(input: &[u8]) -> IResult<&[u8], &[u8]> {
tag("|")(input) tag("|")(input)
} }
fn parse_eol(input: &[u8]) -> IResult<&[u8], &[u8]> { fn parse_eol(input: &[u8]) -> IResult<&[u8], &[u8]> {
alt((tag("\r\n"), tag("\n")))(input) alt((tag("\r\n"), tag("\n")))(input)
} }
fn parse_usize(input: &[u8]) -> IResult<&[u8], usize> { fn parse_usize(input: &[u8]) -> IResult<&[u8], usize> {
map_res(take_while1(|c| (c as char).is_ascii_digit()), |s| { map_res(take_while1(|c| (c as char).is_ascii_digit()), |s| {
usize::from_str_radix(&String::from_utf8_lossy(s), 10) usize::from_str_radix(&String::from_utf8_lossy(s), 10)
})(input) })(input)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::is_parameter_char; use super::is_parameter_char;
#[test] #[test]
fn test_valid_parameter_char() { fn test_valid_parameter_char() {
let char_lst = "a0.:-_/"; let char_lst = "a0.:-_/";
for c in char_lst.bytes() { for c in char_lst.bytes() {
assert!(is_parameter_char(c)); assert!(is_parameter_char(c));
} }
} }
#[test] #[test]
fn test_invalid_parameter_char() { fn test_invalid_parameter_char() {
let char_lst = "|\n"; let char_lst = "|\n";
for c in char_lst.bytes() { for c in char_lst.bytes() {
assert!(!is_parameter_char(c)); assert!(!is_parameter_char(c));
} }
} }
} }

View file

@ -1,6 +1,6 @@
use super::{ use super::{
is_body_char, is_parameter_char, parse_data_structure, parse_delimiter, parse_eol, is_body_char, is_parameter_char, parse_data_structure, parse_delimiter, parse_eol,
parse_string_parameter, parse_usize, parse_string_parameter, parse_usize,
}; };
use crate::{Address, AuthResult, FilterKind, FilterPhase, MailResult, Method}; use crate::{Address, AuthResult, FilterKind, FilterPhase, MailResult, Method};
use nom::branch::alt; use nom::branch::alt;
@ -11,338 +11,338 @@ use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
pub(crate) fn parse_filter_auth(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_filter_auth(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_string_parameter(input)?; let (input, s) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_filter_connect( pub(crate) fn parse_filter_connect(
input: &[u8], input: &[u8],
) -> IResult<&[u8], (String, String, Address, Address)> { ) -> IResult<&[u8], (String, String, Address, Address)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, rdns) = parse_string_parameter(input)?; let (input, rdns) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, fcrdns) = parse_string_parameter(input)?; let (input, fcrdns) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, src) = parse_address(input)?; let (input, src) = parse_address(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, dest) = parse_address(input)?; let (input, dest) = parse_address(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (rdns, fcrdns, src, dest))) Ok((input, (rdns, fcrdns, src, dest)))
} }
pub(crate) fn parse_filter_data_line(input: &[u8]) -> IResult<&[u8], &[u8]> { pub(crate) fn parse_filter_data_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = take_while(is_body_char)(input)?; let (input, s) = take_while(is_body_char)(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_filter_ehlo(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_filter_ehlo(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_string_parameter(input)?; let (input, s) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_filter_helo(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_filter_helo(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_string_parameter(input)?; let (input, s) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_filter_mail_from(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_filter_mail_from(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_string_parameter(input)?; let (input, s) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_filter_rcpt_to(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_filter_rcpt_to(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_string_parameter(input)?; let (input, s) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_filter_starttls(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_filter_starttls(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_string_parameter(input)?; let (input, s) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, s)) Ok((input, s))
} }
pub(crate) fn parse_report_link_auth(input: &[u8]) -> IResult<&[u8], (String, AuthResult)> { pub(crate) fn parse_report_link_auth(input: &[u8]) -> IResult<&[u8], (String, AuthResult)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, username) = parse_string_parameter(input)?; let (input, username) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, result) = parse_data_structure::<AuthResult>(input)?; let (input, result) = parse_data_structure::<AuthResult>(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (username, result))) Ok((input, (username, result)))
} }
pub(crate) fn parse_report_link_connect( pub(crate) fn parse_report_link_connect(
input: &[u8], input: &[u8],
) -> IResult<&[u8], (String, String, Address, Address)> { ) -> IResult<&[u8], (String, String, Address, Address)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, rdns) = parse_string_parameter(input)?; let (input, rdns) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, fcrdns) = parse_string_parameter(input)?; let (input, fcrdns) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, src) = parse_address(input)?; let (input, src) = parse_address(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, dest) = parse_address(input)?; let (input, dest) = parse_address(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (rdns, fcrdns, src, dest))) Ok((input, (rdns, fcrdns, src, dest)))
} }
pub(crate) fn parse_report_link_greeting(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_report_link_greeting(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, hostname) = parse_string_parameter(input)?; let (input, hostname) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, hostname)) Ok((input, hostname))
} }
pub(crate) fn parse_report_link_identify(input: &[u8]) -> IResult<&[u8], (Method, String)> { pub(crate) fn parse_report_link_identify(input: &[u8]) -> IResult<&[u8], (Method, String)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, method) = parse_data_structure::<Method>(input)?; let (input, method) = parse_data_structure::<Method>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, identity) = parse_string_parameter(input)?; let (input, identity) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (method, identity))) Ok((input, (method, identity)))
} }
pub(crate) fn parse_report_link_tls(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_report_link_tls(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, tls_string) = parse_string_parameter(input)?; let (input, tls_string) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, tls_string)) Ok((input, tls_string))
} }
pub(crate) fn parse_report_tx_begin(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_report_tx_begin(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, id)) Ok((input, id))
} }
pub(crate) fn parse_report_tx_mail(input: &[u8]) -> IResult<&[u8], (String, MailResult, String)> { pub(crate) fn parse_report_tx_mail(input: &[u8]) -> IResult<&[u8], (String, MailResult, String)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, result) = parse_data_structure::<MailResult>(input)?; let (input, result) = parse_data_structure::<MailResult>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, addr) = parse_string_parameter(input)?; let (input, addr) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (id, result, addr))) Ok((input, (id, result, addr)))
} }
pub(crate) fn parse_report_tx_reset(input: &[u8]) -> IResult<&[u8], Option<String>> { pub(crate) fn parse_report_tx_reset(input: &[u8]) -> IResult<&[u8], Option<String>> {
let (input, id) = opt(parse_tx_reset_opt)(input)?; let (input, id) = opt(parse_tx_reset_opt)(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, id)) Ok((input, id))
} }
fn parse_tx_reset_opt(input: &[u8]) -> IResult<&[u8], String> { fn parse_tx_reset_opt(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
Ok((input, id)) Ok((input, id))
} }
pub(crate) fn parse_report_tx_rcpt(input: &[u8]) -> IResult<&[u8], (String, MailResult, String)> { pub(crate) fn parse_report_tx_rcpt(input: &[u8]) -> IResult<&[u8], (String, MailResult, String)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, result) = parse_data_structure::<MailResult>(input)?; let (input, result) = parse_data_structure::<MailResult>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, addr) = parse_string_parameter(input)?; let (input, addr) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (id, result, addr))) Ok((input, (id, result, addr)))
} }
pub(crate) fn parse_report_tx_envelope(input: &[u8]) -> IResult<&[u8], (String, String)> { pub(crate) fn parse_report_tx_envelope(input: &[u8]) -> IResult<&[u8], (String, String)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, msg) = parse_string_parameter(input)?; let (input, msg) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, env) = parse_string_parameter(input)?; let (input, env) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (msg, env))) Ok((input, (msg, env)))
} }
pub(crate) fn parse_report_tx_data(input: &[u8]) -> IResult<&[u8], (String, MailResult)> { pub(crate) fn parse_report_tx_data(input: &[u8]) -> IResult<&[u8], (String, MailResult)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, result) = parse_data_structure::<MailResult>(input)?; let (input, result) = parse_data_structure::<MailResult>(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (id, result))) Ok((input, (id, result)))
} }
pub(crate) fn parse_report_tx_commit(input: &[u8]) -> IResult<&[u8], (String, usize)> { pub(crate) fn parse_report_tx_commit(input: &[u8]) -> IResult<&[u8], (String, usize)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, s) = parse_usize(input)?; let (input, s) = parse_usize(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (id, s))) Ok((input, (id, s)))
} }
pub(crate) fn parse_report_tx_rollback(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_report_tx_rollback(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, id) = parse_string_parameter(input)?; let (input, id) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, id)) Ok((input, id))
} }
pub(crate) fn parse_report_protocol_client(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_report_protocol_client(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, cmd) = parse_string_parameter(input)?; let (input, cmd) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, cmd)) Ok((input, cmd))
} }
pub(crate) fn parse_report_protocol_server(input: &[u8]) -> IResult<&[u8], String> { pub(crate) fn parse_report_protocol_server(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, res) = parse_string_parameter(input)?; let (input, res) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, res)) Ok((input, res))
} }
pub(crate) fn parse_report_filter_response( pub(crate) fn parse_report_filter_response(
input: &[u8], input: &[u8],
) -> IResult<&[u8], (FilterPhase, String, Option<String>)> { ) -> IResult<&[u8], (FilterPhase, String, Option<String>)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, phase) = parse_data_structure::<FilterPhase>(input)?; let (input, phase) = parse_data_structure::<FilterPhase>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, res) = parse_string_parameter(input)?; let (input, res) = parse_string_parameter(input)?;
let (input, param) = opt(parse_filter_response_opt)(input)?; let (input, param) = opt(parse_filter_response_opt)(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (phase, res, param))) Ok((input, (phase, res, param)))
} }
fn parse_filter_response_opt(input: &[u8]) -> IResult<&[u8], String> { fn parse_filter_response_opt(input: &[u8]) -> IResult<&[u8], String> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
parse_string_parameter(input) parse_string_parameter(input)
} }
pub(crate) fn parse_report_filter_report( pub(crate) fn parse_report_filter_report(
input: &[u8], input: &[u8],
) -> IResult<&[u8], (FilterKind, String, String)> { ) -> IResult<&[u8], (FilterKind, String, String)> {
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, kind) = parse_data_structure::<FilterKind>(input)?; let (input, kind) = parse_data_structure::<FilterKind>(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, name) = parse_string_parameter(input)?; let (input, name) = parse_string_parameter(input)?;
let (input, _) = parse_delimiter(input)?; let (input, _) = parse_delimiter(input)?;
let (input, message) = parse_string_parameter(input)?; let (input, message) = parse_string_parameter(input)?;
let (input, _) = parse_eol(input)?; let (input, _) = parse_eol(input)?;
Ok((input, (kind, name, message))) Ok((input, (kind, name, message)))
} }
fn parse_address(input: &[u8]) -> IResult<&[u8], Address> { fn parse_address(input: &[u8]) -> IResult<&[u8], Address> {
alt((parse_unix_socket, parse_socketaddr))(input) alt((parse_unix_socket, parse_socketaddr))(input)
} }
fn parse_socketaddr(input: &[u8]) -> IResult<&[u8], Address> { fn parse_socketaddr(input: &[u8]) -> IResult<&[u8], Address> {
map_res( map_res(
take_while1(is_parameter_char), take_while1(is_parameter_char),
|s: &[u8]| -> Result<Address, String> { |s: &[u8]| -> Result<Address, String> {
let s = String::from_utf8(s.to_vec()).map_err(|e| e.to_string())?; let s = String::from_utf8(s.to_vec()).map_err(|e| e.to_string())?;
let addr = s.parse::<SocketAddr>().map_err(|e| e.to_string())?; let addr = s.parse::<SocketAddr>().map_err(|e| e.to_string())?;
Ok(Address::Ip(addr)) Ok(Address::Ip(addr))
}, },
)(input) )(input)
} }
fn parse_unix_socket(input: &[u8]) -> IResult<&[u8], Address> { fn parse_unix_socket(input: &[u8]) -> IResult<&[u8], Address> {
let (input, _) = tag("unix:")(input)?; let (input, _) = tag("unix:")(input)?;
map_res( map_res(
take_while1(is_parameter_char), take_while1(is_parameter_char),
|s: &[u8]| -> Result<Address, String> { |s: &[u8]| -> Result<Address, String> {
let s = String::from_utf8(s.to_vec()).map_err(|e| e.to_string())?; let s = String::from_utf8(s.to_vec()).map_err(|e| e.to_string())?;
let addr = s.parse::<PathBuf>().map_err(|e| e.to_string())?; let addr = s.parse::<PathBuf>().map_err(|e| e.to_string())?;
Ok(Address::UnixSocket(addr)) Ok(Address::UnixSocket(addr))
}, },
)(input) )(input)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::Path; use std::path::Path;
#[test] #[test]
fn test_ipv4_port() { fn test_ipv4_port() {
let res = parse_address(b"199.185.178.25:33174|"); let res = parse_address(b"199.185.178.25:33174|");
assert!(res.is_ok()); assert!(res.is_ok());
let (i, addr) = res.unwrap(); let (i, addr) = res.unwrap();
assert_eq!(i, b"|"); assert_eq!(i, b"|");
match addr { match addr {
Address::Ip(addr) => { Address::Ip(addr) => {
assert!(addr.is_ipv4()); assert!(addr.is_ipv4());
assert_eq!(addr.port(), 33174); assert_eq!(addr.port(), 33174);
assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(199, 185, 178, 25))); assert_eq!(addr.ip(), IpAddr::V4(Ipv4Addr::new(199, 185, 178, 25)));
} }
Address::UnixSocket(_) => assert!(false), Address::UnixSocket(_) => assert!(false),
}; };
} }
#[test] #[test]
fn test_ipv6_port() { fn test_ipv6_port() {
let res = parse_address(b"[2001:db8::42]:33174\n"); let res = parse_address(b"[2001:db8::42]:33174\n");
assert!(res.is_ok()); assert!(res.is_ok());
let (i, addr) = res.unwrap(); let (i, addr) = res.unwrap();
assert_eq!(i, b"\n"); assert_eq!(i, b"\n");
match addr { match addr {
Address::Ip(addr) => { Address::Ip(addr) => {
assert!(addr.is_ipv6()); assert!(addr.is_ipv6());
assert_eq!(addr.port(), 33174); assert_eq!(addr.port(), 33174);
assert_eq!( assert_eq!(
addr.ip(), addr.ip(),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0x42)) IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0x42))
); );
} }
Address::UnixSocket(_) => assert!(false), Address::UnixSocket(_) => assert!(false),
}; };
} }
#[test] #[test]
fn test_unix_socket() { fn test_unix_socket() {
let res = parse_address(b"unix:/var/something.sock|"); let res = parse_address(b"unix:/var/something.sock|");
assert!(res.is_ok()); assert!(res.is_ok());
let (i, addr) = res.unwrap(); let (i, addr) = res.unwrap();
assert_eq!(i, b"|"); assert_eq!(i, b"|");
match addr { match addr {
Address::UnixSocket(addr) => { Address::UnixSocket(addr) => {
assert_eq!(addr, Path::new("/var/something.sock").to_path_buf()); assert_eq!(addr, Path::new("/var/something.sock").to_path_buf());
} }
Address::Ip(_) => assert!(false), Address::Ip(_) => assert!(false),
}; };
} }
#[test] #[test]
fn test_valid_parse_filter_auth() { fn test_valid_parse_filter_auth() {
let test_vectors = vec![ let test_vectors = vec![
("|derp\n", "derp"), ("|derp\n", "derp"),
("|derp.derpson@example.com\r\n", "derp.derpson@example.com"), ("|derp.derpson@example.com\r\n", "derp.derpson@example.com"),
]; ];
for (test, ref_auth) in test_vectors { for (test, ref_auth) in test_vectors {
let res = parse_filter_auth(test.as_bytes()); let res = parse_filter_auth(test.as_bytes());
assert!(res.is_ok()); assert!(res.is_ok());
let (input, auth) = res.unwrap(); let (input, auth) = res.unwrap();
assert_eq!(input, b""); assert_eq!(input, b"");
assert_eq!(auth, ref_auth.to_string()); assert_eq!(auth, ref_auth.to_string());
} }
} }
#[test] #[test]
fn test_invalid_parse_filter_auth() { fn test_invalid_parse_filter_auth() {
let test_vectors = vec!["|\n", "|\r\n", "|derp", "|derp|derpson\n"]; let test_vectors = vec!["|\n", "|\r\n", "|derp", "|derp|derpson\n"];
for test in test_vectors { for test in test_vectors {
let res = parse_filter_auth(test.as_bytes()); let res = parse_filter_auth(test.as_bytes());
assert!(!res.is_ok()); assert!(!res.is_ok());
} }
} }
} }

View file

@ -1,166 +1,166 @@
use crate::error::nom_err_to_string; use crate::error::nom_err_to_string;
use crate::parsers::entry::{parse_entry, EntryOption}; use crate::parsers::entry::{parse_entry, EntryOption};
use crate::parsers::parameters::{ use crate::parsers::parameters::{
parse_filter_auth, parse_filter_connect, parse_filter_data_line, parse_filter_ehlo, parse_filter_auth, parse_filter_connect, parse_filter_data_line, parse_filter_ehlo,
parse_filter_helo, parse_filter_mail_from, parse_filter_rcpt_to, parse_filter_starttls, parse_filter_helo, parse_filter_mail_from, parse_filter_rcpt_to, parse_filter_starttls,
parse_report_filter_report, parse_report_filter_response, parse_report_link_auth, parse_report_filter_report, parse_report_filter_response, parse_report_link_auth,
parse_report_link_connect, parse_report_link_greeting, parse_report_link_identify, parse_report_link_connect, parse_report_link_greeting, parse_report_link_identify,
parse_report_link_tls, parse_report_protocol_client, parse_report_protocol_server, parse_report_link_tls, parse_report_protocol_client, parse_report_protocol_server,
parse_report_tx_begin, parse_report_tx_commit, parse_report_tx_data, parse_report_tx_envelope, parse_report_tx_begin, parse_report_tx_commit, parse_report_tx_data, parse_report_tx_envelope,
parse_report_tx_mail, parse_report_tx_rcpt, parse_report_tx_reset, parse_report_tx_rollback, parse_report_tx_mail, parse_report_tx_rcpt, parse_report_tx_reset, parse_report_tx_rollback,
}; };
use crate::{Event, Filter, FilterPhase}; use crate::{Event, Filter, FilterPhase};
macro_rules! handle_reports { macro_rules! handle_reports {
($obj: ident, $r: ident, $input: ident) => { ($obj: ident, $r: ident, $input: ident) => {
match $r.event { match $r.event {
Event::LinkAuth => { Event::LinkAuth => {
let (_, (username, result)) = let (_, (username, result)) =
parse_report_link_auth($input).map_err(|e| e.to_string())?; parse_report_link_auth($input).map_err(|e| e.to_string())?;
$obj.on_report_link_auth(&$r, &username, result); $obj.on_report_link_auth(&$r, &username, result);
} }
Event::LinkConnect => { Event::LinkConnect => {
let (_, (rdns, fcrdns, src, dest)) = let (_, (rdns, fcrdns, src, dest)) =
parse_report_link_connect($input).map_err(|e| e.to_string())?; parse_report_link_connect($input).map_err(|e| e.to_string())?;
$obj.on_report_link_connect(&$r, &rdns, &fcrdns, &src, &dest); $obj.on_report_link_connect(&$r, &rdns, &fcrdns, &src, &dest);
} }
Event::LinkDisconnect => { Event::LinkDisconnect => {
$obj.on_report_link_disconnect(&$r); $obj.on_report_link_disconnect(&$r);
} }
Event::LinkGreeting => { Event::LinkGreeting => {
let (_, hostname) = let (_, hostname) =
parse_report_link_greeting($input).map_err(|e| e.to_string())?; parse_report_link_greeting($input).map_err(|e| e.to_string())?;
$obj.on_report_link_greeting(&$r, &hostname); $obj.on_report_link_greeting(&$r, &hostname);
} }
Event::LinkIdentify => { Event::LinkIdentify => {
let (_, (method, identity)) = let (_, (method, identity)) =
parse_report_link_identify($input).map_err(|e| e.to_string())?; parse_report_link_identify($input).map_err(|e| e.to_string())?;
$obj.on_report_link_identify(&$r, method, &identity); $obj.on_report_link_identify(&$r, method, &identity);
} }
Event::LinkTls => { Event::LinkTls => {
let (_, s) = parse_report_link_tls($input).map_err(|e| e.to_string())?; let (_, s) = parse_report_link_tls($input).map_err(|e| e.to_string())?;
$obj.on_report_link_tls(&$r, &s); $obj.on_report_link_tls(&$r, &s);
} }
Event::TxBegin => { Event::TxBegin => {
let (_, id) = parse_report_tx_begin($input).map_err(|e| e.to_string())?; let (_, id) = parse_report_tx_begin($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_begin(&$r, &id); $obj.on_report_tx_begin(&$r, &id);
} }
Event::TxMail => { Event::TxMail => {
let (_, (id, result, addr)) = let (_, (id, result, addr)) =
parse_report_tx_mail($input).map_err(|e| e.to_string())?; parse_report_tx_mail($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_mail(&$r, &id, result, &addr); $obj.on_report_tx_mail(&$r, &id, result, &addr);
} }
Event::TxReset => { Event::TxReset => {
let (_, id) = parse_report_tx_reset($input).map_err(|e| e.to_string())?; let (_, id) = parse_report_tx_reset($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_reset(&$r, &id); $obj.on_report_tx_reset(&$r, &id);
} }
Event::TxRcpt => { Event::TxRcpt => {
let (_, (id, result, addr)) = let (_, (id, result, addr)) =
parse_report_tx_rcpt($input).map_err(|e| e.to_string())?; parse_report_tx_rcpt($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_rcpt(&$r, &id, result, &addr); $obj.on_report_tx_rcpt(&$r, &id, result, &addr);
} }
Event::TxEnvelope => { Event::TxEnvelope => {
let (_, (msg, env)) = let (_, (msg, env)) =
parse_report_tx_envelope($input).map_err(|e| e.to_string())?; parse_report_tx_envelope($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_envelope(&$r, &msg, &env); $obj.on_report_tx_envelope(&$r, &msg, &env);
} }
Event::TxData => { Event::TxData => {
let (_, (id, result)) = parse_report_tx_data($input).map_err(|e| e.to_string())?; let (_, (id, result)) = parse_report_tx_data($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_data(&$r, &id, result); $obj.on_report_tx_data(&$r, &id, result);
} }
Event::TxCommit => { Event::TxCommit => {
let (_, (id, size)) = parse_report_tx_commit($input).map_err(|e| e.to_string())?; let (_, (id, size)) = parse_report_tx_commit($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_commit(&$r, &id, size); $obj.on_report_tx_commit(&$r, &id, size);
} }
Event::TxRollback => { Event::TxRollback => {
let (_, id) = parse_report_tx_rollback($input).map_err(|e| e.to_string())?; let (_, id) = parse_report_tx_rollback($input).map_err(|e| e.to_string())?;
$obj.on_report_tx_rollback(&$r, &id); $obj.on_report_tx_rollback(&$r, &id);
} }
Event::ProtocolClient => { Event::ProtocolClient => {
let (_, cmd) = parse_report_protocol_client($input).map_err(|e| e.to_string())?; let (_, cmd) = parse_report_protocol_client($input).map_err(|e| e.to_string())?;
$obj.on_report_protocol_client(&$r, &cmd); $obj.on_report_protocol_client(&$r, &cmd);
} }
Event::ProtocolServer => { Event::ProtocolServer => {
let (_, res) = parse_report_protocol_server($input).map_err(|e| e.to_string())?; let (_, res) = parse_report_protocol_server($input).map_err(|e| e.to_string())?;
$obj.on_report_protocol_server(&$r, &res); $obj.on_report_protocol_server(&$r, &res);
} }
Event::FilterResponse => { Event::FilterResponse => {
let (_, (phase, res, param)) = let (_, (phase, res, param)) =
parse_report_filter_response($input).map_err(|e| e.to_string())?; parse_report_filter_response($input).map_err(|e| e.to_string())?;
$obj.on_report_filter_response(&$r, phase, &res, &param); $obj.on_report_filter_response(&$r, phase, &res, &param);
} }
Event::FilterReport => { Event::FilterReport => {
let (_, (kind, name, message)) = let (_, (kind, name, message)) =
parse_report_filter_report($input).map_err(|e| e.to_string())?; parse_report_filter_report($input).map_err(|e| e.to_string())?;
$obj.on_report_filter_report(&$r, kind, &name, &message); $obj.on_report_filter_report(&$r, kind, &name, &message);
} }
Event::Timeout => { Event::Timeout => {
$obj.on_report_timeout(&$r); $obj.on_report_timeout(&$r);
} }
} }
}; };
} }
macro_rules! handle_filters { macro_rules! handle_filters {
($obj: ident, $f: ident, $input: ident) => { ($obj: ident, $f: ident, $input: ident) => {
match $f.phase { match $f.phase {
FilterPhase::Auth => { FilterPhase::Auth => {
let (_, auth) = parse_filter_auth($input).map_err(|e| e.to_string())?; let (_, auth) = parse_filter_auth($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_auth(&$f, &auth)) Some($obj.on_filter_auth(&$f, &auth))
} }
FilterPhase::Commit => Some($obj.on_filter_commit(&$f)), FilterPhase::Commit => Some($obj.on_filter_commit(&$f)),
FilterPhase::Connect => { FilterPhase::Connect => {
let (_, (rdns, fcrdns, src, dest)) = let (_, (rdns, fcrdns, src, dest)) =
parse_filter_connect($input).map_err(|e| e.to_string())?; parse_filter_connect($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_connect(&$f, &rdns, &fcrdns, &src, &dest)) Some($obj.on_filter_connect(&$f, &rdns, &fcrdns, &src, &dest))
} }
FilterPhase::Data => Some($obj.on_filter_data(&$f)), FilterPhase::Data => Some($obj.on_filter_data(&$f)),
FilterPhase::DataLine => { FilterPhase::DataLine => {
let (_, data_line) = parse_filter_data_line($input).map_err(|e| e.to_string())?; let (_, data_line) = parse_filter_data_line($input).map_err(|e| e.to_string())?;
$obj.on_filter_data_line(&$f, &data_line); $obj.on_filter_data_line(&$f, &data_line);
None None
} }
FilterPhase::Ehlo => { FilterPhase::Ehlo => {
let (_, identity) = parse_filter_ehlo($input).map_err(|e| e.to_string())?; let (_, identity) = parse_filter_ehlo($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_ehlo(&$f, &identity)) Some($obj.on_filter_ehlo(&$f, &identity))
} }
FilterPhase::Helo => { FilterPhase::Helo => {
let (_, identity) = parse_filter_helo($input).map_err(|e| e.to_string())?; let (_, identity) = parse_filter_helo($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_helo(&$f, &identity)) Some($obj.on_filter_helo(&$f, &identity))
} }
FilterPhase::MailFrom => { FilterPhase::MailFrom => {
let (_, address) = parse_filter_mail_from($input).map_err(|e| e.to_string())?; let (_, address) = parse_filter_mail_from($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_mail_from(&$f, &address)) Some($obj.on_filter_mail_from(&$f, &address))
} }
FilterPhase::RcptTo => { FilterPhase::RcptTo => {
let (_, address) = parse_filter_rcpt_to($input).map_err(|e| e.to_string())?; let (_, address) = parse_filter_rcpt_to($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_rcpt_to(&$f, &address)) Some($obj.on_filter_rcpt_to(&$f, &address))
} }
FilterPhase::StartTls => { FilterPhase::StartTls => {
let (_, tls_str) = parse_filter_starttls($input).map_err(|e| e.to_string())?; let (_, tls_str) = parse_filter_starttls($input).map_err(|e| e.to_string())?;
Some($obj.on_filter_starttls(&$f, &tls_str)) Some($obj.on_filter_starttls(&$f, &tls_str))
} }
} }
}; };
} }
pub(crate) fn line<T>(user_object: &mut T, input: &[u8]) -> Result<(), String> pub(crate) fn line<T>(user_object: &mut T, input: &[u8]) -> Result<(), String>
where where
T: Filter, T: Filter,
{ {
let (input, entry) = parse_entry(input).map_err(nom_err_to_string)?; let (input, entry) = parse_entry(input).map_err(nom_err_to_string)?;
match entry { match entry {
EntryOption::Report(r) => handle_reports!(user_object, r, input), EntryOption::Report(r) => handle_reports!(user_object, r, input),
EntryOption::Filter(f) => { EntryOption::Filter(f) => {
if let Some(answer) = handle_filters!(user_object, f, input) { if let Some(answer) = handle_filters!(user_object, f, input) {
println!( println!(
"filter-result|{}|{}|{}", "filter-result|{}|{}|{}",
f.session_id, f.session_id,
f.token, f.token,
answer.to_string() answer.to_string()
); );
}; };
} }
}; };
Ok(()) Ok(())
} }