From 255e08cff2c64b6e1a871c456fdeccc64799780c Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 14 Sep 2023 18:17:02 +0100 Subject: [PATCH] PORKBUN: Add registrar support (#2542) Co-authored-by: Tom Limoncelli --- documentation/providers.md | 2 +- providers/porkbun/api.go | 39 ++++++++++++++++++++---- providers/porkbun/porkbunProvider.go | 44 ++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/documentation/providers.md b/documentation/providers.md index d9a37484fd..daa8edce84 100644 --- a/documentation/providers.md +++ b/documentation/providers.md @@ -53,7 +53,7 @@ If a feature is definitively not supported for whatever reason, we would also li | [`ORACLE`](providers/oracle.md) | ❌ | ✅ | ❌ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | | [`OVH`](providers/ovh.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ❔ | ❔ | ❔ | ❌ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ❌ | ✅ | ✅ | | [`PACKETFRAME`](providers/packetframe.md) | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ❔ | -| [`PORKBUN`](providers/porkbun.md) | ❌ | ✅ | ❌ | ✅ | ❔ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | +| [`PORKBUN`](providers/porkbun.md) | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | [`POWERDNS`](providers/powerdns.md) | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`ROUTE53`](providers/route53.md) | ✅ | ✅ | ✅ | ❌ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | ✅ | | [`RWTH`](providers/rwth.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ✅ | ❌ | ❔ | ❌ | ❌ | ✅ | ✅ | diff --git a/providers/porkbun/api.go b/providers/porkbun/api.go index 9c083f9263..3145319af8 100644 --- a/providers/porkbun/api.go +++ b/providers/porkbun/api.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "sort" "time" ) @@ -18,7 +19,7 @@ type porkbunProvider struct { secretKey string } -type requestParams map[string]string +type requestParams map[string]any type errorResponse struct { Status string `json:"status"` @@ -40,6 +41,11 @@ type recordResponse struct { Records []domainRecord `json:"records"` } +type nsResponse struct { + Status string `json:"status"` + Nameservers []string `json:"ns"` +} + func (c *porkbunProvider) post(endpoint string, params requestParams) ([]byte, error) { params["apikey"] = c.apiKey params["secretapikey"] = c.secretKey @@ -82,7 +88,7 @@ func (c *porkbunProvider) ping() error { func (c *porkbunProvider) createRecord(domain string, rec requestParams) error { if _, err := c.post("/dns/create/"+domain, rec); err != nil { - return fmt.Errorf("failed create record (porkbun): %s", err) + return fmt.Errorf("failed create record (porkbun): %w", err) } return nil } @@ -90,14 +96,14 @@ func (c *porkbunProvider) createRecord(domain string, rec requestParams) error { func (c *porkbunProvider) deleteRecord(domain string, recordID string) error { params := requestParams{} if _, err := c.post(fmt.Sprintf("/dns/delete/%s/%s", domain, recordID), params); err != nil { - return fmt.Errorf("failed delete record (porkbun): %s", err) + return fmt.Errorf("failed delete record (porkbun): %w", err) } return nil } func (c *porkbunProvider) modifyRecord(domain string, recordID string, rec requestParams) error { if _, err := c.post(fmt.Sprintf("/dns/edit/%s/%s", domain, recordID), rec); err != nil { - return fmt.Errorf("failed update (porkbun): %s", err) + return fmt.Errorf("failed update (porkbun): %w", err) } return nil } @@ -106,7 +112,7 @@ func (c *porkbunProvider) getRecords(domain string) ([]domainRecord, error) { params := requestParams{} var bodyString, err = c.post("/dns/retrieve/"+domain, params) if err != nil { - return nil, fmt.Errorf("failed fetching record list from porkbun: %s", err) + return nil, fmt.Errorf("failed fetching record list from porkbun: %w", err) } var dr recordResponse @@ -121,3 +127,26 @@ func (c *porkbunProvider) getRecords(domain string) ([]domainRecord, error) { } return records, nil } + +func (c *porkbunProvider) getNameservers(domain string) ([]string, error) { + params := requestParams{} + var bodyString, err = c.post(fmt.Sprintf("/domain/getNs/%s", domain), params) + if err != nil { + return nil, fmt.Errorf("failed fetching nameserver list from porkbun: %w", err) + } + + var ns nsResponse + json.Unmarshal(bodyString, &ns) + + sort.Strings(ns.Nameservers) + return ns.Nameservers, nil +} + +func (c *porkbunProvider) updateNameservers(ns []string, domain string) error { + params := requestParams{} + params["ns"] = ns + if _, err := c.post(fmt.Sprintf("/domain/updateNs/%s", domain), params); err != nil { + return fmt.Errorf("failed NS update (porkbun): %w", err) + } + return nil +} diff --git a/providers/porkbun/porkbunProvider.go b/providers/porkbun/porkbunProvider.go index 1825a7550a..0eddd1c9ff 100644 --- a/providers/porkbun/porkbunProvider.go +++ b/providers/porkbun/porkbunProvider.go @@ -3,6 +3,7 @@ package porkbun import ( "encoding/json" "fmt" + "sort" "strconv" "strings" @@ -25,8 +26,16 @@ var defaultNS = []string{ "salvador.ns.porkbun.com", } -// NewPorkbun creates the provider. -func NewPorkbun(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { +func newReg(conf map[string]string) (providers.Registrar, error) { + return newPorkbun(conf, nil) +} + +func newDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { + return newPorkbun(conf, metadata) +} + +// newPorkbun creates the provider. +func newPorkbun(m map[string]string, metadata json.RawMessage) (*porkbunProvider, error) { c := &porkbunProvider{} c.apiKey, c.secretKey = m["api_key"], m["secret_key"] @@ -63,8 +72,9 @@ var features = providers.DocumentationNotes{ } func init() { + providers.RegisterRegistrarType("PORKBUN", newReg) fns := providers.DspFuncs{ - Initializer: NewPorkbun, + Initializer: newDsp, RecordAuditor: AuditRecords, } providers.RegisterDomainServiceProviderType("PORKBUN", fns, features) @@ -317,3 +327,31 @@ func fixTTL(ttl uint32) uint32 { } return minimumTTL } + +func (c *porkbunProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { + nss, err := c.getNameservers(dc.Name) + if err != nil { + return nil, err + } + foundNameservers := strings.Join(nss, ",") + + expected := []string{} + for _, ns := range dc.Nameservers { + expected = append(expected, ns.Name) + } + sort.Strings(expected) + expectedNameservers := strings.Join(expected, ",") + + if foundNameservers == expectedNameservers { + return nil, nil + } + + return []*models.Correction{ + { + Msg: fmt.Sprintf("Update nameservers %s -> %s", foundNameservers, expectedNameservers), + F: func() error { + return c.updateNameservers(expected, dc.Name) + }, + }, + }, nil +}