when checking domain settings, check that dmarc & tls reporting addresses are present if there is a record

This commit is contained in:
Mechiel Lukkien
2023-11-10 20:25:06 +01:00
parent 61bae75228
commit 2073db194b
7 changed files with 82 additions and 26 deletions

View File

@ -36,8 +36,8 @@ func TestLookup(t *testing.T) {
}
}
test("basic.example", &Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:tlsrpt@basic.example"}}}, nil)
test("one.example", &Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:tlsrpt@basic.example"}}}, nil)
test("basic.example", &Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:tlsrpt@basic.example"}}}, nil)
test("one.example", &Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:tlsrpt@basic.example"}}}, nil)
test("multiple.example", nil, ErrMultipleRecords)
test("absent.example", nil, ErrNoRecord)
test("other.example", nil, ErrNoRecord)

View File

@ -21,19 +21,42 @@ type Record struct {
Version string // "TLSRPTv1", for "v=".
// Aggregate reporting URI, for "rua=". "rua=" can occur multiple times, each can
// be a list. Must be URL-encoded strings, with ",", "!" and ";" encoded.
RUAs [][]string
// be a list.
RUAs [][]RUA
// ../rfc/8460:383
Extensions []Extension
}
// RUA is a reporting address with scheme and special characters ",", "!" and
// ";" not encoded.
type RUA string
// String returns the RUA with special characters encoded, for inclusion in a
// TLSRPT record.
func (rua RUA) String() string {
s := string(rua)
s = strings.ReplaceAll(s, ",", "%2C")
s = strings.ReplaceAll(s, "!", "%21")
s = strings.ReplaceAll(s, ";", "%3B")
return s
}
// URI parses a RUA as URI, with either a mailto or https scheme.
func (rua RUA) URI() (*url.URL, error) {
return url.Parse(string(rua))
}
// String returns a string or use as a TLSRPT DNS TXT record.
func (r Record) String() string {
b := &strings.Builder{}
fmt.Fprint(b, "v="+r.Version)
for _, rua := range r.RUAs {
fmt.Fprint(b, "; rua="+strings.Join(rua, ","))
for _, ruas := range r.RUAs {
l := make([]string, len(ruas))
for i, rua := range ruas {
l[i] = rua.String()
}
fmt.Fprint(b, "; rua="+strings.Join(l, ","))
}
for _, p := range r.Extensions {
fmt.Fprint(b, "; "+p.Key+"="+p.Value)
@ -204,8 +227,8 @@ func (p *parser) wsp() {
}
// ../rfc/8460:358
func (p *parser) xruas() []string {
l := []string{p.xuri()}
func (p *parser) xruas() []RUA {
l := []RUA{p.xuri()}
p.wsp()
for p.take(",") {
p.wsp()
@ -216,7 +239,7 @@ func (p *parser) xruas() []string {
}
// ../rfc/8460:360
func (p *parser) xuri() string {
func (p *parser) xuri() RUA {
v := p.xtakefn1(func(b rune, i int) bool {
return b != ',' && b != '!' && b != ' ' && b != '\t' && b != ';'
})
@ -227,5 +250,5 @@ func (p *parser) xuri() string {
if u.Scheme == "" {
p.xerrorf("missing scheme in uri")
}
return v
return RUA(v)
}

View File

@ -25,10 +25,10 @@ func TestRecord(t *testing.T) {
}
}
good("v=TLSRPTv1; rua=mailto:tlsrpt@mox.example;", Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:tlsrpt@mox.example"}}})
good("v=TLSRPTv1; rua=mailto:tlsrpt@mox.example , \t\t https://mox.example/tlsrpt ", Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:tlsrpt@mox.example", "https://mox.example/tlsrpt"}}})
good("v=TLSRPTv1; rua=mailto:tlsrpt@mox.example; ext=yes", Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:tlsrpt@mox.example"}}, Extensions: []Extension{{"ext", "yes"}}})
good("v=TLSRPTv1 ; rua=mailto:x@x.example; rua=mailto:y@x.example", Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:x@x.example"}, {"mailto:y@x.example"}}})
good("v=TLSRPTv1; rua=mailto:tlsrpt@mox.example;", Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:tlsrpt@mox.example"}}})
good("v=TLSRPTv1; rua=mailto:tlsrpt@mox.example , \t\t https://mox.example/tlsrpt ", Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:tlsrpt@mox.example", "https://mox.example/tlsrpt"}}})
good("v=TLSRPTv1; rua=mailto:tlsrpt@mox.example; ext=yes", Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:tlsrpt@mox.example"}}, Extensions: []Extension{{"ext", "yes"}}})
good("v=TLSRPTv1 ; rua=mailto:x@x.example; rua=mailto:y@x.example", Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:x@x.example"}, {"mailto:y@x.example"}}})
bad("v=TLSRPTv0")
bad("v=TLSRPTv10")
@ -48,7 +48,7 @@ func TestRecord(t *testing.T) {
bad("v=TLSRPTv1; rua=http://bad/%") // bad URI
const want = `v=TLSRPTv1; rua=mailto:x@mox.example; more=a; ext=2`
record := Record{Version: "TLSRPTv1", RUAs: [][]string{{"mailto:x@mox.example"}}, Extensions: []Extension{{"more", "a"}, {"ext", "2"}}}
record := Record{Version: "TLSRPTv1", RUAs: [][]RUA{{"mailto:x@mox.example"}}, Extensions: []Extension{{"more", "a"}, {"ext", "2"}}}
got := record.String()
if got != want {
t.Fatalf("record string, got %q, want %q", got, want)