Skip to content

Commit 744ac2d

Browse files
committed
dmarc: fix handling if multiple records
Fixes emersion#72
1 parent addeefe commit 744ac2d

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

dmarc/lookup.go

+44-3
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,54 @@ func LookupWithOptions(domain string, options *LookupOptions) (*Record, error) {
5151
}
5252
return nil, errors.New("dmarc: failed to lookup TXT record: " + err.Error())
5353
}
54+
55+
// net.LookupTXT will concatenate strings contained in a single TXT record.
56+
// In other words, net.LookupTXT returns one entry per TXT record, even if
57+
// a record contains multiple strings.
5458
if len(txts) == 0 {
5559
return nil, ErrNoPolicy
5660
}
5761

58-
// Long keys are split in multiple parts
59-
txt := strings.Join(txts, "")
60-
return Parse(txt)
62+
// RFC 6376:
63+
// Records that do not start with a "v=" tag that identifies the
64+
// current version of DMARC are discarded.
65+
dmarcRecords := make([]string, 0, len(txts))
66+
for _, record := range txts {
67+
if IsDmarcRecord(record) {
68+
dmarcRecords = append(dmarcRecords, record)
69+
}
70+
}
71+
72+
if len(dmarcRecords) != 1 {
73+
return nil, ErrNoPolicy
74+
}
75+
76+
return Parse(dmarcRecords[0])
77+
}
78+
79+
func IsDmarcRecord(txt string) bool {
80+
// RFC 6376:
81+
// DMARC records follow the extensible "tag-value" syntax for DNS-based
82+
// key records defined in DKIM.
83+
firstTagSpec, _, _ := strings.Cut(txt, ";")
84+
85+
tagName, tagValue, found := strings.Cut(firstTagSpec, "=")
86+
if !found {
87+
return false
88+
}
89+
90+
// RFC 6376:
91+
// Note that WSP is allowed anywhere around tags. In particular, any
92+
// WSP after the "=" and any WSP before the terminating ";" is not part
93+
// of the value; however, WSP inside the value is significant.
94+
if strings.TrimSpace(tagName) != "v" {
95+
return false
96+
}
97+
if strings.TrimSpace(tagValue) != "DMARC1" {
98+
return false
99+
}
100+
101+
return true
61102
}
62103

63104
func Parse(txt string) (*Record, error) {

0 commit comments

Comments
 (0)