diff --git a/dmarc.go b/dmarc.go deleted file mode 100644 index 66f74c5..0000000 --- a/dmarc.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "encoding/xml" - "fmt" - "os" -) - -func main() { - - type DateRange struct { - // TODO: should be int but Y! trailing spaces - Begin string `xml:"begin"` - End string `xml:"end"` - } - - type ReportMetadata struct { - OrgName string `xml:"org_name"` - Email string `xml:"email"` - ExtraContact string `xml:"extra_contact_info"` - ReportId string `xml:"report_id"` - DateRange DateRange `xml:"date_range"` - } - - type PolicyPublished struct { - Domain string `xml:"domain"` - Adkim string `xml:"adkim"` - Aspf string `xml:"aspf"` - Policy string `xml:"p"` - SubdomainPolicy string `xml:"sp"` - Percentage int `xml:"pct"` - } - - type PolicyEvaluated struct { - Disposition string `xml:"disposition"` - Dkim string `xml:"dkim"` - Spf string `xml:"spf"` - } - - type Row struct { - // TODO: Figure out how to cast this to an IP - SourceIp string `xml:"source_ip"` - Count int `xml:"count"` - PolicyEvaluated PolicyEvaluated `xml:"policy_evaluated"` - } - - type Identifiers struct { - HeaderFrom string `xml:"header_from"` - } - - type AuthResult struct { - // FIXME: this could be either DKIM or SPF - XMLName xml.Name - Domain string `xml:"domain"` - Result string `xml:"result"` - } - - type AuthResults struct { - AuthResult []AuthResult `xml:",any"` - } - - type Record struct { - Row Row `xml:"row"` - Identifiers Identifiers `xml:"identifiers"` - AuthResults AuthResults `xml:"auth_results"` - } - - type FeedbackReport struct { - XMLName xml.Name `xml:"feedback"` - ReportMetadata ReportMetadata `xml:"report_metadata"` - PolicyPublished PolicyPublished `xml:"policy_published"` - Record []Record `xml:"record"` - } - - xmlFile, err := os.Open("/Users/gcolburn/go/src/github.com/gc1code/dmarcparser/samples/yahoo.xml") // For read access. - if err != nil { - fmt.Printf("os error: %v", err) - return - } - - defer xmlFile.Close() - - decoder := xml.NewDecoder(xmlFile) - var inElement string - for { - t, _ := decoder.Token() - if t == nil { - break - } - switch se := t.(type) { - case xml.StartElement: - inElement = se.Name.Local - if inElement == "feedback" { - f := FeedbackReport{} - - xmlerr := decoder.DecodeElement(&f, &se) - if xmlerr != nil { - fmt.Printf("decode error: %v", xmlerr) - } - fmt.Printf("XMLName: %#v\n", f) - } - default: - } - } -} diff --git a/dmarc/report.go b/dmarc/report.go new file mode 100644 index 0000000..d69a875 --- /dev/null +++ b/dmarc/report.go @@ -0,0 +1,98 @@ +package dmarc + +import ( + "encoding/xml" + "io" +) + +type DateRange struct { + // TODO: should be int but Y! trailing spaces + Begin string `xml:"begin"` + End string `xml:"end"` +} + +type ReportMetadata struct { + OrgName string `xml:"org_name"` + Email string `xml:"email"` + ExtraContact string `xml:"extra_contact_info"` + ReportId string `xml:"report_id"` + DateRange DateRange `xml:"date_range"` +} + +type PolicyPublished struct { + Domain string `xml:"domain"` + Adkim string `xml:"adkim"` + Aspf string `xml:"aspf"` + Policy string `xml:"p"` + SubdomainPolicy string `xml:"sp"` + Percentage int `xml:"pct"` +} + +type PolicyEvaluated struct { + Disposition string `xml:"disposition"` + Dkim string `xml:"dkim"` + Spf string `xml:"spf"` +} + +type Row struct { + // TODO: Figure out how to cast this to an IP + SourceIp string `xml:"source_ip"` + Count int `xml:"count"` + PolicyEvaluated PolicyEvaluated `xml:"policy_evaluated"` +} + +type Identifiers struct { + HeaderFrom string `xml:"header_from"` +} + +type AuthResult struct { + // FIXME: this could be either DKIM or SPF + XMLName xml.Name + Domain string `xml:"domain"` + Result string `xml:"result"` +} + +type AuthResults struct { + AuthResult []AuthResult `xml:",any"` +} + +type Record struct { + Row Row `xml:"row"` + Identifiers Identifiers `xml:"identifiers"` + AuthResults AuthResults `xml:"auth_results"` +} + +type FeedbackReport struct { + XMLName xml.Name `xml:"feedback"` + ReportMetadata ReportMetadata `xml:"report_metadata"` + PolicyPublished PolicyPublished `xml:"policy_published"` + Record []Record `xml:"record"` +} + +func ParseReader(xmlFileReader io.Reader) FeedbackReport { + var f FeedbackReport + + decoder := xml.NewDecoder(xmlFileReader) + var inElement string + + for { + t, _ := decoder.Token() + if t == nil { + break + } + switch se := t.(type) { + case xml.StartElement: + inElement = se.Name.Local + if inElement == "feedback" { + decoder.DecodeElement(&f, &se) + // xmlerr := decoder.DecodeElement(&f, &se) + // if xmlerr != nil { + // fmt.Printf("decode error: %v\n", xmlerr) + // } + // fmt.Printf("XMLName: %#v\n", f) + } + default: + } + } + return f +} diff --git a/dmarcparser.go b/dmarcparser.go new file mode 100644 index 0000000..0b475a8 --- /dev/null +++ b/dmarcparser.go @@ -0,0 +1,30 @@ +package main + +import ( + "flag" + "fmt" + "github.com/gc1code/dmarcparser/dmarc" + "os" +) + +func main() { + + var filename = flag.String("filename", "none", "Path to DMARC Aggregate Report XML") + + flag.Parse() + + if *filename == "none" { + fmt.Printf("XML File required.\n") + return + } + + xmlFile, err := os.Open(*filename) // For read access. + if err != nil { + fmt.Printf("os error: %v\n", err) + return + } + defer xmlFile.Close() + + feedbackReport := dmarc.ParseReader(xmlFile) + fmt.Printf("XMLName: %#v\n", feedbackReport) +}