Skip to content

Commit 48429ca

Browse files
authored
Fix specific record types not being parsed correctly (#17)
* Fix specific record types not being parsed correctly This change specifically applies to MX and SRV records as all other record types got parsed correctly already. Some of Cloudflare's API responses for specific record types do not include the raw DNS record content in the `Content` field. For example, `MX` records would have a content of `example.com` instead of `<PRIORITY> example.com`, making them unable to be properly parsed. To solve this, we use the structured record data already provided to us by Cloudflare's API wherever it makes sense, and fallback to the libdns parsing when it makes sense. Signed-off-by: Matthew Penner <[email protected]> * Remove leftover `GOT RECORDS` debug log Signed-off-by: Matthew Penner <[email protected]> --------- Signed-off-by: Matthew Penner <[email protected]>
1 parent e57c132 commit 48429ca

File tree

2 files changed

+93
-12
lines changed

2 files changed

+93
-12
lines changed

models.go

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package cloudflare
22

33
import (
44
"encoding/json"
5+
"fmt"
6+
"net/netip"
57
"strings"
68
"time"
79

@@ -54,6 +56,7 @@ type cfDNSRecord struct {
5456
Type string `json:"type,omitempty"`
5557
Name string `json:"name,omitempty"`
5658
Content string `json:"content,omitempty"`
59+
Priority uint16 `json:"priority,omitempty"`
5760
Proxiable bool `json:"proxiable,omitempty"`
5861
Proxied bool `json:"proxied,omitempty"`
5962
TTL int `json:"ttl,omitempty"` // seconds
@@ -85,10 +88,16 @@ type cfDNSRecord struct {
8588
Weight uint16 `json:"weight,omitempty"`
8689
Port uint16 `json:"port,omitempty"`
8790
Target string `json:"target,omitempty"`
88-
Value string `json:"value,omitempty"`
8991

92+
// CAA, SRV, HTTPS
93+
Value string `json:"value,omitempty"`
94+
95+
// CAA
96+
Tag string `json:"tag"`
97+
98+
// CAA, DNSKEY
99+
Flags int `json:"flags,omitempty"`
90100
// DNSKEY
91-
Flags int `json:"flags,omitempty"`
92101
Protocol int `json:"protocol,omitempty"`
93102
Algorithm int `json:"algorithm,omitempty"`
94103

@@ -105,18 +114,92 @@ type cfDNSRecord struct {
105114
Content string `json:"content,omitempty"`
106115
} `json:"data,omitempty"`
107116
Meta *struct {
108-
AutoAdded bool `json:"auto_added,omitempty"`
109-
Source string `json:"source,omitempty"`
117+
AutoAdded bool `json:"auto_added,omitempty"`
118+
Source string `json:"source,omitempty"`
119+
EmailRouting bool `json:"email_routing,omitempty"`
120+
ReadOnly bool `json:"read_only,omitempty"`
110121
} `json:"meta,omitempty"`
111122
}
112123

113124
func (r cfDNSRecord) libdnsRecord(zone string) (libdns.Record, error) {
114-
return libdns.RR{
115-
Name: libdns.RelativeName(r.Name, zone),
116-
TTL: time.Duration(r.TTL) * time.Second,
117-
Type: r.Type,
118-
Data: r.Content,
119-
}.Parse()
125+
name := libdns.RelativeName(r.Name, zone)
126+
ttl := time.Duration(r.TTL) * time.Second
127+
switch r.Type {
128+
case "A", "AAAA":
129+
addr, err := netip.ParseAddr(r.Content)
130+
if err != nil {
131+
return libdns.Address{}, fmt.Errorf("invalid IP address %q: %v", r.Data, err)
132+
}
133+
return libdns.Address{
134+
Name: name,
135+
TTL: ttl,
136+
IP: addr,
137+
}, nil
138+
case "CAA":
139+
// NOTE: CAA records from Cloudflare have a `r.Content` that can be
140+
// parsed by [libdns.RR.Parse], but all the data we need is already sent
141+
// to us in a structured format by Cloudflare, so we use that instead.
142+
return libdns.CAA{
143+
Name: name,
144+
TTL: ttl,
145+
Flags: uint8(r.Data.Flags),
146+
Tag: r.Data.Tag,
147+
Value: r.Data.Value,
148+
}, nil
149+
case "CNAME":
150+
return libdns.CNAME{
151+
Name: name,
152+
TTL: ttl,
153+
Target: r.Content,
154+
}, nil
155+
case "MX":
156+
return libdns.MX{
157+
Name: name,
158+
TTL: ttl,
159+
Preference: r.Priority,
160+
Target: r.Content,
161+
}, nil
162+
case "NS":
163+
return libdns.NS{
164+
Name: name,
165+
TTL: ttl,
166+
Target: r.Content,
167+
}, nil
168+
case "SRV":
169+
parts := strings.SplitN(name, ".", 3)
170+
if len(parts) < 3 {
171+
return libdns.SRV{}, fmt.Errorf("name %v does not contain enough fields; expected format: '_service._proto.name'", name)
172+
}
173+
return libdns.SRV{
174+
Service: strings.TrimPrefix(parts[0], "_"),
175+
Transport: strings.TrimPrefix(parts[1], "_"),
176+
Name: parts[2],
177+
TTL: ttl,
178+
Priority: r.Data.Priority,
179+
Weight: r.Data.Weight,
180+
Port: r.Data.Port,
181+
Target: r.Data.Target,
182+
}, nil
183+
case "TXT":
184+
return libdns.TXT{
185+
Name: name,
186+
TTL: ttl,
187+
Text: r.Content,
188+
}, nil
189+
// NOTE: HTTPS records from Cloudflare have a `r.Content` that can be
190+
// parsed by [libdns.RR.Parse] so that is what we do here. While we are
191+
// provided with structured data, it still requires a bit of parsing
192+
// that would end up duplicating the code from libdns anyways.
193+
// case "HTTPS", "SVCB":
194+
// fallthrough
195+
default:
196+
return libdns.RR{
197+
Name: name,
198+
TTL: ttl,
199+
Type: r.Type,
200+
Data: r.Content,
201+
}.Parse()
202+
}
120203
}
121204

122205
func cloudflareRecord(r libdns.Record) (cfDNSRecord, error) {

provider.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cloudflare
33
import (
44
"context"
55
"fmt"
6-
"log"
76
"net/http"
87
"sync"
98

@@ -49,7 +48,6 @@ func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record
4948
}
5049
recs = append(recs, libdnsRec)
5150
}
52-
log.Printf("GOT RECORDS: %#v", recs)
5351

5452
return recs, nil
5553
}

0 commit comments

Comments
 (0)