Mark signed zones with a padlock, and name clients in statistics
The zones list shows a padlock after signed zones, linking to their DNSSEC tab. Client lists in the statistics show the host name Jarl's records have for each address (PTR, else A/AAAA); no DNS queries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4113d5d6fc
commit
403f85486d
14 files changed
+216
-15
No files matched your search
@@ -2,10 +2,14 @@ package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"jarl/internal/auth"
|
||||
"jarl/internal/dnsname"
|
||||
"jarl/internal/model"
|
||||
"jarl/internal/stats"
|
||||
)
|
||||
|
||||
@@ -53,6 +57,9 @@ func (s *Server) statsSummary(w http.ResponseWriter, r *http.Request, p *auth.Pr
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.nameClients(r, p, sum.Tops[stats.KindClient]); err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, http.StatusOK, sum)
|
||||
return nil
|
||||
}
|
||||
@@ -75,6 +82,11 @@ func (s *Server) statsTop(w http.ResponseWriter, r *http.Request, p *auth.Princi
|
||||
if err != nil {
|
||||
return badRequest("%v", err)
|
||||
}
|
||||
if kind == stats.KindClient || kind == stats.KindRefused {
|
||||
if err := s.nameClients(r, p, es); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
writeJSON(w, http.StatusOK, es)
|
||||
return nil
|
||||
}
|
||||
@@ -145,3 +157,86 @@ func (s *Server) statsBreakdown(w http.ResponseWriter, r *http.Request, p *auth.
|
||||
writeJSON(w, http.StatusOK, es)
|
||||
return nil
|
||||
}
|
||||
|
||||
// nameClients gives client entries the host name Jarl's records have for
|
||||
// the address: the PTR record's target, or else the name of an A/AAAA
|
||||
// record with the address. It sends no DNS queries, and uses only records
|
||||
// the user may read. Anonymized clients (prefixes) get no name.
|
||||
func (s *Server) nameClients(r *http.Request, p *auth.Principal, es []stats.Entry) error {
|
||||
if len(es) == 0 {
|
||||
return nil
|
||||
}
|
||||
want := map[netip.Addr]bool{}
|
||||
for _, e := range es {
|
||||
if a, err := netip.ParseAddr(e.Key); err == nil {
|
||||
want[a.Unmap()] = true
|
||||
}
|
||||
}
|
||||
if len(want) == 0 {
|
||||
return nil
|
||||
}
|
||||
st, err := s.state(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
names := clientNames(st, want, func(rec *model.Record) bool { return canReadRecord(p, st, rec) })
|
||||
for i, e := range es {
|
||||
if a, err := netip.ParseAddr(e.Key); err == nil {
|
||||
es[i].Name = names[a.Unmap()]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// clientNames finds names for the addresses in want (see nameClients).
|
||||
// Where several records match, the first in name order wins.
|
||||
func clientNames(st *model.State, want map[netip.Addr]bool, readable func(*model.Record) bool) map[netip.Addr]string {
|
||||
ptr, host := map[netip.Addr]string{}, map[netip.Addr]string{}
|
||||
var recs []*model.Record
|
||||
for _, rec := range st.Records {
|
||||
if !rec.Disabled && (rec.Type == "PTR" || rec.Type == "A" || rec.Type == "AAAA") {
|
||||
recs = append(recs, rec)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(recs, func(a, b *model.Record) int {
|
||||
if a.Name != b.Name {
|
||||
if model.CanonicalLess(a.Name, b.Name) {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
return strings.Compare(a.Data, b.Data)
|
||||
})
|
||||
for _, rec := range recs {
|
||||
var a netip.Addr
|
||||
var name string
|
||||
if rec.Type == "PTR" {
|
||||
var ok bool
|
||||
if a, ok = dnsname.AddrFromReverse(rec.Name); !ok {
|
||||
continue
|
||||
}
|
||||
name = rec.Data
|
||||
} else {
|
||||
var err error
|
||||
if a, err = netip.ParseAddr(rec.Data); err != nil {
|
||||
continue
|
||||
}
|
||||
name = rec.Name
|
||||
}
|
||||
a = a.Unmap()
|
||||
m := host
|
||||
if rec.Type == "PTR" {
|
||||
m = ptr
|
||||
}
|
||||
if !want[a] || m[a] != "" || !readable(rec) {
|
||||
continue
|
||||
}
|
||||
m[a] = dnsname.Display(name)
|
||||
}
|
||||
for a, n := range host {
|
||||
if ptr[a] == "" {
|
||||
ptr[a] = n
|
||||
}
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
Reference in new issue
Block a user