Commit
+41 -12 +/-3 browse
1 | diff --git a/cmd/maitred-debug/src/main.rs b/cmd/maitred-debug/src/main.rs |
2 | index 97457be..03008f2 100644 |
3 | --- a/cmd/maitred-debug/src/main.rs |
4 | +++ b/cmd/maitred-debug/src/main.rs |
5 | @@ -6,15 +6,9 @@ use maitred::{ |
6 | }; |
7 | |
8 | async fn print_message(message: Message<'static>) -> Result<(), DeliveryError> { |
9 | - println!("New SMTP Message:"); |
10 | - println!("{:?}", message.headers()); |
11 | - println!("Subject: {:?}", message.subject()); |
12 | println!( |
13 | - "{}", |
14 | - message |
15 | - .body_text(0) |
16 | - .map(|text| String::from_utf8_lossy(text.as_bytes()).to_string()) |
17 | - .unwrap_or_default() |
18 | + "New SMTP Message:\n{}", |
19 | + String::from_utf8_lossy(message.raw_message()) |
20 | ); |
21 | Ok(()) |
22 | } |
23 | diff --git a/maitred/src/auth.rs b/maitred/src/auth.rs |
24 | index cea772e..7a0c25d 100644 |
25 | --- a/maitred/src/auth.rs |
26 | +++ b/maitred/src/auth.rs |
27 | @@ -134,7 +134,6 @@ impl TryFrom<&str> for AuthData { |
28 | } |
29 | raw_data[n].push(*ch); |
30 | } |
31 | - println!("N: {}", n); |
32 | if n == 0 { |
33 | return Err(AuthError::NotEnoughFields); |
34 | } |
35 | diff --git a/maitred/src/session.rs b/maitred/src/session.rs |
36 | index 00e2d53..f5fd268 100644 |
37 | --- a/maitred/src/session.rs |
38 | +++ b/maitred/src/session.rs |
39 | @@ -59,6 +59,24 @@ pub fn timeout(message: &str) -> Response<String> { |
40 | smtp_response!(421, 4, 4, 2, format!("Timeout exceeded: {}", message)) |
41 | } |
42 | |
43 | + /// Extract a host from HELO/EHLO per RFC5321 4.1.3 |
44 | + fn parse_host(host: &str) -> String { |
45 | + // confusingly the url library determines if an address is IPv6 by checking |
46 | + // for [ ] but SMTP uses "tags" to determine this. |
47 | + let n_periods = host |
48 | + .chars() |
49 | + .fold(0, |accm, c| if c == '.' { accm + 1 } else { accm }); |
50 | + if n_periods == 3 { |
51 | + host.trim_start_matches("[") |
52 | + .trim_end_matches("]") |
53 | + .to_string() |
54 | + } else if host.contains("IPv6:") { |
55 | + format!("[{}]", host.replace("IPv6:", "").trim()) |
56 | + } else { |
57 | + host.to_string() |
58 | + } |
59 | + } |
60 | + |
61 | /// Session level options that configure individual SMTP transactions |
62 | #[derive(Clone)] |
63 | pub struct SessionOptions { |
64 | @@ -283,7 +301,8 @@ impl Session { |
65 | match req { |
66 | Request::Ehlo { host } => { |
67 | self.hostname = Some( |
68 | - Host::parse(host).map_err(|e| smtp_response!(500, 0, 0, 0, e.to_string()))?, |
69 | + Host::parse(&parse_host(host)) |
70 | + .map_err(|e| smtp_response!(500, 0, 0, 0, e.to_string()))?, |
71 | ); |
72 | self.reset(); |
73 | self.initialized = Some(Mode::Extended); |
74 | @@ -310,7 +329,7 @@ impl Session { |
75 | } |
76 | Request::Helo { host } => { |
77 | self.hostname = Some( |
78 | - Host::parse(host).map_err(|e| smtp_response!(500, 0, 0, 0, e.to_string()))?, |
79 | + Host::parse(&parse_host(host)).map_err(|e| smtp_response!(500, 0, 0, 0, e.to_string()))?, |
80 | ); |
81 | self.reset(); |
82 | self.initialized = Some(Mode::Legacy); |
83 | @@ -380,7 +399,11 @@ impl Session { |
84 | AuthData::try_from(initial_response.as_str()).map_err(|e| e.into())?; |
85 | |
86 | auth_fn |
87 | - .authenticate(&auth_data.authcid(), &auth_data.authzid(), &auth_data.passwd()) |
88 | + .authenticate( |
89 | + &auth_data.authcid(), |
90 | + &auth_data.authzid(), |
91 | + &auth_data.passwd(), |
92 | + ) |
93 | .await |
94 | .map_err(|e| e.into())?; |
95 | |
96 | @@ -876,4 +899,17 @@ transport rather than the session. |
97 | .is_some_and(|subject| subject == "Hello World") |
98 | })); |
99 | } |
100 | + |
101 | + #[tokio::test] |
102 | + pub async fn test_domain_parsing() { |
103 | + let mut session = Session::default(); |
104 | + for host in ["127.0.0.1", "[127.0.0.1]", "example.org", "IPv6: ::1"] { |
105 | + session |
106 | + .process(&Request::Ehlo { |
107 | + host: host.to_string(), |
108 | + }) |
109 | + .await |
110 | + .unwrap(); |
111 | + } |
112 | + } |
113 | } |