From 70806137da78a236efb43b4599023ddb8a0e4cd3 Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Sat, 22 Jul 2023 14:20:50 +0200 Subject: [PATCH] for submission over IPv6, allow missing "IPv6" tag in ip address (unless in pedantic mode) an EHLO ipv4 address looks like this: "[1.2.3.4]". for ipv6, the syntax is: "[IPv6:abcd::1]". mail user agents aren't as careful in compliance as smtp servers. for incoming messages from smtp servers, we want to be strict (we're eager to find a reason not to accept spam messages, and not adhering to the standards is usually a strong spam signal), but there is no reason to punish authenticated users. for the syntax requirements, see ABNF rule "address-literal" in rfc 5321. for issue #48 by @bobobo1618, thanks! --- smtpserver/parse.go | 20 +++++++++++++++----- smtpserver/server.go | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/smtpserver/parse.go b/smtpserver/parse.go index d4cfdc3..932ecee 100644 --- a/smtpserver/parse.go +++ b/smtpserver/parse.go @@ -270,7 +270,7 @@ func (p *parser) xsubdomain() string { func (p *parser) xmailbox() smtp.Path { localpart := p.xlocalpart() p.xtake("@") - return smtp.Path{Localpart: localpart, IPDomain: p.xipdomain()} + return smtp.Path{Localpart: localpart, IPDomain: p.xipdomain(false)} } // ../rfc/5321:2307 @@ -281,7 +281,7 @@ func (p *parser) xldhstr() string { } // parse address-literal or domain. -func (p *parser) xipdomain() dns.IPDomain { +func (p *parser) xipdomain(isehlo bool) dns.IPDomain { // ../rfc/5321:2309 // ../rfc/5321:2397 if p.take("[") { @@ -304,10 +304,20 @@ func (p *parser) xipdomain() dns.IPDomain { p.xerrorf("invalid ip in address: %q", ipaddr) } isv4 := ip.To4() != nil + isAllowedSloppyIPv6Submission := func() bool { + // Mail user agents that submit are relatively likely to use IPs in EHLO and forget + // that an IPv6 address needs to be tagged as such. We can forgive them. For + // SMTP servers we are strict. + return isehlo && p.conn.submission && !moxvar.Pedantic && ip.To16() != nil + } if ipv6 && isv4 { - p.xerrorf("ip is not ipv6") - } else if !ipv6 && !isv4 { - p.xerrorf("ip is not ipv4") + p.xerrorf("ip address is not ipv6") + } else if !ipv6 && !isv4 && !isAllowedSloppyIPv6Submission() { + if ip.To16() != nil { + p.xerrorf("ip address is ipv6, must use syntax [IPv6:...]") + } else { + p.xerrorf("ip address is not ipv4") + } } return dns.IPDomain{IP: ip} } diff --git a/smtpserver/server.go b/smtpserver/server.go index b29b307..6ca0a84 100644 --- a/smtpserver/server.go +++ b/smtpserver/server.go @@ -772,7 +772,7 @@ func (c *conn) cmdHello(p *parser, ehlo bool) { p.xspace() var remote dns.IPDomain if ehlo { - remote = p.xipdomain() + remote = p.xipdomain(true) } else { remote = dns.IPDomain{Domain: p.xdomain()} if !c.submission {