all repos — navani @ 6a3b4b68a2ff0917e49e2eeb42f8d6b99281fb1d

forlater's primary mail processing service

Implement SPF record checking
Anirudh Oppiliappan x@icyphox.sh
Sun, 19 Sep 2021 18:39:46 +0530
commit

6a3b4b68a2ff0917e49e2eeb42f8d6b99281fb1d

parent

57138aa32c9fa1554d3eb783367906b810b98f66

5 files changed, 87 insertions(+), 25 deletions(-)

jump to
M go.modgo.mod

@@ -3,6 +3,7 @@

go 1.16 require ( + blitiri.com.ar/go/spf v1.2.0 github.com/go-shiori/go-readability v0.0.0-20210627123243-82cc33435520 github.com/gomodule/redigo v1.8.5 github.com/joegrasse/mail v1.0.0
M go.sumgo.sum

@@ -1,3 +1,5 @@

+blitiri.com.ar/go/spf v1.2.0 h1:aPpeEVKz5Ue4xb4SEt4AzScCSyES7/pol6znzZGle3A= +blitiri.com.ar/go/spf v1.2.0/go.mod h1:HLmgHxdrsqbBgi5omEopdAKm18PypvUKJGkF/j7BO0w= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=

@@ -174,8 +176,9 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=

gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I= mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4=
A mail/check.go

@@ -0,0 +1,50 @@

+package mail + +import ( + "fmt" + "net" + "net/mail" + "strings" + + "blitiri.com.ar/go/spf" + "golang.org/x/net/idna" +) + +func VerifySPF(email string) (bool, error) { + e, err := mail.ParseAddress(email) + if err != nil { + return false, fmt.Errorf("parse address: %w", err) + } + domain := strings.Split(e.Address, "@")[1] + + domain, err = idna.ToASCII(domain) + if err != nil { + return false, fmt.Errorf("to ascii: %w\n", err) + } + + mxs, err := net.LookupMX(domain) + if err != nil { + return false, fmt.Errorf("mx lookup: %w\n", err) + } + + passes := []spf.Result{} + for _, mx := range mxs { + ips, err := net.LookupIP(mx.Host) + if err != nil { + return false, fmt.Errorf("ip lookup: %w\n", err) + } + + for _, ip := range ips { + result, _ := spf.CheckHostWithSender(ip, domain, e.Address) + if result == spf.Pass { + passes = append(passes, result) + } + } + } + + if len(passes) > 0 { + return true, nil + } else { + return false, nil + } +}
M mail/send.gomail/send.go

@@ -55,6 +55,5 @@ err = email.Send(SMTP_HOST + ":" + SMTP_PORT)

if err != nil { return err } - return nil }
M main.gomain.go

@@ -21,37 +21,46 @@ log.Printf("using body as is: %v\n", err)

body = m.Body } - urls := mail.ExtractURLs(body) - if len(urls) == 0 { - log.Printf("no urls found") + spf, err := mail.VerifySPF(m.From) + if err != nil { + log.Printf("verify spf: %v", err) } - for _, u := range distinct(urls) { - log.Printf("url: %s\n", u) - parsedURL, err := url.Parse(u) - if err != nil { - log.Printf("url parse: %v\n", err) - } - f, err := reader.Fetch(parsedURL.String()) - if err != nil { - log.Printf("reader fetch: %v\n", err) + if spf { + urls := mail.ExtractURLs(body) + if len(urls) == 0 { + log.Printf("no urls found") } + for _, u := range distinct(urls) { + log.Printf("url: %s\n", u) + parsedURL, err := url.Parse(u) + if err != nil { + log.Printf("url parse: %v\n", err) + } - article, err := reader.Readable(f, parsedURL) - if err == nil { - err = mail.SendArticle(&article, m.From, true) + f, err := reader.Fetch(parsedURL.String()) if err != nil { - log.Printf("error sending mail to: %s: %v\n", m.From, err) + log.Printf("reader fetch: %v\n", err) + } + + article, err := reader.Readable(f, parsedURL) + if err == nil { + err = mail.SendArticle(&article, m.From, true) + if err != nil { + log.Printf("error sending mail to: %s: %v\n", m.From, err) + } else { + log.Printf("sent mail to %s: %s\n", m.From, article.Title) + } } else { - log.Printf("sent mail to %s: %s\n", m.From, article.Title) - } - } else { - log.Printf("not readable: %s\n", err) - err := mail.SendArticle(&article, m.From, false) - if err != nil { - log.Printf("error sending mail to: %s: %v\n", m.From, err) + log.Printf("not readable: %s\n", err) + err := mail.SendArticle(&article, m.From, false) + if err != nil { + log.Printf("error sending mail to: %s: %v\n", m.From, err) + } } } + } else { + log.Printf("bad spf: %s", m.From) } w.WriteHeader(204) })