From f5180ad521a3c3811eb0e0e723b09f1b942d7c04 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sat, 18 Jan 2020 05:25:50 +0400 Subject: [PATCH] namecheap: allow external domains (#1042) --- .golangci.toml | 2 +- providers/dns/namecheap/client.go | 33 ----------- providers/dns/namecheap/namecheap.go | 35 +++-------- providers/dns/namecheap/namecheap_test.go | 71 ++++++----------------- 4 files changed, 29 insertions(+), 112 deletions(-) diff --git a/.golangci.toml b/.golangci.toml index b6aed20b..0e4a8e73 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -90,7 +90,7 @@ text = "`envTest` is a global variable" [[issues.exclude-rules]] path = "providers/dns/namecheap/namecheap_test.go" - text = "`(tldsMock|testCases)` is a global variable" + text = "`testCases` is a global variable" [[issues.exclude-rules]] path = "providers/dns/acmedns/acmedns_test.go" text = "`(errorClientErr|errorStorageErr|egTestAccount)` is a global variable" diff --git a/providers/dns/namecheap/client.go b/providers/dns/namecheap/client.go index 85bec85f..1936f2c9 100644 --- a/providers/dns/namecheap/client.go +++ b/providers/dns/namecheap/client.go @@ -42,39 +42,6 @@ type getHostsResponse struct { Hosts []Record `xml:"CommandResponse>DomainDNSGetHostsResult>host"` } -type getTldsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Errors []apiError `xml:"Errors>Error"` - Result []struct { - Name string `xml:",attr"` - } `xml:"CommandResponse>Tlds>Tld"` -} - -// getTLDs requests the list of available TLDs. -// https://www.namecheap.com/support/api/methods/domains/get-tld-list.aspx -func (d *DNSProvider) getTLDs() (map[string]string, error) { - request, err := d.newRequestGet("namecheap.domains.getTldList") - if err != nil { - return nil, err - } - - var gtr getTldsResponse - err = d.do(request, >r) - if err != nil { - return nil, err - } - - if len(gtr.Errors) > 0 { - return nil, fmt.Errorf("%s [%d]", gtr.Errors[0].Description, gtr.Errors[0].Number) - } - - tlds := make(map[string]string) - for _, t := range gtr.Result { - tlds[t.Name] = t.Name - } - return tlds, nil -} - // getHosts reads the full list of DNS host records. // https://www.namecheap.com/support/api/methods/domains-dns/get-hosts.aspx func (d *DNSProvider) getHosts(sld, tld string) ([]Record, error) { diff --git a/providers/dns/namecheap/namecheap.go b/providers/dns/namecheap/namecheap.go index 3a5a4ad6..fd48e19a 100644 --- a/providers/dns/namecheap/namecheap.go +++ b/providers/dns/namecheap/namecheap.go @@ -13,6 +13,7 @@ import ( "github.com/go-acme/lego/v3/challenge/dns01" "github.com/go-acme/lego/v3/log" "github.com/go-acme/lego/v3/platform/config/env" + "golang.org/x/net/publicsuffix" ) // Notes about namecheap's tool API: @@ -129,12 +130,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present installs a TXT record for the DNS challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - tlds, err := d.getTLDs() - if err != nil { - return fmt.Errorf("namecheap: %v", err) - } - - ch, err := newChallenge(domain, keyAuth, tlds) + ch, err := newChallenge(domain, keyAuth) if err != nil { return fmt.Errorf("namecheap: %v", err) } @@ -169,12 +165,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes a TXT record used for a previous DNS challenge. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - tlds, err := d.getTLDs() - if err != nil { - return fmt.Errorf("namecheap: %v", err) - } - - ch, err := newChallenge(domain, keyAuth, tlds) + ch, err := newChallenge(domain, keyAuth) if err != nil { return fmt.Errorf("namecheap: %v", err) } @@ -226,25 +217,17 @@ func getClientIP(client *http.Client, debug bool) (addr string, err error) { return string(clientIP), nil } -// newChallenge builds a challenge record from a domain name, a challenge -// authentication key, and a map of available TLDs. -func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, error) { +// newChallenge builds a challenge record from a domain name and a challenge authentication key. +func newChallenge(domain, keyAuth string) (*challenge, error) { domain = dns01.UnFqdn(domain) - parts := strings.Split(domain, ".") - // Find the longest matching TLD. - longest := -1 - for i := len(parts); i > 0; i-- { - t := strings.Join(parts[i-1:], ".") - if _, found := tlds[t]; found { - longest = i - 1 - } - } - if longest < 1 { + tld, _ := publicsuffix.PublicSuffix(domain) + if tld == domain { return nil, fmt.Errorf("invalid domain name %q", domain) } - tld := strings.Join(parts[longest:], ".") + parts := strings.Split(domain, ".") + longest := len(parts) - strings.Count(tld, ".") - 1 sld := parts[longest-1] var host string diff --git a/providers/dns/namecheap/namecheap_test.go b/providers/dns/namecheap/namecheap_test.go index 67a49d89..015195d9 100644 --- a/providers/dns/namecheap/namecheap_test.go +++ b/providers/dns/namecheap/namecheap_test.go @@ -18,16 +18,6 @@ const ( envTestClientIP = "10.0.0.1" ) -var tldsMock = map[string]string{ - "com.au": "com.au", - "com": "com", - "co.uk": "co.uk", - "uk": "uk", - "edu": "edu", - "co.com": "co.com", - "za.com": "za.com", -} - func TestDNSProvider_getHosts(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { @@ -36,7 +26,7 @@ func TestDNSProvider_getHosts(t *testing.T) { provider := mockDNSProvider(mock.URL) - ch, err := newChallenge(test.domain, "", tldsMock) + ch, err := newChallenge(test.domain, "") require.NoError(t, err) hosts, err := provider.getHosts(ch.sld, ch.tld) @@ -77,7 +67,7 @@ func TestDNSProvider_setHosts(t *testing.T) { prov := mockDNSProvider(mock.URL) - ch, err := newChallenge(test.domain, "", tldsMock) + ch, err := newChallenge(test.domain, "") require.NoError(t, err) hosts, err := prov.getHosts(ch.sld, ch.tld) @@ -144,23 +134,22 @@ func TestDomainSplit(t *testing.T) { {domain: "test.co.com", valid: true, tld: "co.com", sld: "test"}, {domain: "www.test.com.au", valid: true, tld: "com.au", sld: "test", host: "www"}, {domain: "www.za.com", valid: true, tld: "za.com", sld: "www"}, + {domain: "my.test.tf", valid: true, tld: "tf", sld: "test", host: "my"}, {}, {domain: "a"}, {domain: "com"}, + {domain: "com.au"}, {domain: "co.com"}, {domain: "co.uk"}, - {domain: "test.au"}, + {domain: "tf"}, {domain: "za.com"}, - {domain: "www.za"}, - {domain: "www.test.au"}, - {domain: "www.test.unk"}, } for _, test := range tests { test := test t.Run(test.domain, func(t *testing.T) { valid := true - ch, err := newChallenge(test.domain, "", tldsMock) + ch, err := newChallenge(test.domain, "") if err != nil { valid = false } @@ -172,30 +161,26 @@ func TestDomainSplit(t *testing.T) { } if test.valid && valid { - assertEq(t, "domain", ch.domain, test.domain) - assertEq(t, "tld", ch.tld, test.tld) - assertEq(t, "sld", ch.sld, test.sld) - assertEq(t, "host", ch.host, test.host) + require.NotNil(t, ch) + assert.Equal(t, test.domain, ch.domain, "domain") + assert.Equal(t, test.tld, ch.tld, "tld") + assert.Equal(t, test.sld, ch.sld, "sld") + assert.Equal(t, test.host, ch.host, "host") } }) } } -func assertEq(t *testing.T, variable, got, want string) { - if got != want { - t.Errorf("Expected %s to be '%s' but got '%s'", variable, want, got) - } -} - func assertHdr(tc *testCase, t *testing.T, values *url.Values) { - ch, _ := newChallenge(tc.domain, "", tldsMock) + t.Helper() - assertEq(t, "ApiUser", values.Get("ApiUser"), envTestUser) - assertEq(t, "ApiKey", values.Get("ApiKey"), envTestKey) - assertEq(t, "UserName", values.Get("UserName"), envTestUser) - assertEq(t, "ClientIp", values.Get("ClientIp"), envTestClientIP) - assertEq(t, "SLD", values.Get("SLD"), ch.sld) - assertEq(t, "TLD", values.Get("TLD"), ch.tld) + ch, _ := newChallenge(tc.domain, "") + assert.Equal(t, envTestUser, values.Get("ApiUser"), "ApiUser") + assert.Equal(t, envTestKey, values.Get("ApiKey"), "ApiKey") + assert.Equal(t, envTestUser, values.Get("UserName"), "UserName") + assert.Equal(t, envTestClientIP, values.Get("ClientIp"), "ClientIp") + assert.Equal(t, ch.sld, values.Get("SLD"), "SLD") + assert.Equal(t, ch.tld, values.Get("TLD"), "TLD") } func mockServer(tc *testCase, t *testing.T) http.Handler { @@ -209,9 +194,6 @@ func mockServer(tc *testCase, t *testing.T) http.Handler { assertHdr(tc, t, &values) w.WriteHeader(http.StatusOK) fmt.Fprint(w, tc.getHostsResponse) - case "namecheap.domains.getTldList": - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, responseGetTlds) default: t.Errorf("Unexpected GET command: %s", cmd) } @@ -373,18 +355,3 @@ const responseGetHostsErrorBadAPIKey1 = ` --5:00 0 ` - -const responseGetTlds = ` - - - - namecheap.domains.getTldList - - - Most recognized top level domain - - - PHX01SBAPI01 - --5:00 - 0.004 -`