AXFRDDNS: Chunk large changes in multiple packets (#3732)

Co-authored-by: Mynacol <Mynacol@users.noreply.github.com>
Co-authored-by: Tom Limoncelli <6293917+tlimoncelli@users.noreply.github.com>
This commit is contained in:
Mynacol 2025-08-28 18:27:18 +02:00 committed by GitHub
parent 89ac03faca
commit c858e8fa57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -366,8 +366,8 @@ func (c *axfrddnsProvider) GetZoneRecords(domain string, meta map[string]string)
} }
// BuildCorrection return a Correction for a given set of DDNS update and the corresponding message. // BuildCorrection return a Correction for a given set of DDNS update and the corresponding message.
func (c *axfrddnsProvider) BuildCorrection(dc *models.DomainConfig, msgs []string, update *dns.Msg) *models.Correction { func (c *axfrddnsProvider) BuildCorrection(dc *models.DomainConfig, msgs []string, updates []*dns.Msg) *models.Correction {
if update == nil { if updates == nil {
return &models.Correction{ return &models.Correction{
Msg: fmt.Sprintf("DDNS UPDATES to '%s' (primary master: '%s'). Changes:\n%s", dc.Name, c.master, strings.Join(msgs, "\n")), Msg: fmt.Sprintf("DDNS UPDATES to '%s' (primary master: '%s'). Changes:\n%s", dc.Name, c.master, strings.Join(msgs, "\n")),
} }
@ -375,6 +375,7 @@ func (c *axfrddnsProvider) BuildCorrection(dc *models.DomainConfig, msgs []strin
return &models.Correction{ return &models.Correction{
Msg: fmt.Sprintf("DDNS UPDATES to '%s' (primary master: '%s'). Changes:\n%s", dc.Name, c.master, strings.Join(msgs, "\n")), Msg: fmt.Sprintf("DDNS UPDATES to '%s' (primary master: '%s'). Changes:\n%s", dc.Name, c.master, strings.Join(msgs, "\n")),
F: func() error { F: func() error {
for _, update := range updates {
update.Compress = true update.Compress = true
client := new(dns.Client) client := new(dns.Client)
client.Net = c.updateMode client.Net = c.updateMode
@ -396,6 +397,7 @@ func (c *axfrddnsProvider) BuildCorrection(dc *models.DomainConfig, msgs []strin
dns.RcodeToString[msg.MsgHdr.Rcode], dns.RcodeToString[msg.MsgHdr.Rcode],
msg.MsgHdr.Rcode) msg.MsgHdr.Rcode)
} }
}
return nil return nil
}, },
@ -453,8 +455,7 @@ func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, fo
var msgs []string var msgs []string
var reports []string var reports []string
update := new(dns.Msg) updates := []*dns.Msg{}
update.SetUpdate(dc.Name + ".")
dummyNs1, err := dns.NewRR(dc.Name + ". IN NS dnscontrol.invalid.") dummyNs1, err := dns.NewRR(dc.Name + ". IN NS dnscontrol.invalid.")
if err != nil { if err != nil {
@ -473,6 +474,9 @@ func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, fo
return nil, 0, nil return nil, 0, nil
} }
update := new(dns.Msg)
update.SetUpdate(dc.Name + ".")
// A DNS server should silently ignore a DDNS update that removes // A DNS server should silently ignore a DDNS update that removes
// the last NS record of a zone. Since modifying a record is // the last NS record of a zone. Since modifying a record is
// implemented by successively a deletion of the old record and an // implemented by successively a deletion of the old record and an
@ -491,6 +495,9 @@ func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, fo
update.Insert([]dns.RR{dummyNs1}) update.Insert([]dns.RR{dummyNs1})
} }
i := 1
appendFinalUpdate := true
for _, change := range changes { for _, change := range changes {
switch change.Type { switch change.Type {
case diff2.DELETE: case diff2.DELETE:
@ -523,16 +530,35 @@ func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, fo
case diff2.REPORT: case diff2.REPORT:
reports = append(reports, change.Msgs...) reports = append(reports, change.Msgs...)
} }
// Chunk packets that exceed 2^14 = 16 KiB.
// A single DNS RR can theoretically reach 64 KiB, the total packet limit.
// This is a compromise, succeeding whenever RRs are not bigger than about 64 KiB - 16 KiB = 48 KiB.
if update.Len() >= 2<<13 {
updates = append(updates, update)
update = new(dns.Msg)
update.SetUpdate(dc.Name + ".")
appendFinalUpdate = false
i = 1
} else {
appendFinalUpdate = true
i++
}
} }
if hasNSDeletion { if hasNSDeletion {
update.Remove([]dns.RR{dummyNs2}) update.Remove([]dns.RR{dummyNs2})
appendFinalUpdate = true
}
if appendFinalUpdate {
updates = append(updates, update)
} }
returnValue := []*models.Correction{} returnValue := []*models.Correction{}
if len(msgs) > 0 { if len(msgs) > 0 {
returnValue = append(returnValue, c.BuildCorrection(dc, msgs, update)) returnValue = append(returnValue, c.BuildCorrection(dc, msgs, updates))
} }
if len(reports) > 0 { if len(reports) > 0 {
returnValue = append(returnValue, c.BuildCorrection(dc, reports, nil)) returnValue = append(returnValue, c.BuildCorrection(dc, reports, nil))