From e4006518fe66b7e1e11be84ab0469107ee104941 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 3 Jun 2022 15:39:57 +0200 Subject: [PATCH] Stop using the regex crate for parsing addresses (#776) --- Cargo.toml | 8 ++++---- src/address/types.rs | 49 +++++++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b6dfa6a..23eed10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ maintenance = { status = "actively-developed" } [dependencies] idna = "0.2" -once_cell = "1" +once_cell = { version = "1", optional = true } tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # feature # builder @@ -29,7 +29,6 @@ mime = { version = "0.3.4", optional = true } fastrand = { version = "1.4", optional = true } quoted_printable = { version = "0.4", optional = true } base64 = { version = "0.13", optional = true } -regex = { version = "1", default-features = false, features = ["std", "unicode-case"] } email-encoding = { git = "https://github.com/lettre/email-encoding.git", rev = "ac7ab8630b8d012ade63869c89b2855e27b3ce7a", optional = true } # file transport @@ -67,6 +66,7 @@ tokio1_rustls = { package = "tokio-rustls", version = "0.23", optional = true } sha2 = { version = "0.10", optional = true } rsa = { version = "0.6.0", optional = true } ed25519-dalek = { version = "1.0.1", optional = true } +regex = { version = "1", default-features = false, features = ["std"], optional = true } # email formats email_address = { version = "0.2.1", default-features = false } @@ -95,7 +95,7 @@ mime03 = ["mime"] file-transport = ["uuid"] file-transport-envelope = ["serde", "serde_json", "file-transport"] sendmail-transport = [] -smtp-transport = ["base64", "nom", "socket2"] +smtp-transport = ["base64", "nom", "socket2", "once_cell"] pool = ["futures-util"] @@ -109,7 +109,7 @@ tokio1 = ["tokio1_crate", "async-trait", "futures-io", "futures-util"] tokio1-native-tls = ["tokio1", "native-tls", "tokio1_native_tls_crate"] tokio1-rustls-tls = ["tokio1", "rustls-tls", "tokio1_rustls"] -dkim = ["base64", "sha2", "rsa", "ed25519-dalek"] +dkim = ["base64", "sha2", "rsa", "ed25519-dalek", "regex", "once_cell"] [package.metadata.docs.rs] all-features = true diff --git a/src/address/types.rs b/src/address/types.rs index bad5a78..6aba26e 100644 --- a/src/address/types.rs +++ b/src/address/types.rs @@ -10,8 +10,6 @@ use std::{ use email_address::EmailAddress; use idna::domain_to_ascii; -use once_cell::sync::Lazy; -use regex::Regex; /// Represents an email address with a user and a domain name. /// @@ -56,9 +54,6 @@ pub struct Address { at_start: usize, } -// literal form, ipv4 or ipv6 address (SMTP 4.1.3) -static LITERAL_RE: Lazy = Lazy::new(|| Regex::new(r"(?i)\[([A-f0-9:\.]+)\]\z").unwrap()); - impl Address { /// Creates a new email address from a user and domain. /// @@ -132,16 +127,19 @@ impl Address { } fn check_domain_ascii(domain: &str) -> Result<(), AddressError> { + // Domain if EmailAddress::is_valid_domain(domain) { return Ok(()); } - if let Some(caps) = LITERAL_RE.captures(domain) { - if let Some(cap) = caps.get(1) { - if cap.as_str().parse::().is_ok() { - return Ok(()); - } - } + // IP + let ip = domain + .strip_prefix('[') + .and_then(|ip| ip.strip_suffix(']')) + .unwrap_or(domain); + + if ip.parse::().is_ok() { + return Ok(()); } Err(AddressError::InvalidDomain) @@ -259,7 +257,7 @@ mod tests { use super::*; #[test] - fn parse_address() { + fn ascii_address() { let addr_str = "something@example.com"; let addr = Address::from_str(addr_str).unwrap(); let addr2 = Address::new("something", "example.com").unwrap(); @@ -268,7 +266,34 @@ mod tests { assert_eq!(addr.domain(), "example.com"); assert_eq!(addr2.user(), "something"); assert_eq!(addr2.domain(), "example.com"); + } + #[test] + fn ascii_address_ipv4() { + let addr_str = "something@1.1.1.1"; + let addr = Address::from_str(addr_str).unwrap(); + let addr2 = Address::new("something", "1.1.1.1").unwrap(); + assert_eq!(addr, addr2); + assert_eq!(addr.user(), "something"); + assert_eq!(addr.domain(), "1.1.1.1"); + assert_eq!(addr2.user(), "something"); + assert_eq!(addr2.domain(), "1.1.1.1"); + } + + #[test] + fn ascii_address_ipv6() { + let addr_str = "something@[2606:4700:4700::1111]"; + let addr = Address::from_str(addr_str).unwrap(); + let addr2 = Address::new("something", "[2606:4700:4700::1111]").unwrap(); + assert_eq!(addr, addr2); + assert_eq!(addr.user(), "something"); + assert_eq!(addr.domain(), "[2606:4700:4700::1111]"); + assert_eq!(addr2.user(), "something"); + assert_eq!(addr2.domain(), "[2606:4700:4700::1111]"); + } + + #[test] + fn check_parts() { assert!(Address::check_user("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").is_err()); assert!( Address::check_domain("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com").is_err()