mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 17:04:39 +03:00
mox!
This commit is contained in:
118
spf/received.go
Normal file
118
spf/received.go
Normal file
@ -0,0 +1,118 @@
|
||||
package spf
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/mjl-/mox/dns"
|
||||
"github.com/mjl-/mox/message"
|
||||
)
|
||||
|
||||
// ../rfc/7208:2083
|
||||
|
||||
// Received represents a Received-SPF header with the SPF verify results, to be
|
||||
// prepended to a message.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// Received-SPF: pass (mybox.example.org: domain of
|
||||
// myname@example.com designates 192.0.2.1 as permitted sender)
|
||||
// receiver=mybox.example.org; client-ip=192.0.2.1;
|
||||
// envelope-from="myname@example.com"; helo=foo.example.com;
|
||||
type Received struct {
|
||||
Result Status
|
||||
Comment string // Additional free-form information about the verification result. Optional. Included in message header comment inside "()".
|
||||
ClientIP net.IP // IP address of remote SMTP client, "client-ip=".
|
||||
EnvelopeFrom string // Sender mailbox, typically SMTP MAIL FROM, but will be set to "postmaster" at SMTP EHLO if MAIL FROM is empty, "envelop-from=".
|
||||
Helo dns.IPDomain // IP or host name from EHLO or HELO command, "helo=".
|
||||
Problem string // Optional. "problem="
|
||||
Receiver string // Hostname of receiving mail server, "receiver=".
|
||||
Identity Identity // The identity that was checked, "mailfrom" or "helo", for "identity=".
|
||||
Mechanism string // Mechanism that caused the result, can be "default". Optional.
|
||||
}
|
||||
|
||||
// Identity that was verified.
|
||||
type Identity string
|
||||
|
||||
const (
|
||||
ReceivedMailFrom Identity = "mailfrom"
|
||||
ReceivedHELO Identity = "helo"
|
||||
)
|
||||
|
||||
func receivedValueEncode(s string) string {
|
||||
if s == "" {
|
||||
return quotedString("")
|
||||
}
|
||||
for i, c := range s {
|
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c > 0x7f {
|
||||
continue
|
||||
}
|
||||
// ../rfc/5322:679
|
||||
const atext = "!#$%&'*+-/=?^_`{|}~"
|
||||
if strings.IndexByte(atext, byte(c)) >= 0 {
|
||||
continue
|
||||
}
|
||||
if c != '.' || (i == 0 || i+1 == len(s)) {
|
||||
return quotedString(s)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ../rfc/5322:736
|
||||
func quotedString(s string) string {
|
||||
w := &strings.Builder{}
|
||||
w.WriteByte('"')
|
||||
for _, c := range s {
|
||||
if c > ' ' && c < 0x7f && c != '"' && c != '\\' || c > 0x7f || c == ' ' || c == '\t' {
|
||||
// We allow utf-8. This should only be needed when the destination address has an
|
||||
// utf8 localpart, in which case we are already doing smtputf8.
|
||||
// We also allow unescaped space and tab. This is FWS, and the name of ABNF
|
||||
// production "qcontent" implies the FWS is not part of the string, but escaping
|
||||
// space and tab leads to ugly strings. ../rfc/5322:743
|
||||
w.WriteRune(c)
|
||||
continue
|
||||
}
|
||||
switch c {
|
||||
case ' ', '\t', '"', '\\':
|
||||
w.WriteByte('\\')
|
||||
w.WriteRune(c)
|
||||
}
|
||||
}
|
||||
w.WriteByte('"')
|
||||
return w.String()
|
||||
}
|
||||
|
||||
// Header returns a Received-SPF header line including trailing crlf that can
|
||||
// be prepended to an incoming message.
|
||||
func (r Received) Header() string {
|
||||
// ../rfc/7208:2043
|
||||
w := &message.HeaderWriter{}
|
||||
w.Add("", "Received-SPF: "+string(r.Result))
|
||||
if r.Comment != "" {
|
||||
w.Add(" ", "("+r.Comment+")")
|
||||
}
|
||||
w.Addf(" ", "client-ip=%s;", receivedValueEncode(r.ClientIP.String()))
|
||||
w.Addf(" ", "envelope-from=%s;", receivedValueEncode(r.EnvelopeFrom))
|
||||
var helo string
|
||||
if len(r.Helo.IP) > 0 {
|
||||
helo = r.Helo.IP.String()
|
||||
} else {
|
||||
helo = r.Helo.Domain.ASCII
|
||||
}
|
||||
w.Addf(" ", "helo=%s;", receivedValueEncode(helo))
|
||||
if r.Problem != "" {
|
||||
s := r.Problem
|
||||
max := 77 - len("problem=; ")
|
||||
if len(s) > max {
|
||||
s = s[:max]
|
||||
}
|
||||
w.Addf(" ", "problem=%s;", receivedValueEncode(s))
|
||||
}
|
||||
if r.Mechanism != "" {
|
||||
w.Addf(" ", "mechanism=%s;", receivedValueEncode(r.Mechanism))
|
||||
}
|
||||
w.Addf(" ", "receiver=%s;", receivedValueEncode(r.Receiver))
|
||||
w.Addf(" ", "identity=%s", receivedValueEncode(string(r.Identity)))
|
||||
return w.String()
|
||||
}
|
Reference in New Issue
Block a user