Skip to content
Snippets Groups Projects
Select Git revision
  • 39e8811a19c50065616fd8162faa28d3f34e4811
  • noblogs default
  • noblogs-5.7.1
  • upstream
  • noblogs-5.7
  • noblogs-5.6new
  • upstream5.5.1
  • noblogs28dic
  • upstream28dic
  • noblogs-5.5.1
  • noblogs-5.4.2
  • noblogs-5.4_seconda
  • noblogs-5.4
  • noblogs-7c
  • wp5.2.3p3
  • mergedbconf
  • noblogs-5.7.1
  • noblogs.5.7.0p1
  • noblogs-5.7.0
  • noblogs-5.6p3
  • noblogs5.6p2
  • noblogs-5.6p1
  • noblogs-5.6
  • noblogs-5.4.2p1
  • noblogs-5.4.2
  • noblogs-5.4.1
  • noblogs-5.4
  • noblogs-p5.4
  • noblogs-5.3.2p2
  • noblogs-5.3.2p1
  • noblogs-5.3.2
  • noblogs-5.3
  • noblogs-5.2.3p4
  • noblogs-5.2.3p3
  • noblogs-5.2.3p2
  • noblogs-5.2.3p1
36 results

jquery.cookie.min.js

Blame
  • dmarc.go 4.74 KiB
    package reportscollector
    
    import (
    	"archive/zip"
    	"bytes"
    	"compress/gzip"
    	"encoding/xml"
    	"io"
    	"io/ioutil"
    	"net/http"
    	"strings"
    	"time"
    
    	"github.com/jhillyerd/enmime"
    )
    
    type dmarcRecord struct {
    	Row struct {
    		SourceIP        string `xml:"source_ip"`
    		Count           int    `xml:"count"`
    		PolicyEvaluated struct {
    			Disposition string `xml:"disposition"`
    			DKIM        string `xml:"dkim"`
    			SPF         string `xml:"spf"`
    		} `xml:"policy_evaluated"`
    	}
    	Identifiers struct {
    		EnvelopeFrom string `xml:"envelope_from"`
    		HeaderFrom   string `xml:"header_from"`
    	} `xml:"identifiers"`
    	AuthResults struct {
    		DKIM struct {
    			Domain string `xml:"domain"`
    			Result string `xml:"result"`
    		} `xml:"dkim"`
    		SPF struct {
    			Domain string `xml:"domain"`
    			Result string `xml:"result"`
    		} `xml:"spf"`
    	} `xml:"auth_results"`
    }
    
    func (r *dmarcRecord) isFailure() bool {
    	return (r.AuthResults.DKIM.Result == "fail" ||
    		r.AuthResults.SPF.Result == "fail")
    }
    
    type dmarcPolicy struct {
    	Domain string  `xml:"domain"`
    	ADKIM  string  `xml:"adkim"`
    	ASPF   string  `xml:"aspf"`
    	P      string  `xml:"p"`
    	SP     string  `xml:"sp"`
    	PCT    float64 `xml:"pct"`
    	FO     float64 `xml:"fo"`
    }
    
    type dmarcReport struct {
    	Name xml.Name `xml:"feedback"`
    
    	Version  string `xml:"version"`
    	Metadata struct {
    		ReportID     string `xml:"report_id"`
    		Organization string `xml:"org_name"`
    		Email        string `xml:"email"`
    		DateRange    struct {
    			StartSecs int64 `xml:"begin"`
    			EndSecs   int64 `xml:"end"`
    		} `xml:"date_range"`
    	} `xml:"report_metadata"`
    
    	Policy  *dmarcPolicy   `xml:"policy_published"`
    	Records []*dmarcRecord `xml:"record"`
    }
    
    type DMARCHandler struct{}
    
    func (h *DMARCHandler) Name() string { return "dmarc" }
    
    func (h *DMARCHandler) parseDMARC(r io.Reader) ([]Event, error) {
    	var report dmarcReport
    	if err := xml.NewDecoder(r).Decode(&report); err != nil {
    		return nil, err
    	}
    
    	var events []Event
    	for _, rec := range report.Records {
    		if rec.isFailure() {
    			events = append(events, h.eventFromRecord(&report, rec))
    		}
    	}
    	return events, nil
    }
    
    func (h *DMARCHandler) Parse(contentType string, req *http.Request) ([]Event, error) {
    	var r io.Reader
    	switch contentType {
    	case "text/xml":
    		r = req.Body
    	case "application/gzip":
    		var err error
    		r, err = gzip.NewReader(req.Body)
    		if err != nil {
    			return nil, err
    		}
    	default:
    		return nil, ErrNoMatch
    	}
    
    	return h.parseDMARC(r)
    }
    
    func (h *DMARCHandler) ParseMIME(msg *enmime.Part) ([]Event, error) {
    	// Try plain text/xml first.
    	if part := msg.DepthMatchFirst(func(p *enmime.Part) bool {
    		return p.ContentType == "text/xml"
    	}); part != nil {
    		return h.parseDMARC(bytes.NewReader(part.Content))
    	}
    
    	// Try to decode a gzipped attachment (also detected based on
    	// the filename).
    	if part := msg.DepthMatchFirst(func(p *enmime.Part) bool {
    		return (p.ContentType == "application/gzip" ||
    			strings.HasSuffix(p.FileName, ".xml.gz"))
    	}); part != nil {
    		gz, err := gzip.NewReader(bytes.NewReader(part.Content))
    		if err != nil {
    			return nil, err
    		}
    		return h.parseDMARC(gz)
    	}
    
    	// Try to decode a ZIP attachment.
    	if part := msg.DepthMatchFirst(func(p *enmime.Part) bool {
    		return (p.ContentType == "application/zip" ||
    			strings.HasSuffix(p.FileName, ".zip"))
    	}); part != nil {
    		data, ok := extractXMLFromZip(part.Content)
    		if ok {
    			return h.parseDMARC(bytes.NewReader(data))
    		}
    	}
    
    	return nil, ErrNoMatch
    }
    
    func (h *DMARCHandler) eventFromRecord(report *dmarcReport, rec *dmarcRecord) Event {
    	e := make(Event)
    	e.Set("type", "dmarc")
    	e.Set("event_timestamp", time.Unix(report.Metadata.DateRange.EndSecs, 0))
    	e.Set("domain", report.Policy.Domain)
    	e.Set("report_id", report.Metadata.ReportID)
    	e.Set("report_organization", report.Metadata.Organization)
    
    	if asn, ok := lookupASN(rec.Row.SourceIP); ok {
    		e.Set("asn", asn)
    	}
    	e.Set("policy_evaluated_disposition", rec.Row.PolicyEvaluated.Disposition)
    	e.Set("policy_evaluated_dkim", rec.Row.PolicyEvaluated.DKIM)
    	e.Set("policy_evaluated_spf", rec.Row.PolicyEvaluated.SPF)
    	e.Set("dmarc_envelope_from", rec.Identifiers.EnvelopeFrom)
    	e.Set("dmarc_header_from", rec.Identifiers.HeaderFrom)
    	e.Set("dmarc_dkim", rec.AuthResults.DKIM.Result)
    	e.Set("dmarc_spf", rec.AuthResults.SPF.Result)
    	e.Set("failed_session_count", rec.Row.Count)
    
    	return e
    }
    
    // Extract the first file with a .xml extension from a ZIP archive.
    func extractXMLFromZip(zipData []byte) ([]byte, bool) {
    	zr, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))
    	if err != nil {
    		return nil, false
    	}
    	for _, file := range zr.File {
    		if strings.HasSuffix(file.Name, ".xml") {
    			f, err := file.Open()
    			if err != nil {
    				return nil, false
    			}
    			defer f.Close()
    			data, err := ioutil.ReadAll(f)
    			if err != nil {
    				return nil, false
    			}
    			return data, true
    		}
    	}
    	return nil, false
    }