Skip to content

Instantly share code, notes, and snippets.

@yosida95
Created June 1, 2021 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yosida95/f72ea12d95dbd4f7c56e73a8ebda2873 to your computer and use it in GitHub Desktop.
Save yosida95/f72ea12d95dbd4f7c56e73a8ebda2873 to your computer and use it in GitHub Desktop.
DMARC report parser
package main
import (
"context"
"encoding/xml"
"flag"
"log"
"net"
"os"
"time"
)
var reportName string
func init() {
flag.StringVar(&reportName, "report", "", "")
}
type DmarcReport struct {
XMLName xml.Name `xml:"feedback"`
Metadata DmarcReportMetadata `xml:"report_metadata"`
PolicyPublished DmarcReportPolicyPublished `xml:"policy_published"`
Records []DmarcRecord `xml:"record"`
}
type DmarcReportMetadata struct {
OrgName string `xml:"org_name"`
Email string `xml:"email"`
ReportId string `xml:"report_id"`
DateRange struct {
Begin int64 `xml:"begin"`
End int64 `xml:"end"`
} `xml:"date_range"`
XMLName xml.Name `xml:"report_metadata"`
}
type DmarcReportPolicyPublished struct {
Domain string `xml:"domain"`
Adkim string `xml:"adkim"`
Aspf string `xml:"aspf"`
P string `xml:"p"`
SP string `xml:"sp"`
Pct int `xml:"pct"`
XMLName xml.Name `xml:"policy_published"`
}
type DmarcRecord struct {
Row DmarcRecordRow `xml:"row"`
Identifiers DmarcRecordIdentifiers `xml:"identifiers"`
AuthResults DmarcRecordAuthResult `xml:"auth_results"`
XMLName xml.Name `xml:"record"`
}
type DmarcRecordRow struct {
SourceIP string `xml:"source_ip"`
Count int64 `xml:"count"`
PolicyEvaluated struct {
Dispotision string `xml:"disposition"`
DKIM string `xml:"dkim"`
SPF string `xml:"spf"`
Reason struct {
Type string `xml:"type"`
Comment string `xml:"comment"`
} `xml:"reason"`
} `xml:"policy_evaluated"`
XMLName xml.Name `xml:"row"`
}
type DmarcRecordIdentifiers struct {
HeaderFrom string `xml:"header_from"`
XMLName xml.Name `xml:"identifiers"`
}
type DmarcRecordAuthResult struct {
DKIM struct {
Domain string `xml:"domain"`
Result string `xml:"result"`
Selector string `xml:"selector"`
} `xml:"dkim"`
SPF struct {
Domain string `xml:"domain"`
Result string `xml:"result"`
} `xml:"spf"`
XMLName xml.Name `xml:"auth_results"`
}
func main() {
flag.Parse()
fh, err := os.Open(reportName)
if err != nil {
log.Printf("Failed to open report: %v", err)
return
}
var report DmarcReport
err = xml.NewDecoder(fh).Decode(&report)
fh.Close()
if err != nil {
log.Printf("Failed to parse report: %v", err)
return
}
for _, record := range report.Records {
row := record.Row
eval := row.PolicyEvaluated
if eval.Dispotision == "none" {
continue
}
resolvctx, resolvcancel := context.WithTimeout(context.Background(), 2*time.Second)
ret, err := net.DefaultResolver.LookupAddr(resolvctx, row.SourceIP)
resolvcancel()
var reverse string
if err != nil || len(ret) < 1 {
reverse = row.SourceIP
} else {
reverse = ret[0]
}
log.Printf(
"From: %s [%s] For: %s %s:%d",
reverse, row.SourceIP, record.Identifiers.HeaderFrom, eval.Dispotision, row.Count)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment