Author:
Hash:
Timestamp:
+41 -12 +/-3 browse
Kevin Schoon [me@kevinschoon.com]
93848f7eea54d802dad625fb132ba81fb35093d5
Sun, 01 Sep 2024 21:06:58 +0000 (1.1 years ago)
| 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 | } |