diff --git a/providers/ovh/ovhProvider.go b/providers/ovh/ovhProvider.go index b8b756340..b73c74d5d 100644 --- a/providers/ovh/ovhProvider.go +++ b/providers/ovh/ovhProvider.go @@ -130,6 +130,21 @@ func (c *ovhProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.C models.PostProcessRecords(actual) txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records + // OVH handles DKIM keys differently, no matter if sent to API as TXT or DKIM record type. The OVH expects the DKIM + // string to be in one single string. That's why we'll need to un-split the DKIM TXT records. If not, dnscontrol + // will always detect a mismatch between the unsplitted string from API to the splitted one in dnscontrol. + for _, rc := range dc.Records { + if c.isDKIMRecord(rc) { + // When DKIM records are updated, OVH does special logic in their backend, most likely validating the string. + // Splitting the string causes the validation on the API backend to fail with error message: + // "FAILURE! Error 400: "Invalid subfield found in DKIM : \"v=DKIM1"" + // Therefore, we merge the text string before we compare or send it to the API. + unsplittedTarget := strings.Join(rc.TxtStrings, "") + rc.SetTarget(unsplittedTarget) + rc.SetTargetTXTString(unsplittedTarget) + } + } + differ := diff.New(dc) _, create, delete, modify, err := differ.IncrementalDiff(actual) if err != nil { diff --git a/providers/ovh/protocol.go b/providers/ovh/protocol.go index 91228ebea..fed9e0da7 100644 --- a/providers/ovh/protocol.go +++ b/providers/ovh/protocol.go @@ -2,10 +2,9 @@ package ovh import ( "fmt" - "strings" - "github.com/StackExchange/dnscontrol/v3/models" "github.com/miekg/dns/dnsutil" + "strings" ) // Void an empty structure. @@ -111,9 +110,6 @@ func (c *ovhProvider) deleteRecordFunc(id int64, fqdn string) func() error { // Returns a function that can be invoked to create a record in a zone. func (c *ovhProvider) createRecordFunc(rc *models.RecordConfig, fqdn string) func() error { return func() error { - if c.isDKIMRecord(rc) { - rc.Type = "DKIM" - } record := Record{ SubDomain: dnsutil.TrimDomainName(rc.GetLabelFQDN(), fqdn), FieldType: rc.Type, @@ -132,9 +128,6 @@ func (c *ovhProvider) createRecordFunc(rc *models.RecordConfig, fqdn string) fun // Returns a function that can be invoked to update a record in a zone. func (c *ovhProvider) updateRecordFunc(old *Record, rc *models.RecordConfig, fqdn string) func() error { return func() error { - if c.isDKIMRecord(rc) { - rc.Type = "DKIM" - } record := Record{ SubDomain: rc.GetLabel(), FieldType: rc.Type, @@ -147,10 +140,13 @@ func (c *ovhProvider) updateRecordFunc(old *Record, rc *models.RecordConfig, fqd record.SubDomain = "" } - err := c.client.CallAPI("PUT", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, old.ID), &record, &Void{}, true) - if err != nil && rc.Type == "DKIM" && strings.Contains(err.Error(), "alter read-only properties: fieldType") { - err = fmt.Errorf("this usually occurs when DKIM value is longer than the TXT record limit what OVH allows. Delete the TXT record to get past this limitation. [Original error: %s]", err.Error()) + // We do this last just right before the final API call + if c.isDKIMRecord(rc) { + // When DKIM value is longer than 255, the MODIFY fails with "Try to alter read-only properties: fieldType" + // Setting FieldType to empty string results in the property not being altered, hence error does not occur. + record.FieldType = "" } + err := c.client.CallAPI("PUT", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, old.ID), &record, &Void{}, true) return err } @@ -161,6 +157,7 @@ func (c *ovhProvider) isDKIMRecord(rc *models.RecordConfig) bool { return (rc != nil && rc.Type == "TXT" && strings.Contains(rc.GetLabel(), "._domainkey")) } +// refreshZone initiates a refresh task on OVHs backend func (c *ovhProvider) refreshZone(fqdn string) error { return c.client.CallAPI("POST", fmt.Sprintf("/domain/zone/%s/refresh", fqdn), nil, &Void{}, true) }