mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 17:44:35 +03:00
mox!
This commit is contained in:
127
dmarc/txt.go
Normal file
127
dmarc/txt.go
Normal file
@ -0,0 +1,127 @@
|
||||
package dmarc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// todo: DMARCPolicy should be named just Policy, but this is causing conflicting types in sherpadoc output. should somehow get the dmarc-prefix only in the sherpadoc.
|
||||
|
||||
// Policy as used in DMARC DNS record for "p=" or "sp=".
|
||||
type DMARCPolicy string
|
||||
|
||||
// ../rfc/7489:1157
|
||||
|
||||
const (
|
||||
PolicyEmpty DMARCPolicy = "" // Only for the optional Record.SubdomainPolicy.
|
||||
PolicyNone DMARCPolicy = "none"
|
||||
PolicyQuarantine DMARCPolicy = "quarantine"
|
||||
PolicyReject DMARCPolicy = "reject"
|
||||
)
|
||||
|
||||
// URI is a destination address for reporting.
|
||||
type URI struct {
|
||||
Address string // Should start with "mailto:".
|
||||
MaxSize uint64 // Optional maximum message size, subject to Unit.
|
||||
Unit string // "" (b), "k", "g", "t" (case insensitive), unit size, where k is 2^10 etc.
|
||||
}
|
||||
|
||||
// String returns a string representation of the URI for inclusion in a DMARC
|
||||
// record.
|
||||
func (u URI) String() string {
|
||||
s := u.Address
|
||||
s = strings.ReplaceAll(s, ",", "%2C")
|
||||
s = strings.ReplaceAll(s, "!", "%21")
|
||||
if u.MaxSize > 0 {
|
||||
s += fmt.Sprintf("%d", u.MaxSize)
|
||||
}
|
||||
s += u.Unit
|
||||
return s
|
||||
}
|
||||
|
||||
// ../rfc/7489:1127
|
||||
|
||||
// Align specifies the required alignment of a domain name.
|
||||
type Align string
|
||||
|
||||
const (
|
||||
AlignStrict Align = "s" // Strict requires an exact domain name match.
|
||||
AlignRelaxed Align = "r" // Relaxed requires either an exact or subdomain name match.
|
||||
)
|
||||
|
||||
// Record is a DNS policy or reporting record.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// v=DMARC1; p=reject; rua=mailto:postmaster@mox.example
|
||||
type Record struct {
|
||||
Version string // "v=DMARC1"
|
||||
Policy DMARCPolicy // Required, for "p=".
|
||||
SubdomainPolicy DMARCPolicy // Like policy but for subdomains. Optional, for "sp=".
|
||||
AggregateReportAddresses []URI // Optional, for "rua=".
|
||||
FailureReportAddresses []URI // Optional, for "ruf="
|
||||
ADKIM Align // "r" (default) for relaxed or "s" for simple. For "adkim=".
|
||||
ASPF Align // "r" (default) for relaxed or "s" for simple. For "aspf=".
|
||||
AggregateReportingInterval int // Default 86400. For "ri="
|
||||
FailureReportingOptions []string // "0" (default), "1", "d", "s". For "fo=".
|
||||
ReportingFormat []string // "afrf" (default). Ffor "rf=".
|
||||
Percentage int // Between 0 and 100, default 100. For "pct=".
|
||||
}
|
||||
|
||||
// DefaultRecord holds the defaults for a DMARC record.
|
||||
var DefaultRecord = Record{
|
||||
Version: "DMARC1",
|
||||
ADKIM: "r",
|
||||
ASPF: "r",
|
||||
AggregateReportingInterval: 86400,
|
||||
FailureReportingOptions: []string{"0"},
|
||||
ReportingFormat: []string{"afrf"},
|
||||
Percentage: 100,
|
||||
}
|
||||
|
||||
// String returns the DMARC record for use as DNS TXT record.
|
||||
func (r Record) String() string {
|
||||
b := &strings.Builder{}
|
||||
b.WriteString("v=" + r.Version)
|
||||
|
||||
wrote := false
|
||||
write := func(do bool, tag, value string) {
|
||||
if do {
|
||||
fmt.Fprintf(b, ";%s=%s", tag, value)
|
||||
wrote = true
|
||||
}
|
||||
}
|
||||
write(r.Policy != "", "p", string(r.Policy))
|
||||
write(r.SubdomainPolicy != "", "sp", string(r.SubdomainPolicy))
|
||||
if len(r.AggregateReportAddresses) > 0 {
|
||||
l := make([]string, len(r.AggregateReportAddresses))
|
||||
for i, a := range r.AggregateReportAddresses {
|
||||
l[i] = a.String()
|
||||
}
|
||||
s := strings.Join(l, ",")
|
||||
write(true, "rua", s)
|
||||
}
|
||||
if len(r.FailureReportAddresses) > 0 {
|
||||
l := make([]string, len(r.FailureReportAddresses))
|
||||
for i, a := range r.FailureReportAddresses {
|
||||
l[i] = a.String()
|
||||
}
|
||||
s := strings.Join(l, ",")
|
||||
write(true, "ruf", s)
|
||||
}
|
||||
write(r.ADKIM != "", "adkim", string(r.ADKIM))
|
||||
write(r.ASPF != "", "aspf", string(r.ASPF))
|
||||
write(r.AggregateReportingInterval != DefaultRecord.AggregateReportingInterval, "ri", fmt.Sprintf("%d", r.AggregateReportingInterval))
|
||||
if len(r.FailureReportingOptions) > 1 || (len(r.FailureReportingOptions) == 1 && r.FailureReportingOptions[0] != "0") {
|
||||
write(true, "fo", strings.Join(r.FailureReportingOptions, ":"))
|
||||
}
|
||||
if len(r.ReportingFormat) > 1 || (len(r.ReportingFormat) == 1 && strings.EqualFold(r.ReportingFormat[0], "afrf")) {
|
||||
write(true, "rf", strings.Join(r.FailureReportingOptions, ":"))
|
||||
}
|
||||
write(r.Percentage != 100, "pct", fmt.Sprintf("%d", r.Percentage))
|
||||
|
||||
if !wrote {
|
||||
b.WriteString(";")
|
||||
}
|
||||
return b.String()
|
||||
}
|
Reference in New Issue
Block a user