Skip to content

Commit 2275fd8

Browse files
author
link2xt
authored
Use address literal as default EHLO argument (#30)
This commit fixes formatting of IPv4 and IPv6 address literals. Both are enclosed in brackets and IPv6 is prefixed with `IPv6:`. Default trait is derived for ClientId. Default ClientId is `[127.0.0.1]` now, because it passes the most strict Postfix `smtpd_helo_restrictions` checks. Fallback domain ClientId is replaced with `localhost.localdomain`, because a domain must be an FQDN according to RFC 5321.
1 parent 87c24ac commit 2275fd8

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

src/smtp/commands.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ mod test {
335335
#[test]
336336
fn test_display() {
337337
let id = ClientId::Domain("localhost".to_string());
338+
let id_ipv4 = ClientId::Ipv4(std::net::Ipv4Addr::new(127, 0, 0, 1));
338339
let email = EmailAddress::new("[email protected]".to_string()).unwrap();
339340
let mail_parameter = MailParameter::Other {
340341
keyword: "TEST".to_string(),
@@ -345,6 +346,10 @@ mod test {
345346
value: Some("value".to_string()),
346347
};
347348
assert_eq!(format!("{}", EhloCommand::new(id)), "EHLO localhost\r\n");
349+
assert_eq!(
350+
format!("{}", EhloCommand::new(id_ipv4)),
351+
"EHLO [127.0.0.1]\r\n"
352+
);
348353
assert_eq!(
349354
format!("{}", MailCommand::new(Some(email.clone()), vec![])),
350355
"MAIL FROM:<[email protected]>\r\n"

src/smtp/extension.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ use std::fmt::{self, Display, Formatter};
1010
use std::net::{Ipv4Addr, Ipv6Addr};
1111
use std::result::Result;
1212

13-
/// Default client id
14-
const DEFAULT_DOMAIN_CLIENT_ID: &str = "localhost";
13+
/// Default client id.
14+
///
15+
/// It passes
16+
/// `smtpd_helo_restrictions = reject_non_fqdn_helo_hostname`
17+
/// Postfix check, but not `reject_unknown_helo_hostname`.
18+
const DEFAULT_DOMAIN_CLIENT_ID: &str = "localhost.localdomain";
1519

1620
/// Client identifier, the parameter to `EHLO`
1721
#[derive(PartialEq, Eq, Clone, Debug)]
@@ -28,12 +32,26 @@ pub enum ClientId {
2832
Ipv6(Ipv6Addr),
2933
}
3034

35+
impl Default for ClientId {
36+
fn default() -> Self {
37+
// The most compatible address.
38+
//
39+
// It passes Postfix checks
40+
// ```
41+
// smtpd_helo_restrictions = reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_helo_hostname
42+
// smtpd_helo_required = yes
43+
// smtpd_delay_reject = no
44+
// ```
45+
Self::Ipv4(Ipv4Addr::new(127, 0, 0, 1))
46+
}
47+
}
48+
3149
impl Display for ClientId {
3250
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
3351
match *self {
3452
ClientId::Domain(ref value) => f.write_str(value),
35-
ClientId::Ipv4(ref value) => write!(f, "{}", value),
36-
ClientId::Ipv6(ref value) => write!(f, "{}", value),
53+
ClientId::Ipv4(ref value) => write!(f, "[{}]", value),
54+
ClientId::Ipv6(ref value) => write!(f, "[IPv6:{}]", value),
3755
}
3856
}
3957
}
@@ -44,8 +62,8 @@ impl ClientId {
4462
ClientId::Domain(domain)
4563
}
4664

47-
/// Defines a `ClientId` with the current hostname, of `localhost` if hostname could not be
48-
/// found
65+
/// Defines a `ClientId` with the current hostname, or
66+
/// `localhost.localdomain` if hostname could not be found.
4967
pub fn hostname() -> ClientId {
5068
ClientId::Domain(get_hostname().unwrap_or_else(|| DEFAULT_DOMAIN_CLIENT_ID.to_string()))
5169
}

src/smtp/smtp_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl SmtpClient {
108108
smtp_utf8: false,
109109
credentials: None,
110110
connection_reuse: ConnectionReuseParameters::NoReuse,
111-
hello_name: ClientId::new("localhost".to_string()),
111+
hello_name: Default::default(),
112112
authentication_mechanism: None,
113113
force_set_auth: false,
114114
timeout: Some(Duration::new(60, 0)),

0 commit comments

Comments
 (0)