Migrate report parsing code to library
Change-Id: I156290b8bf7fff314266f62980eb100af600198a
This commit is contained in:
105
dmarc.go
105
dmarc.go
@@ -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:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
98
dmarc/report.go
Normal file
98
dmarc/report.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
30
dmarcparser.go
Normal file
30
dmarcparser.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user