Every DNS query the name server checks send is now reported from exchangeHostPort as a dig command line with a short answer (addresses, referral, DS, NXDOMAIN) and its round-trip time; the serials check's SOA queries keep their own events with the server's result. The delegation check streams with ?stream=1 like /serials, and the Name servers tab shows its queries as they happen, keeps its result and follows a running check when you come back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d6c28ba69
commit
bb6b4ab64d
6 files changed
+334
-128
No files matched your search
@@ -60,30 +60,12 @@ func (s *Server) zoneSerials(w http.ResponseWriter, r *http.Request, p *auth.Pri
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||
defer cancel()
|
||||
// ?stream=1: newline-delimited JSON events while the servers are
|
||||
// asked (nscheck.Event, kind start/end), then {"kind":"done",
|
||||
// "response":...}. The web UI shows the queries as they go.
|
||||
ck := s.checker()
|
||||
var emit func(any)
|
||||
if queryBool(r, "stream") {
|
||||
emit = s.streamJSON(w)
|
||||
ck.Watch = func(e nscheck.Event) { emit(e) }
|
||||
}
|
||||
cmd := s.eng.LoadedSerialCommand(st, z)
|
||||
if emit != nil && cmd != "" {
|
||||
emit(nscheck.Event{Kind: "start", ID: 0, Query: cmd})
|
||||
}
|
||||
resp.Serial, resp.SerialSource, err = s.localSerial(ctx, st, z)
|
||||
emit := s.watchChecks(w, r, ck)
|
||||
resp.Serial, resp.SerialSource, err = s.watchedLocalSerial(ctx, emit, st, z)
|
||||
if err != nil {
|
||||
resp.Notes = append(resp.Notes, "named on this server didn't report the zone's serial: "+err.Error())
|
||||
}
|
||||
if emit != nil && cmd != "" {
|
||||
e := nscheck.Event{Kind: "end", ID: 0}
|
||||
if err != nil {
|
||||
e.Error = err.Error()
|
||||
}
|
||||
emit(e)
|
||||
}
|
||||
if z.Signed() && resp.SerialSource == "jarl" {
|
||||
// named gives the signed zone its own serial, ahead of Jarl's.
|
||||
resp.Notes = append(resp.Notes, "The zone is signed and named wasn't asked: other servers serve the signed zone's serial, which named sets and which is usually ahead of Jarl's.")
|
||||
@@ -112,6 +94,40 @@ func (s *Server) zoneSerials(w http.ResponseWriter, r *http.Request, p *auth.Pri
|
||||
return nil
|
||||
}
|
||||
|
||||
// watchChecks: with ?stream=1, /serials and /delegation answer with
|
||||
// newline-delimited JSON events while they ask the servers (nscheck.Event,
|
||||
// kind start/end, one pair per DNS query), then {"kind":"done",
|
||||
// "response":...}. The web UI shows the queries as they go. It returns
|
||||
// the function that writes an event, or nil without ?stream=1.
|
||||
func (s *Server) watchChecks(w http.ResponseWriter, r *http.Request, ck *nscheck.Checker) func(any) {
|
||||
if !queryBool(r, "stream") {
|
||||
return nil
|
||||
}
|
||||
emit := s.streamJSON(w)
|
||||
ck.Watch = func(e nscheck.Event) { emit(e) }
|
||||
return emit
|
||||
}
|
||||
|
||||
// watchedLocalSerial is localSerial, reported as event 0 when streaming.
|
||||
func (s *Server) watchedLocalSerial(ctx context.Context, emit func(any), st *model.State, z *model.Zone) (*uint32, string, error) {
|
||||
cmd := s.eng.LoadedSerialCommand(st, z)
|
||||
if emit != nil && cmd != "" {
|
||||
emit(nscheck.Event{Kind: "start", ID: 0, Query: cmd})
|
||||
}
|
||||
serial, src, err := s.localSerial(ctx, st, z)
|
||||
if emit != nil && cmd != "" {
|
||||
e := nscheck.Event{Kind: "end", ID: 0}
|
||||
switch {
|
||||
case err != nil:
|
||||
e.Error = err.Error()
|
||||
case src == "named" && serial != nil:
|
||||
e.Answer = fmt.Sprintf("serial %d", *serial)
|
||||
}
|
||||
emit(e)
|
||||
}
|
||||
return serial, src, err
|
||||
}
|
||||
|
||||
// streamJSON starts a newline-delimited JSON response and returns a
|
||||
// function that writes one value and flushes it; it is safe for
|
||||
// concurrent use.
|
||||
@@ -166,11 +182,16 @@ func (s *Server) zoneDelegation(w http.ResponseWriter, r *http.Request, p *auth.
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||||
defer cancel()
|
||||
ref, _, _ := s.localSerial(ctx, st, z)
|
||||
emit := s.watchChecks(w, r, c)
|
||||
ref, _, _ := s.watchedLocalSerial(ctx, emit, st, z)
|
||||
resp := api.DelegationResponse{DelegationReport: c.Delegation(ctx, z.Name, ns, addrs, ref, s.signing(ctx, st, z)), View: st.ViewName(z), Notes: []string{}, CheckedAt: time.Now()}
|
||||
if resp.View != "" {
|
||||
resp.Notes = append(resp.Notes, fmt.Sprintf("Compared with the zone in view %s. Resolvers on the internet get the view that matches their address; make sure that's this one.", resp.View))
|
||||
}
|
||||
if emit != nil {
|
||||
emit(map[string]any{"kind": "done", "response": resp})
|
||||
return nil
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user