This commit is contained in:
Thomas Limoncelli 2025-11-24 13:24:51 -05:00
parent 633c9ecacc
commit 4b7da682a4
No known key found for this signature in database
6 changed files with 197 additions and 108 deletions

View file

@ -107,9 +107,13 @@ type RecordConfig struct {
FieldsAsRaw []string // Fields as received from the dnsconfig.js file.
FieldsAsUnicode []string // fields with IDN fields converted to Unicode.
// Legacy fields we hope to remove someday
SubDomain string `json:"subdomain,omitempty"`
// Cloudflare-specific fields:
// When these are used, .target is set to a human-readable version (only to be used for display purposes).
//CloudflareRedirect *CloudflareSingleRedirectConfig `json:"cloudflareapi_redirect,omitempty"`
CloudflareRedirect *CloudflareSingleRedirectConfig `json:"cloudflareapi_redirect,omitempty"`
Metadata map[string]string `json:"meta,omitempty"` // Metadata added to record via dnsconfig.js
FilePos string `json:"filepos"` // filename:line:char source
@ -166,7 +170,14 @@ type RecordConfig struct {
UnknownTypeName string `json:"unknown_type_name,omitempty"`
}
// func NewRecordConfig(argsRaw):
func NewRecordConfig(rtype string, ttl uint32, argsRaw []string) *RecordConfig {
rc := &RecordConfig{
Type: rtype,
TTL: ttl,
}
return rc
}
// .Type = type
// .TTL = ttl

View file

@ -23,7 +23,7 @@ func (rc *RecordConfig) SetTargetCAA(flag uint8, tag string, target string) erro
// Per: https://www.iana.org/assignments/pkix-parameters/pkix-parameters.xhtml#caa-properties excluding reserved tags
allowedTags := []string{"issue", "issuewild", "iodef", "contactemail", "contactphone", "issuemail", "issuevmc"}
if !slices.Contains(allowedTags, tag) {
return fmt.Errorf("CAA tag (%v) is not one of the valid types.", tag)
return fmt.Errorf("CAA tag (%v) is not one of the valid types", tag)
}
return nil

View file

@ -333,16 +333,16 @@ var AKAMAICDN = recordBuilder('AKAMAICDN');
// AKAMAITLC(name, answer_type, target, recordModifiers...)
var AKAMAITLC = recordBuilder('AKAMAITLC', {
args: [
['name', _.isString],
['answer_type', function(value) { return _.isString(value) && ['DUAL', 'A', 'AAAA'].indexOf(value) !== -1; }],
['target', _.isString],
],
transform: function (record, args, modifier) {
record.name = args.name;
record.answer_type = args.answer_type;
record.target = args.target;
},
args: [
['name', _.isString],
['answer_type', function (value) { return _.isString(value) && ['DUAL', 'A', 'AAAA'].indexOf(value) !== -1; }],
['target', _.isString],
],
transform: function (record, args, modifier) {
record.name = args.name;
record.answer_type = args.answer_type;
record.target = args.target;
},
});
// ALIAS(name,target, recordModifiers...)
@ -683,20 +683,20 @@ var TXT = recordBuilder('TXT', {
});
var LUA = recordBuilder('LUA', {
args: [
['name', _.isString],
['rtype', _.isString],
['target', isStringOrArray],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.luartype = args.rtype.toUpperCase();
if (_.isString(args.target)) {
record.target = args.target;
} else {
record.target = args.target.join('');
}
},
args: [
['name', _.isString],
['rtype', _.isString],
['target', isStringOrArray],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.luartype = args.rtype.toUpperCase();
if (_.isString(args.target)) {
record.target = args.target;
} else {
record.target = args.target.join('');
}
},
});
// Parses coordinates of the form 41°24'12.2"N 2°10'26.5"E
@ -858,15 +858,15 @@ function locStringBuilder(record, args) {
(args.alt < -100000
? -100000
: args.alt > 42849672.95
? 42849672.95
: args.alt.toString()) + 'm';
? 42849672.95
: args.alt.toString()) + 'm';
precisionbuffer +=
' ' +
(args.siz > 90000000
? 90000000
: args.siz < 0
? 0
: args.siz.toString()) +
? 0
: args.siz.toString()) +
'm';
precisionbuffer +=
' ' +
@ -906,8 +906,8 @@ function locDMSBuilder(record, args) {
record.localtitude > 4294967295
? 4294967295
: record.localtitude < 0
? 0
: record.localtitude;
? 0
: record.localtitude;
// Size
record.locsize = getENotationInt(args.siz);
// Horizontal Precision
@ -1785,7 +1785,7 @@ function CAA_BUILDER(value) {
throw 'CAA_BUILDER requires at least one entry at issue, issuewild, issuevmc or issuemail';
}
var CAA_TTL = function () {};
var CAA_TTL = function () { };
if (value.ttl) {
CAA_TTL = TTL(value.ttl);
}
@ -1802,7 +1802,7 @@ function CAA_BUILDER(value) {
}
if (value.issue) {
var flag = function () {};
var flag = function () { };
if (value.issue_critical) {
flag = CAA_CRITICAL;
}
@ -1811,7 +1811,7 @@ function CAA_BUILDER(value) {
}
if (value.issuewild) {
var flag = function () {};
var flag = function () { };
if (value.issuewild_critical) {
flag = CAA_CRITICAL;
}
@ -1822,7 +1822,7 @@ function CAA_BUILDER(value) {
}
if (value.issuevmc) {
var flag = function () {};
var flag = function () { };
if (value.issuevmc_critical) {
flag = CAA_CRITICAL;
}
@ -1833,7 +1833,7 @@ function CAA_BUILDER(value) {
}
if (value.issuemail) {
var flag = function () {};
var flag = function () { };
if (value.issuemail_critical) {
flag = CAA_CRITICAL;
}
@ -2052,7 +2052,7 @@ function DKIM_BUILDER(value) {
}
// Handle TTL
var DKIM_TTL = value.ttl ? TTL(value.ttl) : function () {};
var DKIM_TTL = value.ttl ? TTL(value.ttl) : function () { };
return TXT(fullLabel, record.join('; '), DKIM_TTL);
}
@ -2284,20 +2284,20 @@ function M365_BUILDER(name, value) {
CNAME(
'selector1._domainkey',
'selector1-' +
value.domainGUID +
'._domainkey.' +
value.initialDomain +
'.'
value.domainGUID +
'._domainkey.' +
value.initialDomain +
'.'
)
);
r.push(
CNAME(
'selector2._domainkey',
'selector2-' +
value.domainGUID +
'._domainkey.' +
value.initialDomain +
'.'
value.domainGUID +
'._domainkey.' +
value.initialDomain +
'.'
)
);
}
@ -2477,3 +2477,4 @@ function rawrecordBuilder(type) {
// CLOUDFLAREAPI:
var CF_SINGLE_REDIRECT = rawrecordBuilder('CLOUDFLAREAPI_SINGLE_REDIRECT');
var RP = rawrecordBuilder('RP');

View file

@ -1,21 +1,43 @@
package rtypecontrol
import "github.com/StackExchange/dnscontrol/v4/providers"
import (
"fmt"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/providers"
)
// backwards compatibility:
var validTypes = map[string]struct{}{}
func Register(t string) {
// Does this already exist?
if _, ok := validTypes[t]; ok {
panic("rtype %q already registered. Can't register it a second time!")
}
type RType interface {
// Returns the name of the rtype ("A", "MX", etc.)
Name() string
validTypes[t] = struct{}{}
// RecordConfig factory. Updates a RecordConfig's fields based on args.
FromArgs(*models.RecordConfig, args []any) (*models.RecordConfig, error) //
providers.RegisterCustomRecordType(t, "", "")
// Returns a string representation of the record in RFC1038 format.
// AsRFC1038String([]string) (string, error)
}
func IsValid(t string) bool {
_, ok := validTypes[t]
// Map of registered rtypes.
var Iface map[string]RType = map[string]RType{}
func Register(typeName string, t RType) {
name := t.Name()
if _, ok := Iface[name]; ok {
panic(fmt.Sprintf("rtype %q already registered. Can't register it a second time!", name))
}
// Store the interface
Iface[name] = t
// For compatibility with legacy systems:
providers.RegisterCustomRecordType(name, "", "")
}
func IsValid(name string) bool {
_, ok := Iface[name]
return ok
}

View file

@ -1,10 +1,8 @@
package rtypes
import (
"fmt"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/providers/cloudflare/rtypes/cfsingleredirect"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
)
func PostProcess(domains []*models.DomainConfig) error {
@ -12,37 +10,54 @@ func PostProcess(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,
Name: rawRec.Args[0].(string),
Metadata: map[string]string{},
Metadata: stringifyMetas(rawRec.Metas),
}
//rec.Name, rec.NameRaw, rec.NameUnicode := normalizeName(rawRec.Args[0].(string), dc.Name, dc.SubDomain)
//rec.NameFQDN, rec.NameFQDNRaw, rec.NameFQDNUnicode := normalizeNameFQDN(rawRec.Args[0].(string), dc.Name, dc.SubDomain)
// Name:
// * Convert to lowercase.
// * Convert to IDN and UNI.
//
// IDN: name + subdomain + domain
// Copy the metadata (convert everything to string)
for _, m := range rawRec.Metas {
for mk, mv := range m {
if v, ok := mv.(string); ok {
rec.Metadata[mk] = v // Already a string. No new malloc.
} else {
rec.Metadata[mk] = fmt.Sprintf("%v", mv)
}
}
}
rtypecontrol.Iface[rawRec.Type].FromArgs(rec, rawRec.Args)
// Call the proper initialize function.
// TODO(tlim): Good candidate for an interface or a lookup table.
switch rawRec.Type {
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
err = cfsingleredirect.FromRaw(rec, rawRec.Args)
rec.SetLabel("@", dc.Name)
// rec := &models.RecordConfig{
// Type: rawRec.Type,
// TTL: rawRec.TTL,
// Name: rawRec.Args[0].(string),
// Metadata: map[string]string{},
// }
default:
err = fmt.Errorf("unknown rawrec type=%q", rawRec.Type)
}
if err != nil {
return fmt.Errorf("%s (%q, %q) record error: %w", rawRec.Type, rec.Name, dc.Name, err)
}
// // Copy the metadata (convert everything to string)
// for _, m := range rawRec.Metas {
// for mk, mv := range m {
// if v, ok := mv.(string); ok {
// rec.Metadata[mk] = v // Already a string. No new malloc.
// } else {
// rec.Metadata[mk] = fmt.Sprintf("%v", mv)
// }
// }
// }
// // Call the proper initialize function.
// // TODO(tlim): Good candidate for an interface or a lookup table.
// switch rawRec.Type {
// case "CLOUDFLAREAPI_SINGLE_REDIRECT":
// err = cfsingleredirect.FromRaw(rec, rawRec.Args)
// rec.SetLabel("@", dc.Name)
// default:
// err = fmt.Errorf("unknown rawrec type=%q", rawRec.Type)
// }
// if err != nil {
// return fmt.Errorf("%s (%q, %q) record error: %w", rawRec.Type, rec.Name, dc.Name, err)
// }
// Free memeory:
clear(rawRec.Args)

View file

@ -1,37 +1,77 @@
package cfsingleredirect
import (
"fmt"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
)
// SINGLEREDIRECT is the string name for this rType.
const SINGLEREDIRECT = "CLOUDFLAREAPI_SINGLE_REDIRECT"
// "github.com/StackExchange/dnscontrol/v4/models"
// "github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
func init() {
rtypecontrol.Register(SINGLEREDIRECT)
rtypecontrol.Register(SingleRedirect{})
}
// FromRaw convert RecordConfig using data from a RawRecordConfig's parameters.
func FromRaw(rc *models.RecordConfig, items []any) error {
// Validate types.
if err := rtypecontrol.PaveArgs(items, "siss"); err != nil {
return err
}
type SingleRedirect struct{}
// Unpack the args:
var name, when, then string
var code uint16
name = items[0].(string)
code = items[1].(uint16)
if code != 301 && code != 302 && code != 303 && code != 307 && code != 308 {
return fmt.Errorf("%s: code (%03d) is not 301,302,303,307,308", rc.FilePos, code)
}
when = items[2].(string)
then = items[3].(string)
return makeSingleRedirectFromRawRec(rc, code, name, when, then)
func (handle *SingleRedirect) Name() string {
return "CLOUDFLAREAPI_SINGLE_REDIRECT"
}
// func MakeSingleRedirect() SingleRedirect { return SingleRedirect{} }
func (handle *SingleRedirect) FromArgs([]any) (*models.RecordConfig, error) {
rec := &models.RecordConfig{
Type: handle.Name(),
TTL: ttl,
//FilePos = FixFilePos(handle.FilePos)
}
return rec, nil
}
// // Validate types.
// if err := rtypecontrol.PaveArgs(items, "siss"); err != nil {
// return err
// }
// // Unpack the args:
// var name, when, then string
// var code uint16
// name = items[0].(string)
// code = items[1].(uint16)
// if code != 301 && code != 302 && code != 303 && code != 307 && code != 308 {
// return fmt.Errorf("%s: code (%03d) is not 301,302,303,307,308", rc.FilePos, code)
// }
// when = items[2].(string)
// then = items[3].(string)
// return makeSingleRedirectFromRawRec(rc, code, name, when, then)
return &models.RecordConfig{}, nil
}
//func (handle *SingleRedirect) IDNFields(argsRaw) (argsIDN, argsUnicode, error) {}
//func (handle *SingleRedirect) AsRFC1038String(*models.RecordConfig) string {}
//func (handle *SingleRedirect) CopyToLegacyFields(*models.RecordConfig) {}
//func (handle *SingleRedirect) CopyFromLegacyFields(*models.RecordConfig) {}
// // FromRaw convert RecordConfig using data from a RawRecordConfig's parameters.
// func FromRaw(rc *models.RecordConfig, items []any) error {
// // Validate types.
// if err := rtypecontrol.PaveArgs(items, "siss"); err != nil {
// return err
// }
// // Unpack the args:
// var name, when, then string
// var code uint16
// name = items[0].(string)
// code = items[1].(uint16)
// if code != 301 && code != 302 && code != 303 && code != 307 && code != 308 {
// return fmt.Errorf("%s: code (%03d) is not 301,302,303,307,308", rc.FilePos, code)
// }
// when = items[2].(string)
// then = items[3].(string)
// return makeSingleRedirectFromRawRec(rc, code, name, when, then)
// }