8// Policy as used in DMARC DNS record for "p=" or "sp=".
14 PolicyEmpty Policy = "" // Only for the optional Record.SubdomainPolicy.
15 PolicyNone Policy = "none"
16 PolicyQuarantine Policy = "quarantine"
17 PolicyReject Policy = "reject"
20// URI is a destination address for reporting.
22 Address string // Should start with "mailto:".
23 MaxSize uint64 // Optional maximum message size, subject to Unit.
24 Unit string // "" (b), "k", "m", "g", "t" (case insensitive), unit size, where k is 2^10 etc.
27// String returns a string representation of the URI for inclusion in a DMARC
29func (u URI) String() string {
31 s = strings.ReplaceAll(s, ",", "%2C")
32 s = strings.ReplaceAll(s, "!", "%21")
34 s += fmt.Sprintf("!%d", u.MaxSize)
42// Align specifies the required alignment of a domain name.
46 AlignStrict Align = "s" // Strict requires an exact domain name match.
47 AlignRelaxed Align = "r" // Relaxed requires either an exact or subdomain name match.
50// Record is a DNS policy or reporting record.
54// v=DMARC1; p=reject; rua=mailto:postmaster@mox.example
56 Version string // "v=DMARC1", fixed.
57 Policy Policy // Required, for "p=".
58 SubdomainPolicy Policy // Like policy but for subdomains. Optional, for "sp=".
59 AggregateReportAddresses []URI // Optional, for "rua=". Destination addresses for aggregate reports.
60 FailureReportAddresses []URI // Optional, for "ruf=". Destination addresses for failure reports.
61 ADKIM Align // Alignment: "r" (default) for relaxed or "s" for simple. For "adkim=".
62 ASPF Align // Alignment: "r" (default) for relaxed or "s" for simple. For "aspf=".
63 AggregateReportingInterval int // In seconds, default 86400. For "ri="
64 FailureReportingOptions []string // "0" (default), "1", "d", "s". For "fo=".
65 ReportingFormat []string // "afrf" (default). For "rf=".
66 Percentage int // Between 0 and 100, default 100. For "pct=". Policy applies randomly to this percentage of messages.
69// DefaultRecord holds the defaults for a DMARC record.
70var DefaultRecord = Record{
74 AggregateReportingInterval: 86400,
75 FailureReportingOptions: []string{"0"},
76 ReportingFormat: []string{"afrf"},
80// String returns the DMARC record for use as DNS TXT record.
81func (r Record) String() string {
82 b := &strings.Builder{}
83 b.WriteString("v=" + r.Version)
86 write := func(do bool, tag, value string) {
88 fmt.Fprintf(b, ";%s=%s", tag, value)
92 write(r.Policy != "", "p", string(r.Policy))
93 write(r.SubdomainPolicy != "", "sp", string(r.SubdomainPolicy))
94 if len(r.AggregateReportAddresses) > 0 {
95 l := make([]string, len(r.AggregateReportAddresses))
96 for i, a := range r.AggregateReportAddresses {
99 s := strings.Join(l, ",")
100 write(true, "rua", s)
102 if len(r.FailureReportAddresses) > 0 {
103 l := make([]string, len(r.FailureReportAddresses))
104 for i, a := range r.FailureReportAddresses {
107 s := strings.Join(l, ",")
108 write(true, "ruf", s)
110 write(r.ADKIM != "" && r.ADKIM != "r", "adkim", string(r.ADKIM))
111 write(r.ASPF != "" && r.ASPF != "r", "aspf", string(r.ASPF))
112 write(r.AggregateReportingInterval != DefaultRecord.AggregateReportingInterval, "ri", fmt.Sprintf("%d", r.AggregateReportingInterval))
113 if len(r.FailureReportingOptions) > 1 || len(r.FailureReportingOptions) == 1 && r.FailureReportingOptions[0] != "0" {
114 write(true, "fo", strings.Join(r.FailureReportingOptions, ":"))
116 if len(r.ReportingFormat) > 1 || len(r.ReportingFormat) == 1 && !strings.EqualFold(r.ReportingFormat[0], "afrf") {
117 write(true, "rf", strings.Join(r.FailureReportingOptions, ":"))
119 write(r.Percentage != 100, "pct", fmt.Sprintf("%d", r.Percentage))