Fix "update PTR": add a missing PTR, also in linked views
Ticking update PTR alone did nothing: the web UI didn't send it, and the engine only acted when the name or address changed. Alone it now adds a missing PTR record (or enables it); for linked zones every linked view's reverse zone counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
921bdd6c95
commit
1cec6cc393
6 files changed
+141
-17
No files matched your search
+69
-13
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -642,11 +643,42 @@ func (x *expander) ptrCreate(z *model.Zone, owner string, p *rr.Parsed) ([]model
|
||||
if !ok {
|
||||
return nil, errf(KindBadRequest, "ptr only applies to A and AAAA records")
|
||||
}
|
||||
rz, ptrName := reverseZoneFor(x.st, z.ViewID, addr)
|
||||
if rz == nil {
|
||||
x.warnings = append(x.warnings, fmt.Sprintf("no reverse zone for %s in this view, so no PTR record was added", addr))
|
||||
return nil, nil
|
||||
var ops []model.Op
|
||||
found := false
|
||||
for _, vid := range ptrViews(x.st, z) {
|
||||
rz, ptrName := reverseZoneFor(x.st, vid, addr)
|
||||
if rz == nil {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
more, err := x.ptrCreateIn(rz, ptrName, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops = append(ops, more...)
|
||||
}
|
||||
if !found {
|
||||
x.warnings = append(x.warnings, fmt.Sprintf("no reverse zone for %s in this view, so no PTR record was added (create one: jarl zone create %s/%d)", addr, addr, addr.BitLen()))
|
||||
}
|
||||
return ops, nil
|
||||
}
|
||||
|
||||
// ptrViews are the views whose reverse zones get the PTR records of zone
|
||||
// z's address records: z's own, and, for a linked zone, the other views
|
||||
// of the link (the record is in all of them).
|
||||
func ptrViews(st *model.State, z *model.Zone) []int64 {
|
||||
out := []int64{z.ViewID}
|
||||
for _, o := range st.LinkedWith(z) {
|
||||
if !slices.Contains(out, o.ViewID) {
|
||||
out = append(out, o.ViewID)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ptrCreateIn returns operations creating the PTR record ptrName -> owner
|
||||
// in reverse zone rz (none when it is there; enabling it when disabled).
|
||||
func (x *expander) ptrCreateIn(rz *model.Zone, ptrName, owner string) ([]model.Op, error) {
|
||||
for _, r := range x.st.ZoneRecords(rz.ID) {
|
||||
if r.Name == ptrName && r.Type == "PTR" {
|
||||
if strings.EqualFold(r.Data, owner) {
|
||||
@@ -674,10 +706,18 @@ func (x *expander) ptrSetDisabled(z *model.Zone, owner string, p *rr.Parsed, dis
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
rz, ptrName := reverseZoneFor(x.st, z.ViewID, addr)
|
||||
if rz == nil {
|
||||
return nil
|
||||
var ops []model.Op
|
||||
for _, vid := range ptrViews(x.st, z) {
|
||||
rz, ptrName := reverseZoneFor(x.st, vid, addr)
|
||||
if rz == nil {
|
||||
continue
|
||||
}
|
||||
ops = append(ops, x.ptrSetDisabledIn(rz, ptrName, owner, disabled)...)
|
||||
}
|
||||
return ops
|
||||
}
|
||||
|
||||
func (x *expander) ptrSetDisabledIn(rz *model.Zone, ptrName, owner string, disabled bool) []model.Op {
|
||||
var ops []model.Op
|
||||
for _, r := range x.st.ZoneRecords(rz.ID) {
|
||||
if r.Name == ptrName && r.Type == "PTR" && strings.EqualFold(r.Data, owner) && r.Disabled != disabled {
|
||||
@@ -700,12 +740,16 @@ func (x *expander) ptrDelete(z *model.Zone, owner string, p *rr.Parsed) []model.
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
rz, ptrName := reverseZoneFor(x.st, z.ViewID, addr)
|
||||
if rz == nil {
|
||||
return nil
|
||||
var ops []model.Op
|
||||
for _, vid := range ptrViews(x.st, z) {
|
||||
rz, ptrName := reverseZoneFor(x.st, vid, addr)
|
||||
if rz == nil {
|
||||
continue
|
||||
}
|
||||
in := RecordInput{ZoneID: rz.ID, Name: model.Ptr(ptrName), Type: model.Ptr("PTR"), Data: model.Ptr(owner), MissingOK: true}
|
||||
ops = append(ops, newOp(OpRecordDelete, 0, 0, in, fmt.Sprintf("delete %s IN PTR %s (if present)", ptrName, owner)))
|
||||
}
|
||||
in := RecordInput{ZoneID: rz.ID, Name: model.Ptr(ptrName), Type: model.Ptr("PTR"), Data: model.Ptr(owner), MissingOK: true}
|
||||
return []model.Op{newOp(OpRecordDelete, 0, 0, in, fmt.Sprintf("delete %s IN PTR %s (if present)", ptrName, owner))}
|
||||
return ops
|
||||
}
|
||||
|
||||
// parseWarnings keeps what parsing noticed (e.g. a name read as absolute).
|
||||
@@ -885,7 +929,10 @@ func (x *expander) recordUpdate(req Request) ([]model.Op, error) {
|
||||
if len(pre) > 0 {
|
||||
base = 0 // the record was just updated by pre
|
||||
}
|
||||
ops := append(pre, newOp(OpRecordUpdate, r.ID, base, in, summary))
|
||||
ops := pre
|
||||
if string(mustJSON(in)) != "{}" || !ptr {
|
||||
ops = append(ops, newOp(OpRecordUpdate, r.ID, base, in, summary))
|
||||
}
|
||||
disabled := r.Disabled
|
||||
if in.Disabled != nil {
|
||||
disabled = *in.Disabled
|
||||
@@ -909,6 +956,15 @@ func (x *expander) recordUpdate(req Request) ([]model.Op, error) {
|
||||
}
|
||||
ops = append(ops, x.ptrSetDisabled(z, owner, p, disabled)...)
|
||||
}
|
||||
if ptr && !moved && !toggled && !disabled {
|
||||
// Nothing else changes: make sure the PTR record is there (the
|
||||
// reverse zone may be newer than the record).
|
||||
more, err := x.ptrCreate(z, owner, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops = append(ops, more...)
|
||||
}
|
||||
return ops, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user