forked from zaccone/spf
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtrace.go
116 lines (110 loc) · 4.08 KB
/
trace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package spf
import (
"fmt"
"net"
"strings"
)
// Trace holds data for "Received-SPF" header field
// https://tools.ietf.org/html/rfc7208#section-9.1
type Trace struct {
Result Result `json:"result"` // the result
Explanation string `json:"exp,omitempty"` // supporting information for the result
ClientIP net.IP `json:"clientIp,omitempty"` // the IP address of the SMTP client
Identity string `json:"identity,omitempty"` // the identity that was checked
Helo string `json:"helo,omitempty"` // the host name given in the HELO or EHLO command
EnvelopeFrom string `json:"envelopeFrom,omitempty"` // the envelope sender mailbox
Problem error `json:"problem,omitempty"` // if an error was returned, details about the error
Receiver string `json:"receiver,omitempty"` // the host name of the SPF verifier
Mechanism string `json:"mechanism,omitempty"` // the mechanism that matched
}
func (r *Trace) ReceivedSPF() string {
// TODO (dmotylev) Should resulting string be wrapped/trimmed? https://tools.ietf.org/html/rfc5322#section-2.1.1
if r == nil {
return ""
}
var b strings.Builder
writeExp := func(s string) {
b.WriteString(" (")
if s != "" {
b.WriteString(r.Explanation)
} else {
// https://tools.ietf.org/html/rfc7208#section-9.1
// Received-SPF: pass (mybox.example.org: domain of
// [email protected] designates 192.0.2.1 as permitted sender)
// receiver=mybox.example.org; client-ip=192.0.2.1;
// envelope-from="[email protected]"; helo=foo.example.com;
//
// Received-SPF: fail (mybox.example.org: domain of
// [email protected] does not designate
// 192.0.2.1 as permitted sender)
// identity=mailfrom; client-ip=192.0.2.1;
// envelope-from="[email protected]";
//
//
// Pass The SPF record designates the host to be allowed to send
// Fail The SPF record has designated the host as NOT being allowed to send
// SoftFail The SPF record has designated the host as NOT being allowed to send but is in transition
// Neutral The SPF record specifies explicitly that nothing can be said about validity
// None The domain does not have an SPF record or the SPF record does not evaluate to a result
// PermError A permanent error has occured (eg. badly formatted SPF record)
// TempError A transient error has occured
if r.Receiver != "" {
b.WriteString(r.Receiver)
b.WriteString(": ")
}
sender := "sender"
if r.EnvelopeFrom != "" {
sender = r.EnvelopeFrom
}
host := "the host"
if r.ClientIP != nil {
host = r.ClientIP.String()
}
switch r.Result {
case Pass:
fmt.Fprintf(&b, "domain of %s designates %s as permitted sender", sender, host)
case Fail:
fmt.Fprintf(&b, "domain of %s does not designate %s as permitted sender", sender, host)
case Softfail:
fmt.Fprintf(&b, "domain of %s does not designate %s as permitted sender but is in transition", sender, host)
case Neutral:
b.WriteString("nothing can be said about validity")
case None:
fmt.Fprintf(&b, "domain of %s does not have an SPF record or the SPF record does not evaluate to a result", sender)
case Permerror:
b.WriteString("a permanent error has occured")
case Temperror:
b.WriteString("a transient error has occured")
}
}
b.WriteByte(')')
}
writeKV := func(sep bool, k, v string) bool {
if v == "" {
return sep
}
if sep {
b.WriteByte(';')
}
b.WriteByte(' ')
b.WriteString(k)
b.WriteByte('=')
b.WriteString(v)
return true
}
b.WriteString(r.Result.String())
writeExp(r.Explanation)
var scol bool
if r.ClientIP != nil {
scol = writeKV(scol, "client-ip", r.ClientIP.String())
}
if r.Problem != nil {
scol = writeKV(scol, "problem", r.Problem.Error())
}
scol = writeKV(scol, "identity", r.Identity)
scol = writeKV(scol, "helo", r.Helo)
scol = writeKV(scol, "envelope-from", r.EnvelopeFrom)
scol = writeKV(scol, "receiver", r.Receiver)
writeKV(scol, "mechanism", r.Mechanism)
return b.String()
}