59 lines
1.8 KiB
Go
59 lines
1.8 KiB
Go
package mailstore
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
// spamKeywords is a tiny, obvious-spam subject keyword list — a coarse signal only.
|
|
var spamKeywords = []string{"viagra", "casino", "lottery winner", "click here now", "wire transfer urgent", "nigerian prince"}
|
|
|
|
// SpamScore is a lightweight built-in heuristic — always runs, regardless of whether
|
|
// rspamd (rspamd.go) is also enabled; both are additive, not either/or. Higher is more
|
|
// suspicious; compare against [Mailstore] spam_reject_score.
|
|
// ponytail: naive keyword/weight heuristic, not a real Bayesian/ML scorer — upgrade or
|
|
// lean harder on rspamd if false-positive rate matters.
|
|
func SpamScore(peerIP string, headers map[string]string, dkimPass, spfPass bool) int {
|
|
score := 0
|
|
if !spfPass {
|
|
score += 2
|
|
}
|
|
if !dkimPass {
|
|
score++
|
|
}
|
|
if CheckDNSBL(peerIP) {
|
|
score += 5
|
|
}
|
|
subject := strings.ToLower(headers["subject"])
|
|
for _, kw := range spamKeywords {
|
|
if strings.Contains(subject, kw) {
|
|
score++
|
|
}
|
|
}
|
|
return score
|
|
}
|
|
|
|
// CheckDNSBL looks up peerIP against the Spamhaus ZEN DNSBL. Per RFC 5782, a listing
|
|
// response is always an A record in 127.0.0.0/8 — checking for that range (rather than
|
|
// "any resolution succeeded") avoids false positives from a resolver that hijacks
|
|
// NXDOMAIN into a search/ad page instead of returning an error.
|
|
func CheckDNSBL(peerIP string) bool {
|
|
ip := net.ParseIP(peerIP)
|
|
if ip == nil || ip.To4() == nil {
|
|
return false
|
|
}
|
|
octets := strings.Split(ip.To4().String(), ".")
|
|
reversed := octets[3] + "." + octets[2] + "." + octets[1] + "." + octets[0]
|
|
addrs, err := net.DefaultResolver.LookupHost(context.Background(), reversed+".zen.spamhaus.org")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
for _, a := range addrs {
|
|
if resolved := net.ParseIP(a); resolved != nil && resolved.To4() != nil && resolved.To4()[0] == 127 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|