Reverse zone suggestions: yours and public, dismiss, per-view switch; child zones take their records
Suggestions split into networks that are yours (private IPv4 /24, ULA /48, an IPv6 /48 this server has an address in), which Create all creates, and public addresses (/32 or /128) to create only if they are yours or dismiss. A view can turn suggestions off, which also keeps it out of choosing a reverse zone's view (database version 13). A zone created inside another in the same view takes the parent's records under it; a delegation and its glue stay, a linked parent keeps them with a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aa4b004a14
commit
b24c730b97
21 files changed
+543
-107
No files matched your search
@@ -157,6 +157,8 @@ func (x *expander) expandOne(req Request) ([]model.Op, error) {
|
||||
return x.zoneCreate(req)
|
||||
case OpZoneFillPTR:
|
||||
return x.zoneFillPTR(req)
|
||||
case OpReverseDismiss, OpReverseUndismiss:
|
||||
return x.reverseDismiss(req, req.Op == OpReverseDismiss)
|
||||
case OpZoneUpdate, OpZoneDelete:
|
||||
z := x.st.Zones[req.ID]
|
||||
if z == nil {
|
||||
@@ -403,6 +405,41 @@ func (x *expander) zoneCreate(req Request) ([]model.Op, error) {
|
||||
in.ViewID = &v.ID
|
||||
}
|
||||
}
|
||||
var moveOps []model.Op
|
||||
var moved []*model.Record
|
||||
if zt == model.ZonePrimary {
|
||||
// The records a parent zone in the view has under the new zone
|
||||
// move into it: named answers those names from the new zone, so
|
||||
// left behind they would never be served again.
|
||||
vid := int64(0)
|
||||
if in.ViewID != nil {
|
||||
vid = *in.ViewID
|
||||
}
|
||||
if parent := x.parentZone(name, vid); parent != nil {
|
||||
moved = x.recordsUnder(parent, name)
|
||||
switch {
|
||||
case len(moved) == 0:
|
||||
case parent.LinkGroup != 0:
|
||||
x.warnings = append(x.warnings, fmt.Sprintf("zone %s is linked, so its %d records under %s stay there (hidden by the new zone): move them by hand", zoneDesc(x.st, parent), len(moved), dnsname.Display(name)))
|
||||
moved = nil
|
||||
default:
|
||||
for _, r := range moved {
|
||||
ri := RecordInput{Name: model.Ptr(r.Name), Type: model.Ptr(r.Type), Data: model.Ptr(r.Data)}
|
||||
if r.TTL != nil {
|
||||
ri.TTL = model.Some(*r.TTL)
|
||||
}
|
||||
if r.Comment != "" {
|
||||
ri.Comment = model.Ptr(r.Comment)
|
||||
}
|
||||
if r.Disabled {
|
||||
ri.Disabled = model.Ptr(true)
|
||||
}
|
||||
in.Records = append(in.Records, ri)
|
||||
moveOps = append(moveOps, newOp(OpRecordDelete, r.ID, r.Version, nil, fmt.Sprintf("move %s %s %s from zone %s into the new zone", r.Name, r.Type, r.Data, zoneDesc(x.st, parent))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fill := in.Fill
|
||||
in.Fill = false
|
||||
if fill {
|
||||
@@ -416,7 +453,7 @@ func (x *expander) zoneCreate(req Request) ([]model.Op, error) {
|
||||
if in.ViewID != nil {
|
||||
vid = *in.ViewID
|
||||
}
|
||||
fills := PTRFills(x.st, vid, name, nil)
|
||||
fills := PTRFills(x.st, vid, name, moved)
|
||||
if len(fills) == 0 {
|
||||
x.warnings = append(x.warnings, fmt.Sprintf("no A or AAAA record of %s has an address in %s, so there was nothing to fill it with", viewOrServer(x.st, vid), dnsname.Display(name)))
|
||||
}
|
||||
@@ -642,7 +679,45 @@ func (x *expander) zoneCreate(req Request) ([]model.Op, error) {
|
||||
r.in.ZoneID = ids[0]
|
||||
ops = append(ops, newOp(OpRecordCreate, ids[1+i], 0, r.in, r.summary))
|
||||
}
|
||||
return ops, nil
|
||||
return append(ops, moveOps...), nil
|
||||
}
|
||||
|
||||
// parentZone returns the most specific primary zone in a view that
|
||||
// contains name (not name itself).
|
||||
func (x *expander) parentZone(name string, viewID int64) *model.Zone {
|
||||
var best *model.Zone
|
||||
for _, z := range x.st.ZonesInView(viewID) {
|
||||
if z.Type == model.ZonePrimary && dnsname.IsStrictSubdomain(name, z.Name) && (best == nil || len(z.Name) > len(best.Name)) {
|
||||
best = z
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
// recordsUnder returns a zone's records at or below name, except a
|
||||
// delegation's NS and DS records and its glue, which belong to the parent.
|
||||
func (x *expander) recordsUnder(z *model.Zone, name string) []*model.Record {
|
||||
all := x.st.ZoneRecords(z.ID)
|
||||
var ns []string
|
||||
for _, r := range all {
|
||||
if r.Name == name && r.Type == "NS" {
|
||||
ns = append(ns, strings.ToLower(r.Data))
|
||||
}
|
||||
}
|
||||
var out []*model.Record
|
||||
for _, r := range all {
|
||||
if !dnsname.IsSubdomain(r.Name, name) {
|
||||
continue
|
||||
}
|
||||
if r.Name == name && (r.Type == "NS" || r.Type == "DS") {
|
||||
continue
|
||||
}
|
||||
if (r.Type == "A" || r.Type == "AAAA") && slices.Contains(ns, strings.ToLower(r.Name)) {
|
||||
continue // glue
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func deref(s *string) string {
|
||||
|
||||
Reference in new issue
Block a user