1// Package dmarcdb stores incoming DMARC aggrate reports and evaluations for outgoing aggregate reports.
2//
3// With DMARC, a domain can request reports with DMARC evaluation results to be
4// sent to a specified address. Mox parses such reports, stores them in its
5// database and makes them available through its admin web interface. Mox also
6// keeps track of the evaluations it does for incoming messages and sends reports
7// to mail servers that request reports.
8//
9// Only aggregate reports are stored and sent. Failure reports about individual
10// messages are not implemented.
11package dmarcdb
12
13import (
14 "context"
15 "fmt"
16 "os"
17 "path/filepath"
18 "time"
19
20 "github.com/mjl-/bstore"
21
22 "github.com/mjl-/mox/mlog"
23 "github.com/mjl-/mox/mox-"
24 "github.com/mjl-/mox/moxvar"
25)
26
27// Init opens the databases.
28//
29// The incoming reports and evaluations for outgoing reports are in separate
30// databases for simpler file-based handling of the databases.
31func Init() error {
32 if ReportsDB != nil || EvalDB != nil {
33 return fmt.Errorf("already initialized")
34 }
35
36 log := mlog.New("dmarcdb", nil)
37 var err error
38
39 ReportsDB, err = openReportsDB(mox.Shutdown, log)
40 if err != nil {
41 return fmt.Errorf("open reports db: %v", err)
42 }
43
44 EvalDB, err = openEvalDB(mox.Shutdown, log)
45 if err != nil {
46 return fmt.Errorf("open eval db: %v", err)
47 }
48
49 return nil
50}
51
52func Close() error {
53 if err := ReportsDB.Close(); err != nil {
54 return fmt.Errorf("closing reports db: %w", err)
55 }
56 ReportsDB = nil
57
58 if err := EvalDB.Close(); err != nil {
59 return fmt.Errorf("closing eval db: %w", err)
60 }
61 EvalDB = nil
62 return nil
63}
64
65func openReportsDB(ctx context.Context, log mlog.Log) (*bstore.DB, error) {
66 p := mox.DataDirPath("dmarcrpt.db")
67 os.MkdirAll(filepath.Dir(p), 0770)
68 opts := bstore.Options{Timeout: 5 * time.Second, Perm: 0660, RegisterLogger: moxvar.RegisterLogger(p, log.Logger)}
69 return bstore.Open(ctx, p, &opts, ReportsDBTypes...)
70}
71
72func openEvalDB(ctx context.Context, log mlog.Log) (*bstore.DB, error) {
73 p := mox.DataDirPath("dmarceval.db")
74 os.MkdirAll(filepath.Dir(p), 0770)
75 opts := bstore.Options{Timeout: 5 * time.Second, Perm: 0660, RegisterLogger: moxvar.RegisterLogger(p, log.Logger)}
76 return bstore.Open(ctx, p, &opts, EvalDBTypes...)
77}
78