Author: Kevin Schoon [me@kevinschoon.com]
Hash: 883093f791e6e7ac8857863ca317335e49d6dd32
Timestamp: Fri, 30 Aug 2024 20:48:41 +0000 (1 month ago)

+84 -12 +/-2 browse
add a few more error codes, add non-pipeline test
1diff --git a/maitred/src/server.rs b/maitred/src/server.rs
2index 2f98398..282dc8e 100644
3--- a/maitred/src/server.rs
4+++ b/maitred/src/server.rs
5 @@ -21,8 +21,8 @@ use tokio_util::codec::Framed;
6
7 use crate::error::Error;
8 use crate::session::Session;
9- use crate::transport::Command;
10- use crate::transport::Transport;
11+ use crate::smtp_response;
12+ use crate::transport::{Command, Transport};
13 use crate::worker::{Packet, Worker};
14 use crate::{Delivery, Milter, Response, SmtpResponse};
15
16 @@ -190,13 +190,46 @@ where
17 }
18 }
19 Ok(Some(Err(err))) => {
20- tracing::error!("Internal server error: {}", err);
21+ tracing::warn!("Client Error: {}", err);
22 let response = match err {
23 crate::transport::Error::PipelineNotEnabled => {
24 crate::smtp_response!(500, 0, 0, 0, "Pipelining is not enabled")
25 }
26 crate::transport::Error::Smtp(e) => {
27- crate::smtp_response!(500, 0, 0, 0, e.to_string())
28+ match e {
29+ smtp_proto::Error::NeedsMoreData { bytes_left: _ } => {
30+ // TODO
31+ smtp_response!(500, 0, 0, 0, e.to_string())
32+ }
33+ smtp_proto::Error::UnknownCommand => {
34+ smtp_response!(500, 5, 5, 1, "Invalid Command")
35+ }
36+ smtp_proto::Error::InvalidSenderAddress => {
37+ smtp_response!(501, 5, 1, 8, e.to_string())
38+ }
39+ smtp_proto::Error::InvalidRecipientAddress => {
40+ smtp_response!(501, 5, 1, 3, e.to_string())
41+ }
42+ smtp_proto::Error::SyntaxError { syntax: _ } => {
43+ smtp_response!(501, 5, 5, 2, e.to_string())
44+ }
45+ smtp_proto::Error::InvalidParameter { param: _ } => {
46+ // TODO
47+ smtp_response!(500, 0, 0, 0, e.to_string())
48+ }
49+ smtp_proto::Error::UnsupportedParameter { param: _ } => {
50+ // TODO
51+ smtp_response!(500, 0, 0, 0, e.to_string())
52+ }
53+ smtp_proto::Error::ResponseTooLong => {
54+ // TODO
55+ smtp_response!(500, 0, 0, 0, e.to_string())
56+ }
57+ smtp_proto::Error::InvalidResponse { code: _ } => {
58+ // TODO
59+ smtp_response!(500, 0, 0, 0, e.to_string())
60+ }
61+ }
62 }
63 // IO Errors considered fatal for the entire session
64 crate::transport::Error::Io(e) => return Err(Error::Io(e)),
65 @@ -274,7 +307,12 @@ where
66 let (socket, _) = listener.accept().await.unwrap();
67 let addr = socket.local_addr()?;
68 tracing::info!("Accepted connection on: {:?}", addr);
69- let framed = Framed::new(socket, Transport::default().pipelining(true));
70+ let pipelining = self
71+ .options
72+ .as_ref()
73+ .is_some_and(|opts| opts.capabilities & smtp_proto::EXT_PIPELINING != 0)
74+ || self.options.is_none();
75+ let framed = Framed::new(socket, Transport::default().pipelining(pipelining));
76
77 match self.process(framed, global_queue.clone()).await {
78 Ok(_) => {
79 @@ -291,7 +329,7 @@ where
80 #[cfg(test)]
81 mod test {
82
83- use crate::{Delivery, Milter};
84+ use crate::{Delivery, Milter, SessionOptions};
85
86 use super::*;
87
88 @@ -349,6 +387,40 @@ mod test {
89 todo!()
90 }
91 }
92+ #[tokio::test]
93+ async fn test_server() {
94+ let stream = FakeStream {
95+ buffer: vec![
96+ "HELO example.org\r\n".into(),
97+ "MAIL FROM: <fuu@bar.com>\r\n".into(),
98+ "RCPT TO: <baz@qux.com>\r\n".into(),
99+ "DATA\r\n".into(),
100+ "Subject: Hello World\r\n.\r\n".into(),
101+ "QUIT\r\n".into(),
102+ ],
103+ ..Default::default()
104+ };
105+ let server = Server::default()
106+ // turn off all extended capabilities
107+ .with_session_opts(SessionOptions::default().capabilities(0))
108+ .with_milter(Milter::new(|_: Message<'static>| {
109+ Box::pin(async move { Ok(Message::default().into_owned()) })
110+ }))
111+ .with_delivery(Delivery::new(|_: Message<'static>| {
112+ Box::pin(async move { Ok(()) })
113+ }));
114+ let framed = Framed::new(stream, Transport::default());
115+ let global_queue = Arc::new(Injector::<Packet>::new());
116+ server.process(framed, global_queue.clone()).await.unwrap();
117+ let packet = global_queue.steal().success().unwrap();
118+ assert!(packet
119+ .mail_from
120+ .as_ref()
121+ .is_some_and(|mail_from| mail_from.email() == "fuu@bar.com"));
122+ assert!(packet.rcpt_to.as_ref().is_some_and(|rcpts| rcpts
123+ .first()
124+ .is_some_and(|rcpt_to| rcpt_to.email() == "baz@qux.com")));
125+ }
126
127 #[tokio::test]
128 async fn test_server_pipelined() {
129 diff --git a/maitred/src/session.rs b/maitred/src/session.rs
130index c78c92c..5c1a081 100644
131--- a/maitred/src/session.rs
132+++ b/maitred/src/session.rs
133 @@ -195,9 +195,9 @@ impl Session {
134 if self.initialized.is_none() {
135 return Err(smtp_response!(
136 500,
137- 0,
138- 0,
139- 0,
140+ 5,
141+ 5,
142+ 1,
143 "It's polite to say EHLO first"
144 ));
145 }
146 @@ -558,9 +558,9 @@ mod test {
147 payload: None,
148 expected: Err(smtp_response!(
149 500,
150- 0,
151- 0,
152- 0,
153+ 5,
154+ 5,
155+ 1,
156 String::from("It's polite to say EHLO first")
157 )),
158 }];