cf redirects work, but wrong order

This commit is contained in:
Thomas Limoncelli 2025-11-26 21:25:25 -05:00
parent 76791cefc7
commit 98f4075bd7
No known key found for this signature in database
11 changed files with 203 additions and 111 deletions

View file

@ -225,3 +225,12 @@ func (dc *DomainConfig) GetPopulateCorrections(providerName string) []*Correctio
defer dc.pendingCorrectionsMutex.Unlock()
return dc.pendingPopulateCorrections[providerName]
}
func MakeFakeDomainConfig(domain string) *DomainConfig {
v := domaintags.MakeDomainNameVarieties(domain)
return &DomainConfig{
Name: v.NameIDN,
NameRaw: v.NameRaw,
NameUnicode: v.NameUnicode,
}
}

View file

@ -527,6 +527,15 @@ func (rc *RecordConfig) GetSVCBValue() []dns.SVCBKeyValue {
return nil
}
func (rc *RecordConfig) IsModernType() bool {
//fmt.Printf("DEBUG: IsModernType rtype=%s\n", rc.Type)
if rc.Type == "CLOUDFLAREAPI_SINGLE_REDIRECT" {
return true
}
return false
}
// Records is a list of *RecordConfig.
type Records []*RecordConfig
@ -593,6 +602,10 @@ func PostProcessRecords(recs []*RecordConfig) {
// Downcase converts all labels and targets to lowercase in a list of RecordConfig.
func Downcase(recs []*RecordConfig) {
for _, r := range recs {
if r.IsModernType() {
continue
}
r.Name = strings.ToLower(r.Name)
r.NameFQDN = strings.ToLower(r.NameFQDN)
switch r.Type { // #rtype_variations
@ -620,6 +633,11 @@ func CanonicalizeTargets(recs []*RecordConfig, origin string) {
originFQDN := origin + "."
for _, r := range recs {
if r.IsModernType() {
continue
}
switch r.Type { // #rtype_variations
case "ALIAS", "ANAME", "CNAME", "DNAME", "DS", "DNSKEY", "MX", "NS", "NAPTR", "PTR", "SRV":
// Target is a hostname that might be a shortname. Turn it into a FQDN.

View file

@ -245,7 +245,9 @@ func humanDiff(a, b targetConfig) string {
}
// Just the TTLs are different:
return fmt.Sprintf("%s ttl=(%d->%d)", a.comparableNoTTL, a.rec.TTL, b.rec.TTL)
return fmt.Sprintf("ttl=(%d->%d) %s",
a.rec.TTL, b.rec.TTL,
a.comparableNoTTL)
}
var echRe = regexp.MustCompile(`ech="?([\w+/=]+)"?`)

View file

@ -14,20 +14,11 @@ func ImportRawRecords(domains []*models.DomainConfig) error {
for _, dc := range domains {
for _, rawRec := range dc.RawRecords {
// Create as much of the RecordConfig as we can now. Allow New() to fill in the reset.
rec := &models.RecordConfig{
Type: rawRec.Type,
TTL: rawRec.TTL,
Metadata: stringifyMetas(rawRec.Metas),
//FilePos: models.FixPosition(rawRec.FilePos),
}
setRecordNames(rec, dc, rawRec.Args[0].(string))
// Fill in the .F/.Fields* fields.
err := Func[rawRec.Type].FromArgs(rec, rawRec.Args)
rec, err := NewRecordConfigFromRaw(rawRec.Type, rawRec.Args, dc)
if err != nil {
return err
return fmt.Errorf("%s: %w", nil, err)
// TODO(tlim): Fix FilePos
//return fmt.Errorf("%s: %w", rawRec.FilePos, err)
}
// Free memeory:
@ -42,6 +33,35 @@ func ImportRawRecords(domains []*models.DomainConfig) error {
return nil
}
func NewRecordConfigFromRaw(t string, args []any, dc *models.DomainConfig) (*models.RecordConfig, error) {
fmt.Printf("DEBUG: NewRecordConfigFromRaw t=%q args=%+v\n", t, args)
if _, ok := Func[t]; !ok {
return nil, fmt.Errorf("record type %q is not supported", t)
}
// Create as much of the RecordConfig as we can now. Allow New() to fill in the reset.
rec := &models.RecordConfig{
Type: t,
Name: args[0].(string), // May be fixed later.
Metadata: map[string]string{},
//FilePos: models.FixPosition(filePos),
}
setRecordNames(rec, dc, args[0].(string))
if rec.Type == "" {
panic("rtypecontrol: NewRecordConfigFromRaw: empty record type")
}
// Fill in the .F/.Fields* fields.
err := Func[t].FromArgs(dc, rec, args)
if err != nil {
return nil, err
}
return rec, nil
}
func stringifyMetas(metas []map[string]any) map[string]string {
result := make(map[string]string)
for _, m := range metas {
@ -60,13 +80,14 @@ func setRecordNames(rec *models.RecordConfig, dc *models.DomainConfig, n string)
if rec.SubDomain == "" {
// Not _EXTEND() mode:
if rec.Name == "@" {
rec.NameRaw = rec.Name
rec.Name = rec.Name
if n == "@" {
rec.Name = "@"
rec.NameRaw = "@"
rec.NameUnicode = "@"
} else {
rec.Name = domaintags.EfficientToASCII(n)
rec.NameRaw = n
rec.Name = domaintags.EfficientToUnicode(n)
rec.NameUnicode = domaintags.EfficientToUnicode(n)
}
rec.NameFQDN = dnsutil.AddOrigin(rec.Name, dc.Name)
rec.NameFQDNRaw = dnsutil.AddOrigin(rec.NameRaw, dc.NameRaw)
@ -74,13 +95,17 @@ func setRecordNames(rec *models.RecordConfig, dc *models.DomainConfig, n string)
} else {
// _EXTEND() mode:
// FIXME(tlim): Not implemented.
if rec.Name == "@" {
rec.NameRaw = rec.Name
rec.Name = rec.Name
sdRaw := rec.SubDomain
sdIDN := domaintags.EfficientToASCII(rec.SubDomain)
sdUnicode := domaintags.EfficientToUnicode(rec.SubDomain)
if n == "@" {
rec.Name = sdIDN
rec.NameRaw = sdRaw
rec.NameUnicode = sdUnicode
} else {
rec.Name = domaintags.EfficientToASCII(n)
rec.NameRaw = n
rec.Name = domaintags.EfficientToUnicode(n)
rec.Name = domaintags.EfficientToASCII(n + "." + sdIDN)
rec.NameRaw = n + "." + sdRaw
rec.NameUnicode = domaintags.EfficientToUnicode(n + "." + sdUnicode)
}
rec.NameFQDN = dnsutil.AddOrigin(rec.Name, dc.Name)
rec.NameFQDNRaw = dnsutil.AddOrigin(rec.NameRaw, dc.NameRaw)

View file

@ -15,7 +15,7 @@ type RType interface {
Name() string
// RecordConfig factory. Updates a RecordConfig's fields based on args.
FromArgs(*models.RecordConfig, []any) error
FromArgs(*models.DomainConfig, *models.RecordConfig, []any) error
// Returns a string representation of the record in RFC1038 format.
// AsRFC1038String([]string) (string, error)

View file

@ -137,13 +137,15 @@ func (c *cloudflareProvider) GetZoneRecords(domain string, meta map[string]strin
}
}
// if c.manageRedirects { // if old
// prs, err := c.getPageRules(domainID, domain)
// if err != nil {
// return nil, err
// }
// records = append(records, prs...)
// }
if c.manageRedirects { // if old-style "page rules" are still being managed.
fmt.Printf("DEBUG: Getting old-style page rules for %s???\n", domain)
panic("stop")
// prs, err := c.getPageRules(domainID, domain)
// if err != nil {
// return nil, err
// }
// records = append(records, prs...)
}
if c.manageSingleRedirects { // if new xor old
// Download the list of Single Redirects.
@ -356,14 +358,19 @@ func (c *cloudflareProvider) mkChangeCorrection(oldrec, newrec *models.RecordCon
func (c *cloudflareProvider) mkDeleteCorrection(recType string, origRec *models.RecordConfig, domainID string, msg string) []*models.Correction {
var idTxt string
switch recType {
// case "PAGE_RULE":
// idTxt = origRec.Original.(cloudflare.PageRule).ID
case "PAGE_RULE":
idTxt = origRec.Original.(cloudflare.PageRule).ID
case "WORKER_ROUTE":
idTxt = origRec.Original.(cloudflare.WorkerRoute).ID
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
idTxt = origRec.Original.(cloudflare.RulesetRule).ID
case "":
fmt.Printf("DEBUG: %q origRec.Original type is %T\nrec=%+v\n\n", recType, origRec.Original, *origRec)
idTxt = origRec.Original.(cloudflare.RulesetRule).ID
default:
idTxt = origRec.Original.(cloudflare.DNSRecord).ID
//fmt.Printf("DEBUG: %q rec=%+v origRec.Original type is %T\n", recType, *origRec, origRec.Original)
fmt.Printf("DEBUG: %q origRec.Original type is %T\n", recType, origRec.Original)
//idTxt = origRec.Original.(cloudflare.DNSRecord).ID
}
msg = msg + color.RedString(" id=%v", idTxt)

View file

@ -10,6 +10,7 @@ import (
"golang.org/x/net/idna"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
"github.com/StackExchange/dnscontrol/v4/providers/cloudflare/rtypes/cfsingleredirect"
)
@ -289,9 +290,6 @@ func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*mo
recs := []*models.RecordConfig{}
for _, pr := range rules.Rules {
thisPr := pr
r := &models.RecordConfig{
Original: thisPr,
}
// Extract the valuables from the rule, use it to make the sr:
srName := pr.Description
@ -299,17 +297,25 @@ func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*mo
srThen := pr.ActionParameters.FromValue.TargetURL.Expression
code := uint16(pr.ActionParameters.FromValue.StatusCode)
if err := cfsingleredirect.MakeSingleRedirectFromAPI(r, code, srName, srWhen, srThen); err != nil {
// if err := cfsingleredirect.MakeSingleRedirectFromAPI(r, code, srName, srWhen, srThen); err != nil {
// return nil, err
// }
// r.SetLabel("@", domain)
rec, err := rtypecontrol.NewRecordConfigFromRaw(
"CLOUDFLAREAPI_SINGLE_REDIRECT",
[]any{srName, code, srWhen, srThen},
models.MakeFakeDomainConfig(domain))
if err != nil {
return nil, err
}
r.SetLabel("@", domain)
rec.Original = thisPr
// Store the IDs
sr := r.F.(*cfsingleredirect.SingleRedirectConfig)
sr := rec.F.(*cfsingleredirect.SingleRedirectConfig)
sr.SRRRulesetID = rules.ID
sr.SRRRulesetRuleID = pr.ID
recs = append(recs, r)
recs = append(recs, rec)
}
return recs, nil

View file

@ -0,0 +1,77 @@
package cfsingleredirect
import (
"fmt"
"sync"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
)
func init() {
rtypecontrol.Register(&CfRedirect{})
rtypecontrol.Register(&CfTempRedirect{})
}
type CfRedirect struct{}
// Name returns the text (all caps) name of the rtype.
func (handle *CfRedirect) Name() string {
return "CF_REDIRECT"
}
func (handle *CfRedirect) FromArgs(dc *models.DomainConfig, rec *models.RecordConfig, args []any) error {
fmt.Printf("DEBUG: CF_REDIRECT FromArgs called with args=%+v\n", args)
return FromArgs_helper(dc, rec, args, 301)
}
type CfTempRedirect struct{}
// Name returns the text (all caps) name of the rtype.
func (handle *CfTempRedirect) Name() string {
return "CF_TEMP_REDIRECT"
}
var services = map[string]int{}
var serviceMutex = sync.RWMutex{}
func inc(name string) {
serviceMutex.Lock()
defer serviceMutex.Unlock()
services[name]++
}
func get(name string) int {
serviceMutex.Lock()
defer serviceMutex.Unlock()
return services[name]
}
func (handle *CfTempRedirect) FromArgs(dc *models.DomainConfig, rec *models.RecordConfig, args []any) error {
fmt.Printf("DEBUG: CF_TEMP_REDIRECT FromArgs called with args=%+v\n", args)
return FromArgs_helper(dc, rec, args, 302)
}
func FromArgs_helper(dc *models.DomainConfig, rec *models.RecordConfig, args []any, code int) error {
// Pave the args to be the expected types.
if err := rtypecontrol.PaveArgs(args, "ss"); err != nil {
return err
}
// Convert old-style patterns to new-style rules:
prWhen := args[0].(string)
prThen := args[1].(string)
srWhen, srThen, err := makeRuleFromPattern(prWhen, prThen)
if err != nil {
return err
}
inc(dc.UniqueName)
name := fmt.Sprintf("%03d,%03d,%s,%s", get(dc.UniqueName), code, prWhen, prThen)
sr := SingleRedirectConfig{}
rec.Type = sr.Name() // This record is now a CLOUDFLAREAPI_SINGLE_REDIRECT
return sr.FromArgs(dc, rec, []any{name, code, srWhen, srThen})
}

View file

@ -35,7 +35,8 @@ func (handle *SingleRedirectConfig) Name() string {
return "CLOUDFLAREAPI_SINGLE_REDIRECT"
}
func (handle *SingleRedirectConfig) FromArgs(rec *models.RecordConfig, args []any) error {
func (handle *SingleRedirectConfig) FromArgs(dc *models.DomainConfig, rec *models.RecordConfig, args []any) error {
fmt.Printf("DEBUG: CLOUDFLAREAPI_SINGLE_REDIRECT FromArgs called with args=%+v\n", args)
// Pave the args to be the expected types.
if err := rtypecontrol.PaveArgs(args, "siss"); err != nil {
return err
@ -52,7 +53,9 @@ func (handle *SingleRedirectConfig) FromArgs(rec *models.RecordConfig, args []an
}
when = args[2].(string)
then = args[3].(string)
//fmt.Printf("\n\nDEBUG: targetFromRaw(name=%q code=%03d when=%q then=%q)\n", name, code, when, then)
display := targetFromRaw(name, code, when, then)
//fmt.Printf("DEBUG: targetFromRaw(name=%q code=%03d when=%q then=%q) display=%q\n\n\n", name, code, when, then, display)
rec.F = &SingleRedirectConfig{
Code: code,
@ -68,15 +71,24 @@ func (handle *SingleRedirectConfig) FromArgs(rec *models.RecordConfig, args []an
SRDisplay: display,
}
//rec.Name = name
rec.Name = "@"
rec.NameRaw = "@"
rec.NameUnicode = "@"
rec.NameFQDN = dc.Name
rec.NameFQDNRaw = dc.NameRaw
rec.NameFQDNUnicode = dc.NameUnicode
rec.TTL = 1
rec.Comparable = display
rec.ZonefilePartial = display
_ = rec.SetTarget(display)
return nil
}
// targetFromRaw create the display text used for a normal Redirect.
func targetFromRaw(name string, code uint16, when, then string) string {
return fmt.Sprintf("%s code=(%03d) when=(%s) then=(%s)",
return fmt.Sprintf("name=(%s) code=(%03d) when=(%s) then=(%s)",
name,
code,
when,

View file

@ -5,67 +5,8 @@ import (
"net"
"net/url"
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
)
func init() {
rtypecontrol.Register(&CfRedirect{})
}
type CfRedirect struct{}
// Name returns the text (all caps) name of the rtype.
func (handle *CfRedirect) Name() string {
return "CF_REDIRECT"
}
func (handle *CfRedirect) FromArgs(rec *models.RecordConfig, args []any) error {
// Pave the args to be the expected types.
if err := rtypecontrol.PaveArgs(args, "ss"); err != nil {
return err
}
// Convert old-style patterns to new-style rules:
prWhen := args[0].(string)
prThen := args[1].(string)
srWhen, srThen, err := makeRuleFromPattern(prWhen, prThen)
if err != nil {
return err
}
sr := SingleRedirectConfig{}
rec.Type = sr.Name() // This record is now a CLOUDFLAREAPI_SINGLE_REDIRECT
return sr.FromArgs(rec, []any{"@", 301, srWhen, srThen})
}
type CfTempRedirect struct{}
// Name returns the text (all caps) name of the rtype.
func (handle *CfTempRedirect) Name() string {
return "CF_TEMP_REDIRECT"
}
func (handle *CfTempRedirect) FromArgs(rec *models.RecordConfig, args []any) error {
// Pave the args to be the expected types.
if err := rtypecontrol.PaveArgs(args, "ss"); err != nil {
return err
}
// Convert old-style patterns to new-style rules:
prWhen := args[0].(string)
prThen := args[1].(string)
srWhen, srThen, err := makeRuleFromPattern(prWhen, prThen)
if err != nil {
return err
}
sr := SingleRedirectConfig{}
rec.Type = sr.Name() // This record is now a CLOUDFLAREAPI_SINGLE_REDIRECT
return sr.FromArgs(rec, []any{"@", 301, srWhen, srThen})
}
// // TranscodePRtoSR takes a PAGE_RULE record, stores transcoded versions of the fields, and makes the record a CLOUDFLAREAPI_SINGLE_REDDIRECT.
// func TranscodePRtoSR(rec *models.RecordConfig) error {
// //rec.Type = SINGLEREDIRECT // This record is now a CLOUDFLAREAPI_SINGLE_REDIRECT

View file

@ -1,10 +1,5 @@
package cfsingleredirect
import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
)
// // MakePageRule updates a RecordConfig to be a PAGE_RULE using PAGE_RULE data.
// func MakePageRule(rc *models.RecordConfig, priority int, code uint16, when, then string) error {
// if rc == nil {
@ -34,9 +29,9 @@ import (
// return fmt.Sprintf("%03d,%03d,%s,%s", priority, code, when, then)
// }
func MakeSingleRedirectFromAPI(rc *models.RecordConfig, code uint16, name, when, then string) error {
return rtypecontrol.Func["SINGLEREDIRECT"].FromArgs(rc, []any{name, code, when, then})
}
// func MakeSingleRedirectFromAPI(rc *models.RecordConfig, code uint16, name, when, then string) error {
// return rtypecontrol.Func["CLOUDFLAREAPI_SINGLE_REDIRECT"].FromArgs(rc, []any{name, code, when, then})
// }
// // MakeSingleRedirectFromAPI updatese a RecordConfig to be a SINGLEREDIRECT using data downloaded via the API.
// func MakeSingleRedirectFromAPI(rc *models.RecordConfig, code uint16, name, when, then string) error {