1package http
2
3import (
4 "log/slog"
5 "net"
6 "net/http"
7 "strings"
8 "time"
9
10 "github.com/mjl-/mox/dns"
11 "github.com/mjl-/mox/mlog"
12 "github.com/mjl-/mox/mox-"
13 "github.com/mjl-/mox/mtasts"
14)
15
16func mtastsPolicyHandle(w http.ResponseWriter, r *http.Request) {
17 log := func() mlog.Log {
18 return pkglog.WithContext(r.Context())
19 }
20
21 host := strings.ToLower(r.Host)
22 if !strings.HasPrefix(host, "mta-sts.") {
23 http.NotFound(w, r)
24 return
25 }
26 host = strings.TrimPrefix(host, "mta-sts.")
27 nhost, _, err := net.SplitHostPort(host)
28 if err == nil {
29 // Only relevant for when host has a port.
30 host = nhost
31 }
32 domain, err := dns.ParseDomain(host)
33 if err != nil {
34 log().Errorx("mtasts policy request: bad domain", err, slog.String("host", host))
35 http.NotFound(w, r)
36 return
37 }
38
39 conf, _ := mox.Conf.Domain(domain)
40 sts := conf.MTASTS
41 if sts == nil {
42 http.NotFound(w, r)
43 return
44 }
45
46 var mxs []mtasts.MX
47 for _, s := range sts.MX {
48 var mx mtasts.MX
49 if strings.HasPrefix(s, "*.") {
50 mx.Wildcard = true
51 s = s[2:]
52 }
53 d, err := dns.ParseDomain(s)
54 if err != nil {
55 log().Errorx("bad domain in mtasts config", err, slog.String("domain", s))
56 http.Error(w, "500 - internal server error - invalid domain in configuration", http.StatusInternalServerError)
57 return
58 }
59 mx.Domain = d
60 mxs = append(mxs, mx)
61 }
62 if len(mxs) == 0 {
63 mxs = []mtasts.MX{{Domain: mox.Conf.Static.HostnameDomain}}
64 }
65
66 policy := mtasts.Policy{
67 Version: "STSv1",
68 Mode: sts.Mode,
69 MaxAgeSeconds: int(sts.MaxAge / time.Second),
70 MX: mxs,
71 }
72 w.Header().Set("Content-Type", "text/plain")
73 w.Header().Set("Cache-Control", "no-cache, max-age=0")
74 _, _ = w.Write([]byte(policy.String()))
75}
76