Reverse zones made easy: suggested, filled, in the right view, PTRs kept in step
Missing reverse zones are suggested for the private views' A and AAAA records (/24 for private IPv4, /32 for a public address, /48 for IPv6) and created filled with PTR records (jarl zone suggest-reverse, the Zones page). zone create --fill and zone fill-ptr add the PTRs a reverse zone lacks; a reverse zone created without a view goes to the private view using its addresses. Per zone, auto_ptr keeps the PTR records of A and AAAA records in step on every change (database version 12). Zone commands take a network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a3b054cc86
commit
aa4b004a14
27 files changed
+1173
-38
No files matched your search
@@ -0,0 +1,39 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"jarl/internal/auth"
|
||||
"jarl/internal/engine"
|
||||
)
|
||||
|
||||
// zoneFillPTR: POST /zones/{zone}/fill-ptr adds the PTR records a reverse
|
||||
// zone lacks for its view's A and AAAA records (docs/reverse.md).
|
||||
func (s *Server) zoneFillPTR(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
st, err := s.state(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
z, err := s.zoneByPath(r, st, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.mutate(w, r, p, "", []engine.Request{engine.NewRequest(engine.OpZoneFillPTR, z.ID, engine.LinkInput{ZoneID: z.ID})}, nil)
|
||||
}
|
||||
|
||||
// reverseSuggestions: GET /reverse-zones/suggestions lists the reverse
|
||||
// zones the private views lack for their records' addresses.
|
||||
func (s *Server) reverseSuggestions(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
st, err := s.state(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := []engine.ReverseSuggestion{}
|
||||
for _, sg := range engine.ReverseSuggestions(st, s.eng.Exposures(st)) {
|
||||
if p.CanSeeZone(sg.Zone+".", sg.View) {
|
||||
out = append(out, sg)
|
||||
}
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
return nil
|
||||
}
|
||||
@@ -29,6 +29,12 @@ func (s *Server) presentZone(st *model.State, z *model.Zone, counts map[int64]re
|
||||
}
|
||||
}
|
||||
}
|
||||
if pf, ok := dnsname.PrefixFromReverse(z.Name); ok {
|
||||
out.Prefix = pf.String()
|
||||
if z.Type == model.ZonePrimary {
|
||||
out.PTRMissing = len(engine.PTRFills(st, z.ViewID, z.Name, st.ZoneRecords(z.ID)))
|
||||
}
|
||||
}
|
||||
for _, o := range st.LinkedWith(z) {
|
||||
out.LinkedWith = append(out.LinkedWith, st.ViewName(o))
|
||||
out.Links = append(out.Links, api.ZoneLink{ID: o.ID, View: st.ViewName(o)})
|
||||
|
||||
@@ -114,6 +114,8 @@ func (s *Server) routes() {
|
||||
m.HandleFunc("GET "+p+"/zones/{zone}/compare", s.authed(s.zoneCompare))
|
||||
m.HandleFunc("POST "+p+"/zones/{zone}/link", s.authed(s.zoneLink(true)))
|
||||
m.HandleFunc("POST "+p+"/zones/{zone}/unlink", s.authed(s.zoneLink(false)))
|
||||
m.HandleFunc("POST "+p+"/zones/{zone}/fill-ptr", s.authed(s.zoneFillPTR))
|
||||
m.HandleFunc("GET "+p+"/reverse-zones/suggestions", s.authed(s.reverseSuggestions))
|
||||
m.HandleFunc("POST "+p+"/zones/{zone}/import", s.authed(s.importZoneFile))
|
||||
m.HandleFunc("GET "+p+"/zones/{zone}/records", s.authed(s.listRecords))
|
||||
m.HandleFunc("POST "+p+"/zones/{zone}/records", s.authed(s.createRecord))
|
||||
|
||||
Reference in new issue
Block a user