Skip to content

Commit dcdc252

Browse files
authored
BUGFIX: RecordConfig v2 wasn't processing record meta data (#3932)
# Issue Record-level metadata wasn't being processed by RecordConfig v2 # Resolution fix it.
1 parent 41efba6 commit dcdc252

File tree

7 files changed

+186
-34
lines changed

7 files changed

+186
-34
lines changed

integrationTest/helpers_integration_test.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,12 @@ func cfSingleRedirectEnabled() bool {
343343
}
344344

345345
func cfSingleRedirect(name string, code any, when, then string) *models.RecordConfig {
346-
rec, err := rtypecontrol.NewRecordConfigFromRaw("CLOUDFLAREAPI_SINGLE_REDIRECT", 1, []any{name, code, when, then}, globalDCN, "")
346+
rec, err := rtypecontrol.NewRecordConfigFromRaw(rtypecontrol.FromRawOpts{
347+
Type: "CLOUDFLAREAPI_SINGLE_REDIRECT",
348+
TTL: 1,
349+
Args: []any{name, code, when, then},
350+
DCN: globalDCN,
351+
})
347352
panicOnErr(err)
348353
return rec
349354
}
@@ -355,13 +360,23 @@ func cfWorkerRoute(pattern, target string) *models.RecordConfig {
355360
}
356361

357362
func cfRedir(pattern, target string) *models.RecordConfig {
358-
rec, err := rtypecontrol.NewRecordConfigFromRaw("CF_REDIRECT", 1, []any{pattern, target}, globalDCN, "")
363+
rec, err := rtypecontrol.NewRecordConfigFromRaw(rtypecontrol.FromRawOpts{
364+
Type: "CF_REDIRECT",
365+
TTL: 1,
366+
Args: []any{pattern, target},
367+
DCN: globalDCN,
368+
})
359369
panicOnErr(err)
360370
return rec
361371
}
362372

363373
func cfRedirTemp(pattern, target string) *models.RecordConfig {
364-
rec, err := rtypecontrol.NewRecordConfigFromRaw("CF_TEMP_REDIRECT", 1, []any{pattern, target}, globalDCN, "")
374+
rec, err := rtypecontrol.NewRecordConfigFromRaw(rtypecontrol.FromRawOpts{
375+
Type: "CF_TEMP_REDIRECT",
376+
TTL: 1,
377+
Args: []any{pattern, target},
378+
DCN: globalDCN,
379+
})
365380
panicOnErr(err)
366381
return rec
367382
}
@@ -487,7 +502,12 @@ func r53alias(name, aliasType, target, evalTargetHealth string) *models.RecordCo
487502
}
488503

489504
func rp(name string, m, t string) *models.RecordConfig {
490-
rec, err := rtypecontrol.NewRecordConfigFromRaw("RP", 300, []any{name, m, t}, globalDCN, "")
505+
rec, err := rtypecontrol.NewRecordConfigFromRaw(rtypecontrol.FromRawOpts{
506+
Type: "RP",
507+
TTL: 300,
508+
Args: []any{name, m, t},
509+
DCN: globalDCN,
510+
})
491511
panicOnErr(err)
492512
return rec
493513
}

pkg/js/parse_tests/050-cfSingleRedirect.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@
7474
"sr_when": "whenmeta"
7575
},
7676
"filepos": "[line:9:5]",
77+
"meta": {
78+
"metanum": "22",
79+
"metastr": "stringy"
80+
},
7781
"name": "@",
7882
"name_raw": "@",
7983
"name_unicode": "@",

pkg/js/parse_tests/060-rawmetas.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// This tests whether or not metadata gets passed to RecordConfig v2.
2+
3+
D("bar.com", "none",
4+
RP("foo.bar.com", "user2.example.com.", "mytxt.example.com.", DISABLE_REPEATED_DOMAIN_CHECK),
5+
RP("bar.com", "user2.example.com.", "mytxt.example.com.", DISABLE_REPEATED_DOMAIN_CHECK),
6+
)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"dns_providers": [],
3+
"domains": [
4+
{
5+
"dnsProviders": {},
6+
"meta": {
7+
"dnscontrol_nameraw": "bar.com",
8+
"dnscontrol_nameunicode": "bar.com",
9+
"dnscontrol_uniquename": "bar.com"
10+
},
11+
"name": "bar.com",
12+
"records": [
13+
{
14+
"comparable": "user2.example.com. mytxt.example.com.",
15+
"fields": {
16+
"Hdr": {
17+
"Class": 0,
18+
"Name": "",
19+
"Rdlength": 0,
20+
"Rrtype": 0,
21+
"Ttl": 0
22+
},
23+
"Mbox": "user2.example.com.",
24+
"Txt": "mytxt.example.com."
25+
},
26+
"filepos": "[line:5:5]",
27+
"meta": {
28+
"skip_fqdn_check": "true"
29+
},
30+
"name": "bar.com",
31+
"name_raw": "bar.com",
32+
"name_unicode": "bar.com",
33+
"target": "",
34+
"ttl": 300,
35+
"type": "RP",
36+
"zonfefilepartial": "user2.example.com. mytxt.example.com."
37+
},
38+
{
39+
"comparable": "user2.example.com. mytxt.example.com.",
40+
"fields": {
41+
"Hdr": {
42+
"Class": 0,
43+
"Name": "",
44+
"Rdlength": 0,
45+
"Rrtype": 0,
46+
"Ttl": 0
47+
},
48+
"Mbox": "user2.example.com.",
49+
"Txt": "mytxt.example.com."
50+
},
51+
"filepos": "[line:4:5]",
52+
"meta": {
53+
"skip_fqdn_check": "true"
54+
},
55+
"name": "foo.bar.com",
56+
"name_raw": "foo.bar.com",
57+
"name_unicode": "foo.bar.com",
58+
"target": "",
59+
"ttl": 300,
60+
"type": "RP",
61+
"zonfefilepartial": "user2.example.com. mytxt.example.com."
62+
}
63+
],
64+
"registrar": "none",
65+
"uniquename": "bar.com"
66+
}
67+
],
68+
"registrars": []
69+
}

pkg/rtypecontrol/import.go

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,21 @@ import (
1313
func ImportRawRecords(domains []*models.DomainConfig) error {
1414
for _, dc := range domains {
1515
for _, rawRec := range dc.RawRecords {
16-
17-
rec, err := NewRecordConfigFromRaw(rawRec.Type, rawRec.TTL, rawRec.Args, dc.DomainNameVarieties(), models.FixPosition(rawRec.FilePos))
16+
filePos := models.FixPosition(rawRec.FilePos)
17+
18+
rec, err := NewRecordConfigFromRaw(FromRawOpts{
19+
Type: rawRec.Type,
20+
TTL: rawRec.TTL,
21+
Args: rawRec.Args,
22+
Metas: rawRec.Metas,
23+
DCN: dc.DomainNameVarieties(),
24+
FilePos: filePos,
25+
})
1826
if err != nil {
19-
return err
27+
return fmt.Errorf("error processing record at %s [%s(%s)]: %v",
28+
filePos,
29+
rawRec.Type, StringifyQuoted(rawRec.Args),
30+
err)
2031
}
2132
if rec.Metadata["skip_fqdn_check"] != "true" && stutters(rec.Name, dc.Name) {
2233
var shortname string
@@ -55,10 +66,27 @@ func stutters(name, domain string) bool {
5566
return false
5667
}
5768

69+
// FromRawOpts contains the options for creating a RecordConfig from raw data.
70+
// Except Type and Args, all fields are optional.
71+
type FromRawOpts struct {
72+
Type string // (required) Record type (e.g., "A", "CNAME")
73+
TTL uint32 // Time to live
74+
Args []any // (required) Arguments for the record
75+
Metas []map[string]any // Metadata for the record
76+
DCN *domaintags.DomainNameVarieties // Domain name varieties
77+
FilePos string // Position in the file where this record was defined
78+
}
79+
5880
// NewRecordConfigFromRaw creates a new RecordConfig from the raw ([]any) args,
5981
// usually from the parsed dnsconfig.js file, but also useful when a provider
6082
// returns the fields of a record as individual values.
61-
func NewRecordConfigFromRaw(t string, ttl uint32, args []any, dcn *domaintags.DomainNameVarieties, FilePos string) (*models.RecordConfig, error) {
83+
func NewRecordConfigFromRaw(opts FromRawOpts) (*models.RecordConfig, error) {
84+
t := opts.Type
85+
ttl := opts.TTL
86+
args := opts.Args
87+
dcn := opts.DCN
88+
FilePos := opts.FilePos
89+
6290
if _, ok := Func[t]; !ok {
6391
return nil, fmt.Errorf("record type %q is not supported", t)
6492
}
@@ -73,13 +101,31 @@ func NewRecordConfigFromRaw(t string, ttl uint32, args []any, dcn *domaintags.Do
73101
Metadata: map[string]string{},
74102
FilePos: FilePos,
75103
}
104+
105+
// Set the label names:
76106
if err := setRecordNames(rec, dcn, args[0].(string)); err != nil {
77107
return rec, err
78108
}
109+
// If setRecordNames notices that a FQDN was used but it is outside the
110+
// D()/D_EXTEND() domain, it will leave the name as a FQDN ending in a dot.
111+
// We catch that here:
79112
if strings.HasSuffix(rec.Name, ".") {
80113
return nil, fmt.Errorf("label %q is not in zone %s", args[0].(string), dcn.DisplayName)
81114
}
82115

116+
// Fill in the metadata fields.
117+
for _, meta := range opts.Metas {
118+
for k, v := range meta {
119+
vStr := fmt.Sprintf("%v", v)
120+
v, exists := rec.Metadata[k]
121+
//fmt.Printf("DEBUG: meta key=%q value=%q v,e=%q,%q\n", k, vStr, v, exists)
122+
if exists && v == vStr {
123+
return nil, fmt.Errorf("duplicate metadata key %q (%q -- %q)", k, rec.Metadata[k], vStr)
124+
}
125+
rec.Metadata[k] = vStr
126+
}
127+
}
128+
83129
// Fill in the .F/.Fields* fields.
84130
err := Func[t].FromArgs(dcn, rec, args)
85131
if err != nil {
@@ -131,6 +177,12 @@ func NewRecordConfigFromStruct(name string, ttl uint32, t string, fields any, dc
131177
if strings.HasSuffix(rec.Name, ".") {
132178
return nil, fmt.Errorf("label %q is not in zone %q", name, dcn.NameASCII+".")
133179
}
180+
if err := setRecordNames(rec, dcn, name); err != nil {
181+
return rec, err
182+
}
183+
if strings.HasSuffix(rec.Name, ".") {
184+
return nil, fmt.Errorf("label %q is not in zone %q", name, dcn.NameASCII+".")
185+
}
134186

135187
err := Func[t].FromStruct(dcn, rec, name, fields)
136188
if err != nil {

pkg/rtypecontrol/setrecordnames_test.go

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ func TestSetRecordNames(t *testing.T) {
181181
dc: dc,
182182
n: "example.com.",
183183
expectedRec: &models.RecordConfig{
184-
NameRaw: "@",
185184
Name: "@",
185+
NameRaw: "@",
186186
NameUnicode: "@",
187-
NameFQDNRaw: "example.com",
188187
NameFQDN: "example.com",
188+
NameFQDNRaw: "example.com",
189189
NameFQDNUnicode: "example.com",
190190
},
191191
},
@@ -195,11 +195,11 @@ func TestSetRecordNames(t *testing.T) {
195195
dc: dc,
196196
n: "www.example.com.",
197197
expectedRec: &models.RecordConfig{
198-
NameRaw: "www",
199198
Name: "www",
199+
NameRaw: "www",
200200
NameUnicode: "www",
201-
NameFQDNRaw: "www.example.com",
202201
NameFQDN: "www.example.com",
202+
NameFQDNRaw: "www.example.com",
203203
NameFQDNUnicode: "www.example.com",
204204
},
205205
},
@@ -209,11 +209,11 @@ func TestSetRecordNames(t *testing.T) {
209209
dc: dc,
210210
n: "bücher.example.com.",
211211
expectedRec: &models.RecordConfig{
212-
NameRaw: "bücher",
213212
Name: "xn--bcher-kva",
213+
NameRaw: "bücher",
214214
NameUnicode: "bücher",
215-
NameFQDNRaw: "bücher.example.com",
216215
NameFQDN: "xn--bcher-kva.example.com",
216+
NameFQDNRaw: "bücher.example.com",
217217
NameFQDNUnicode: "bücher.example.com",
218218
},
219219
},
@@ -223,11 +223,11 @@ func TestSetRecordNames(t *testing.T) {
223223
dc: dcIDN,
224224
n: "www.bücher.com.",
225225
expectedRec: &models.RecordConfig{
226-
NameRaw: "www",
227226
Name: "www",
227+
NameRaw: "www",
228228
NameUnicode: "www",
229-
NameFQDNRaw: "www.bücher.com",
230229
NameFQDN: "www.xn--bcher-kva.com",
230+
NameFQDNRaw: "www.bücher.com",
231231
NameFQDNUnicode: "www.bücher.com",
232232
},
233233
},
@@ -251,12 +251,13 @@ func TestSetRecordNames(t *testing.T) {
251251
dc: dc,
252252
n: "www.bücher.example.com.",
253253
expectedRec: &models.RecordConfig{
254-
SubDomain: "bücher",
255-
Name: "www.xn--bcher-kva",
254+
SubDomain: "bücher",
255+
256256
NameRaw: "www.bücher",
257+
Name: "www.xn--bcher-kva",
257258
NameUnicode: "www.bücher",
258-
NameFQDN: "www.xn--bcher-kva.example.com",
259259
NameFQDNRaw: "www.bücher.example.com",
260+
NameFQDN: "www.xn--bcher-kva.example.com",
260261
NameFQDNUnicode: "www.bücher.example.com",
261262
},
262263
},
@@ -267,11 +268,11 @@ func TestSetRecordNames(t *testing.T) {
267268
n: "bücher.sub.example.com.",
268269
expectedRec: &models.RecordConfig{
269270
SubDomain: "sub",
270-
NameRaw: "bücher.sub",
271271
Name: "xn--bcher-kva.sub",
272+
NameRaw: "bücher.sub",
272273
NameUnicode: "bücher.sub",
273-
NameFQDNRaw: "bücher.sub.example.com",
274274
NameFQDN: "xn--bcher-kva.sub.example.com",
275+
NameFQDNRaw: "bücher.sub.example.com",
275276
NameFQDNUnicode: "bücher.sub.example.com",
276277
},
277278
},
@@ -282,11 +283,11 @@ func TestSetRecordNames(t *testing.T) {
282283
n: "könig.bücher.example.com.",
283284
expectedRec: &models.RecordConfig{
284285
SubDomain: "bücher",
285-
NameRaw: "könig.bücher",
286286
Name: "xn--knig-5qa.xn--bcher-kva",
287+
NameRaw: "könig.bücher",
287288
NameUnicode: "könig.bücher",
288-
NameFQDNRaw: "könig.bücher.example.com",
289289
NameFQDN: "xn--knig-5qa.xn--bcher-kva.example.com",
290+
NameFQDNRaw: "könig.bücher.example.com",
290291
NameFQDNUnicode: "könig.bücher.example.com",
291292
},
292293
},
@@ -297,11 +298,11 @@ func TestSetRecordNames(t *testing.T) {
297298
n: "www.bücher.bücher.com.",
298299
expectedRec: &models.RecordConfig{
299300
SubDomain: "bücher",
300-
NameRaw: "www.bücher",
301301
Name: "www.xn--bcher-kva",
302+
NameRaw: "www.bücher",
302303
NameUnicode: "www.bücher",
303-
NameFQDNRaw: "www.bücher.bücher.com",
304304
NameFQDN: "www.xn--bcher-kva.xn--bcher-kva.com",
305+
NameFQDNRaw: "www.bücher.bücher.com",
305306
NameFQDNUnicode: "www.bücher.bücher.com",
306307
},
307308
},
@@ -324,12 +325,12 @@ func TestSetRecordNames(t *testing.T) {
324325
if tt.rec.NameUnicode != tt.expectedRec.NameUnicode {
325326
t.Errorf("NameUnicode: got %q, want %q", tt.rec.NameUnicode, tt.expectedRec.NameUnicode)
326327
}
327-
if tt.rec.NameFQDN != tt.expectedRec.NameFQDN {
328-
t.Errorf("NameFQDN: got %q, want %q", tt.rec.NameFQDN, tt.expectedRec.NameFQDN)
329-
}
330328
if tt.rec.NameFQDNRaw != tt.expectedRec.NameFQDNRaw {
331329
t.Errorf("NameFQDNRaw: got %q, want %q", tt.rec.NameFQDNRaw, tt.expectedRec.NameFQDNRaw)
332330
}
331+
if tt.rec.NameFQDN != tt.expectedRec.NameFQDN {
332+
t.Errorf("NameFQDN: got %q, want %q", tt.rec.NameFQDN, tt.expectedRec.NameFQDN)
333+
}
333334
if tt.rec.NameFQDNUnicode != tt.expectedRec.NameFQDNUnicode {
334335
t.Errorf("NameFQDNUnicode: got %q, want %q", tt.rec.NameFQDNUnicode, tt.expectedRec.NameFQDNUnicode)
335336
}

providers/cloudflare/rest.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,12 @@ func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*mo
322322
srThen := pr.ActionParameters.FromValue.TargetURL.Expression
323323
code := uint16(pr.ActionParameters.FromValue.StatusCode)
324324

325-
rec, err := rtypecontrol.NewRecordConfigFromRaw(
326-
"CLOUDFLAREAPI_SINGLE_REDIRECT",
327-
1,
328-
[]any{srName, code, srWhen, srThen},
329-
domaintags.MakeDomainNameVarieties(domain),
330-
"")
325+
rec, err := rtypecontrol.NewRecordConfigFromRaw(rtypecontrol.FromRawOpts{
326+
Type: "CLOUDFLAREAPI_SINGLE_REDIRECT",
327+
TTL: 1,
328+
Args: []any{srName, code, srWhen, srThen},
329+
DCN: domaintags.MakeDomainNameVarieties(domain),
330+
})
331331
if err != nil {
332332
return nil, err
333333
}

0 commit comments

Comments
 (0)