Skip to content

Commit 9ec5c8a

Browse files
authored
fix: return an error when extracting record name (#1766)
1 parent 300f42d commit 9ec5c8a

File tree

29 files changed

+363
-225
lines changed

29 files changed

+363
-225
lines changed

challenge/dns01/domain.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dns01
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/miekg/dns"
8+
)
9+
10+
// ExtractSubDomain extracts the subdomain part from a domain and a zone.
11+
func ExtractSubDomain(domain, zone string) (string, error) {
12+
canonDomain := dns.Fqdn(domain)
13+
canonZone := dns.Fqdn(zone)
14+
15+
if canonDomain == canonZone {
16+
return "", fmt.Errorf("no subdomain because the domain and the zone are identical: %s", canonDomain)
17+
}
18+
19+
if !dns.IsSubDomain(canonZone, canonDomain) {
20+
return "", fmt.Errorf("%s is not a subdomain of %s", canonDomain, canonZone)
21+
}
22+
23+
return strings.TrimSuffix(canonDomain, "."+canonZone), nil
24+
}

challenge/dns01/domain_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package dns01
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestExtractSubDomain(t *testing.T) {
11+
testCases := []struct {
12+
desc string
13+
domain string
14+
zone string
15+
expected string
16+
}{
17+
{
18+
desc: "no FQDN",
19+
domain: "_acme-challenge.example.com",
20+
zone: "example.com",
21+
expected: "_acme-challenge",
22+
},
23+
{
24+
desc: "no FQDN zone",
25+
domain: "_acme-challenge.example.com.",
26+
zone: "example.com",
27+
expected: "_acme-challenge",
28+
},
29+
{
30+
desc: "no FQDN domain",
31+
domain: "_acme-challenge.example.com",
32+
zone: "example.com.",
33+
expected: "_acme-challenge",
34+
},
35+
{
36+
desc: "FQDN",
37+
domain: "_acme-challenge.example.com.",
38+
zone: "example.com.",
39+
expected: "_acme-challenge",
40+
},
41+
{
42+
desc: "multi-level subdomain",
43+
domain: "_acme-challenge.one.example.com.",
44+
zone: "example.com.",
45+
expected: "_acme-challenge.one",
46+
},
47+
}
48+
49+
for _, test := range testCases {
50+
test := test
51+
t.Run(test.desc, func(t *testing.T) {
52+
t.Parallel()
53+
54+
subDomain, err := ExtractSubDomain(test.domain, test.zone)
55+
require.NoError(t, err)
56+
57+
assert.Equal(t, test.expected, subDomain)
58+
})
59+
}
60+
}
61+
62+
func TestExtractSubDomain_errors(t *testing.T) {
63+
testCases := []struct {
64+
desc string
65+
domain string
66+
zone string
67+
}{
68+
{
69+
desc: "same domain",
70+
domain: "example.com",
71+
zone: "example.com",
72+
},
73+
{
74+
desc: "same domain, no FQDN zone",
75+
domain: "example.com.",
76+
zone: "example.com",
77+
},
78+
{
79+
desc: "same domain, no FQDN domain",
80+
domain: "example.com",
81+
zone: "example.com.",
82+
},
83+
{
84+
desc: "same domain, FQDN",
85+
domain: "example.com.",
86+
zone: "example.com.",
87+
},
88+
{
89+
desc: "zone and domain are unrelated",
90+
domain: "_acme-challenge.example.com",
91+
zone: "example.org",
92+
},
93+
}
94+
95+
for _, test := range testCases {
96+
test := test
97+
t.Run(test.desc, func(t *testing.T) {
98+
t.Parallel()
99+
100+
_, err := ExtractSubDomain(test.domain, test.zone)
101+
require.Error(t, err)
102+
})
103+
}
104+
}

providers/dns/alidns/alidns.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package alidns
44
import (
55
"errors"
66
"fmt"
7-
"strings"
87
"time"
98

109
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
@@ -269,9 +268,10 @@ func extractRecordName(fqdn, zone string) (string, error) {
269268
return "", fmt.Errorf("fail to convert punycode: %w", err)
270269
}
271270

272-
name := dns01.UnFqdn(fqdn)
273-
if idx := strings.Index(name, "."+asciiDomain); idx != -1 {
274-
return name[:idx], nil
271+
subDomain, err := dns01.ExtractSubDomain(fqdn, asciiDomain)
272+
if err != nil {
273+
return "", err
275274
}
276-
return name, nil
275+
276+
return subDomain, nil
277277
}

providers/dns/arvancloud/arvancloud.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8-
"strings"
98
"sync"
109
"time"
1110

@@ -114,9 +113,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
114113
return err
115114
}
116115

116+
subDomain, err := dns01.ExtractSubDomain(fqdn, authZone)
117+
if err != nil {
118+
return fmt.Errorf("arvancloud: %w", err)
119+
}
120+
117121
record := internal.DNSRecord{
118122
Type: "txt",
119-
Name: extractRecordName(fqdn, authZone),
123+
Name: subDomain,
120124
Value: internal.TXTRecordValue{Text: value},
121125
TTL: d.config.TTL,
122126
UpstreamHTTPS: "default",
@@ -176,11 +180,3 @@ func getZone(fqdn string) (string, error) {
176180

177181
return dns01.UnFqdn(authZone), nil
178182
}
179-
180-
func extractRecordName(fqdn, zone string) string {
181-
name := dns01.UnFqdn(fqdn)
182-
if idx := strings.Index(name, "."+zone); idx != -1 {
183-
return name[:idx]
184-
}
185-
return name
186-
}

providers/dns/civo/civo.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package civo
44
import (
55
"errors"
66
"fmt"
7-
"strings"
87
"time"
98

109
"github.com/civo/civogo"
@@ -104,8 +103,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
104103
return fmt.Errorf("civo: %w", err)
105104
}
106105

106+
subDomain, err := dns01.ExtractSubDomain(fqdn, zone)
107+
if err != nil {
108+
return fmt.Errorf("civo: %w", err)
109+
}
110+
107111
_, err = d.client.CreateDNSRecord(dnsDomain.ID, &civogo.DNSRecordConfig{
108-
Name: extractRecordName(fqdn, zone),
112+
Name: subDomain,
109113
Value: value,
110114
Type: civogo.DNSRecordTypeTXT,
111115
TTL: d.config.TTL,
@@ -136,9 +140,14 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
136140
return fmt.Errorf("civo: %w", err)
137141
}
138142

143+
subDomain, err := dns01.ExtractSubDomain(fqdn, zone)
144+
if err != nil {
145+
return fmt.Errorf("civo: %w", err)
146+
}
147+
139148
var dnsRecord civogo.DNSRecord
140149
for _, entry := range dnsRecords {
141-
if entry.Name == extractRecordName(fqdn, zone) && entry.Value == value {
150+
if entry.Name == subDomain && entry.Value == value {
142151
dnsRecord = entry
143152
break
144153
}
@@ -166,11 +175,3 @@ func getZone(fqdn string) (string, error) {
166175

167176
return dns01.UnFqdn(authZone), nil
168177
}
169-
170-
func extractRecordName(fqdn, zone string) string {
171-
name := dns01.UnFqdn(fqdn)
172-
if idx := strings.Index(name, "."+zone); idx != -1 {
173-
return name[:idx]
174-
}
175-
return name
176-
}

providers/dns/constellix/constellix.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
109109
return fmt.Errorf("constellix: failed to get domain (%s): %w", authZone, err)
110110
}
111111

112-
recordName := getRecordName(fqdn, authZone)
112+
recordName, err := dns01.ExtractSubDomain(fqdn, authZone)
113+
if err != nil {
114+
return fmt.Errorf("constellix: %w", err)
115+
}
113116

114117
records, err := d.client.TxtRecords.Search(dom.ID, internal.Exact, recordName)
115118
if err != nil {
@@ -147,7 +150,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
147150
return fmt.Errorf("constellix: failed to get domain (%s): %w", authZone, err)
148151
}
149152

150-
recordName := getRecordName(fqdn, authZone)
153+
recordName, err := dns01.ExtractSubDomain(fqdn, authZone)
154+
if err != nil {
155+
return fmt.Errorf("constellix: %w", err)
156+
}
151157

152158
records, err := d.client.TxtRecords.Search(dom.ID, internal.Exact, recordName)
153159
if err != nil {
@@ -262,7 +268,3 @@ func containsValue(record *internal.Record, value string) bool {
262268

263269
return false
264270
}
265-
266-
func getRecordName(fqdn, authZone string) string {
267-
return fqdn[0 : len(fqdn)-len(authZone)-1]
268-
}

providers/dns/desec/desec.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
109109
return fmt.Errorf("desec: could not find zone for domain %q and fqdn %q : %w", domain, fqdn, err)
110110
}
111111

112-
recordName := getRecordName(fqdn, authZone)
112+
recordName, err := dns01.ExtractSubDomain(fqdn, authZone)
113+
if err != nil {
114+
return fmt.Errorf("desec: %w", err)
115+
}
113116

114117
domainName := dns01.UnFqdn(authZone)
115118

@@ -156,7 +159,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
156159
return fmt.Errorf("desec: could not find zone for domain %q and fqdn %q : %w", domain, fqdn, err)
157160
}
158161

159-
recordName := getRecordName(fqdn, authZone)
162+
recordName, err := dns01.ExtractSubDomain(fqdn, authZone)
163+
if err != nil {
164+
return fmt.Errorf("desec: %w", err)
165+
}
160166

161167
domainName := dns01.UnFqdn(authZone)
162168

@@ -179,7 +185,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
179185

180186
return nil
181187
}
182-
183-
func getRecordName(fqdn, authZone string) string {
184-
return fqdn[0 : len(fqdn)-len(authZone)-1]
185-
}

providers/dns/dnsimple/dnsimple.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"errors"
77
"fmt"
88
"strconv"
9-
"strings"
109
"time"
1110

1211
"github.com/dnsimple/dnsimple-go/dnsimple"
@@ -103,7 +102,11 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
103102
return fmt.Errorf("dnsimple: %w", err)
104103
}
105104

106-
recordAttributes := newTxtRecord(zoneName, fqdn, value, d.config.TTL)
105+
recordAttributes, err := newTxtRecord(zoneName, fqdn, value, d.config.TTL)
106+
if err != nil {
107+
return fmt.Errorf("dnsimple: %w", err)
108+
}
109+
107110
_, err = d.client.Zones.CreateRecord(context.Background(), accountID, zoneName, recordAttributes)
108111
if err != nil {
109112
return fmt.Errorf("dnsimple: API call failed: %w", err)
@@ -186,33 +189,31 @@ func (d *DNSProvider) findTxtRecords(fqdn string) ([]dnsimple.ZoneRecord, error)
186189
return nil, err
187190
}
188191

189-
recordName := extractRecordName(fqdn, zoneName)
192+
subDomain, err := dns01.ExtractSubDomain(fqdn, zoneName)
193+
if err != nil {
194+
return nil, err
195+
}
190196

191-
result, err := d.client.Zones.ListRecords(context.Background(), accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: &recordName, Type: dnsimple.String("TXT"), ListOptions: dnsimple.ListOptions{}})
197+
result, err := d.client.Zones.ListRecords(context.Background(), accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: &subDomain, Type: dnsimple.String("TXT"), ListOptions: dnsimple.ListOptions{}})
192198
if err != nil {
193199
return nil, fmt.Errorf("API call has failed: %w", err)
194200
}
195201

196202
return result.Data, nil
197203
}
198204

199-
func newTxtRecord(zoneName, fqdn, value string, ttl int) dnsimple.ZoneRecordAttributes {
200-
name := extractRecordName(fqdn, zoneName)
205+
func newTxtRecord(zoneName, fqdn, value string, ttl int) (dnsimple.ZoneRecordAttributes, error) {
206+
subDomain, err := dns01.ExtractSubDomain(fqdn, zoneName)
207+
if err != nil {
208+
return dnsimple.ZoneRecordAttributes{}, err
209+
}
201210

202211
return dnsimple.ZoneRecordAttributes{
203212
Type: "TXT",
204-
Name: &name,
213+
Name: &subDomain,
205214
Content: value,
206215
TTL: ttl,
207-
}
208-
}
209-
210-
func extractRecordName(fqdn, zone string) string {
211-
name := dns01.UnFqdn(fqdn)
212-
if idx := strings.Index(name, "."+zone); idx != -1 {
213-
return name[:idx]
214-
}
215-
return name
216+
}, nil
216217
}
217218

218219
func (d *DNSProvider) getAccountID() (string, error) {

0 commit comments

Comments
 (0)