jarld checks the reverse DNS of the public zones' MX and SPF addresses as the internet sees it, every 6 hours (Exposure page, jarl exposure mail, jarl_mail_reverse_dns_ok). The Server options page, jarl option generated and GET /options/generated list what Jarl writes into named.conf by itself; stats.rate_limit_log turns off the query-errors logging. Option lists of three or more elements are shown one per line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
2.1 KiB
Go
84 lines
2.1 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"jarl/internal/auth"
|
|
"jarl/internal/mailcheck"
|
|
)
|
|
|
|
// mailCheckEvery is how often jarld checks the reverse DNS of the
|
|
// addresses that send and receive mail (docs/exposure.md#mail).
|
|
const mailCheckEvery = 6 * time.Hour
|
|
|
|
// checkMail checks the reverse DNS of the public zones' mail addresses and
|
|
// keeps the report; it logs addresses that became wrong.
|
|
func (s *Server) checkMail(ctx context.Context) *mailcheck.Report {
|
|
st, err := s.store.LoadState(ctx)
|
|
if err != nil {
|
|
s.log.Warn("mail reverse DNS check: loading state", "err", err)
|
|
return nil
|
|
}
|
|
public := map[int64]bool{}
|
|
for _, x := range s.eng.Exposures(st) {
|
|
public[x.ViewID] = x.Public
|
|
}
|
|
if len(st.Views) == 0 {
|
|
public[0] = true
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
|
defer cancel()
|
|
rep := mailcheck.Check(ctx, st, func(id int64) bool { return public[id] }, mailcheck.Resolver{Servers: s.cfg.Resolvers})
|
|
s.mail.Lock()
|
|
prev := s.mail.r
|
|
s.mail.r = rep
|
|
s.mail.Unlock()
|
|
was := map[string]bool{}
|
|
if prev != nil {
|
|
for _, r := range prev.Results {
|
|
was[r.Zone+"|"+r.Address] = r.OK
|
|
}
|
|
}
|
|
for _, r := range rep.Results {
|
|
if ok, seen := was[r.Zone+"|"+r.Address]; !r.OK && (ok || !seen) {
|
|
s.log.Warn("mail reverse DNS", "zone", r.Zone, "address", r.Address, "problem", r.Message)
|
|
}
|
|
}
|
|
return rep
|
|
}
|
|
|
|
func (s *Server) mailReport() *mailcheck.Report {
|
|
s.mail.Lock()
|
|
defer s.mail.Unlock()
|
|
return s.mail.r
|
|
}
|
|
|
|
// mailCheckNow: POST /exposure/mail checks now and returns the report.
|
|
func (s *Server) mailCheckNow(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
|
if err := require(p, auth.Action{Resource: auth.ResOptions}); err != nil {
|
|
return err
|
|
}
|
|
rep := s.checkMail(r.Context())
|
|
if rep == nil {
|
|
rep = &mailcheck.Report{Checked: time.Now(), Results: []mailcheck.Result{}}
|
|
}
|
|
writeJSON(w, http.StatusOK, rep)
|
|
return nil
|
|
}
|
|
|
|
// mailLoop checks a minute after the start, then every mailCheckEvery.
|
|
func (s *Server) mailLoop(ctx context.Context) {
|
|
wait := time.Minute
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(wait):
|
|
s.checkMail(ctx)
|
|
wait = mailCheckEvery
|
|
}
|
|
}
|
|
}
|