| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | package namecheap
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	"encoding/json"
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	"fmt"
 | 
					
						
							| 
									
										
										
										
											2017-04-10 19:26:19 -06:00
										 |  |  | 	"sort"
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	"strings"
 | 
					
						
							| 
									
										
										
										
											2017-10-16 11:21:33 -04:00
										 |  |  | 	"time"
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	"golang.org/x/net/publicsuffix"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	"github.com/StackExchange/dnscontrol/models"
 | 
					
						
							| 
									
										
										
										
											2018-10-08 13:10:44 -07:00
										 |  |  | 	"github.com/StackExchange/dnscontrol/pkg/printer"
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	"github.com/StackExchange/dnscontrol/providers"
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	"github.com/StackExchange/dnscontrol/providers/diff"
 | 
					
						
							| 
									
										
										
										
											2017-01-12 07:31:32 -07:00
										 |  |  | 	nc "github.com/billputer/go-namecheap"
 | 
					
						
							| 
									
										
										
										
											2018-02-05 16:17:20 -05:00
										 |  |  | 	"github.com/pkg/errors"
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 12:53:16 -05:00
										 |  |  | // NamecheapDefaultNs lists the default nameservers for this provider.
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | var NamecheapDefaultNs = []string{"dns1.registrar-servers.com", "dns2.registrar-servers.com"}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 12:53:16 -05:00
										 |  |  | // Namecheap is the handle for this provider.
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | type Namecheap struct {
 | 
					
						
							|  |  |  | 	ApiKey  string
 | 
					
						
							|  |  |  | 	ApiUser string
 | 
					
						
							|  |  |  | 	client  *nc.Client
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-04 19:19:35 -05:00
										 |  |  | var features = providers.DocumentationNotes{
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	providers.CanUseAlias:            providers.Cannot(),
 | 
					
						
							|  |  |  | 	providers.CanUseCAA:              providers.Cannot(),
 | 
					
						
							|  |  |  | 	providers.CanUsePTR:              providers.Cannot(),
 | 
					
						
							| 
									
										
										
										
											2018-01-04 19:19:35 -05:00
										 |  |  | 	providers.CanUseSRV:              providers.Cannot("The namecheap web console allows you to make SRV records, but their api does not let you read or set them"),
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	providers.CanUseTLSA:             providers.Cannot(),
 | 
					
						
							| 
									
										
										
										
											2018-01-04 19:19:35 -05:00
										 |  |  | 	providers.CantUseNOPURGE:         providers.Cannot(),
 | 
					
						
							|  |  |  | 	providers.DocCreateDomains:       providers.Cannot("Requires domain registered through their service"),
 | 
					
						
							|  |  |  | 	providers.DocDualHost:            providers.Cannot("Doesn't allow control of apex NS records"),
 | 
					
						
							|  |  |  | 	providers.DocOfficiallySupported: providers.Cannot(),
 | 
					
						
							| 
									
										
										
										
											2017-09-14 16:13:17 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | func init() {
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 	providers.RegisterRegistrarType("NAMECHEAP", newReg)
 | 
					
						
							| 
									
										
										
										
											2018-01-04 19:19:35 -05:00
										 |  |  | 	providers.RegisterDomainServiceProviderType("NAMECHEAP", newDsp, features)
 | 
					
						
							| 
									
										
										
										
											2017-10-23 12:54:31 -04:00
										 |  |  | 	providers.RegisterCustomRecordType("URL", "NAMECHEAP", "")
 | 
					
						
							|  |  |  | 	providers.RegisterCustomRecordType("URL301", "NAMECHEAP", "")
 | 
					
						
							|  |  |  | 	providers.RegisterCustomRecordType("FRAME", "NAMECHEAP", "")
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
 | 
					
						
							|  |  |  | 	return newProvider(conf, metadata)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newReg(conf map[string]string) (providers.Registrar, error) {
 | 
					
						
							|  |  |  | 	return newProvider(conf, nil)
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | func newProvider(m map[string]string, metadata json.RawMessage) (*Namecheap, error) {
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	api := &Namecheap{}
 | 
					
						
							|  |  |  | 	api.ApiUser, api.ApiKey = m["apiuser"], m["apikey"]
 | 
					
						
							|  |  |  | 	if api.ApiKey == "" || api.ApiUser == "" {
 | 
					
						
							| 
									
										
										
										
											2018-02-05 16:17:20 -05:00
										 |  |  | 		return nil, errors.Errorf("missing Namecheap apikey and apiuser")
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 	api.client = nc.NewClient(api.ApiUser, api.ApiKey, api.ApiUser)
 | 
					
						
							| 
									
										
										
										
											2017-04-25 11:24:55 -04:00
										 |  |  | 	// if BaseURL is specified in creds, use that url
 | 
					
						
							|  |  |  | 	BaseURL, ok := m["BaseURL"]
 | 
					
						
							|  |  |  | 	if ok {
 | 
					
						
							|  |  |  | 		api.client.BaseURL = BaseURL
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	return api, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | func splitDomain(domain string) (sld string, tld string) {
 | 
					
						
							|  |  |  | 	tld, _ = publicsuffix.PublicSuffix(domain)
 | 
					
						
							|  |  |  | 	d, _ := publicsuffix.EffectiveTLDPlusOne(domain)
 | 
					
						
							|  |  |  | 	sld = strings.Split(d, ".")[0]
 | 
					
						
							|  |  |  | 	return sld, tld
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // namecheap has request limiting at unpublished limits
 | 
					
						
							|  |  |  | // from support in SEP-2017:
 | 
					
						
							|  |  |  | //    "The limits for the API calls will be 20/Min, 700/Hour and 8000/Day for one user.
 | 
					
						
							|  |  |  | //     If you can limit the requests within these it should be fine."
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | // this helper performs some api action, checks for rate limited response, and if so, enters a retry loop until it resolves
 | 
					
						
							|  |  |  | // if you are consistently hitting this, you may have success asking their support to increase your account's limits.
 | 
					
						
							|  |  |  | func doWithRetry(f func() error) {
 | 
					
						
							|  |  |  | 	// sleep 5 seconds at a time, up to 23 times (1 minute, 15 seconds)
 | 
					
						
							|  |  |  | 	const maxRetries = 23
 | 
					
						
							|  |  |  | 	const sleepTime = 5 * time.Second
 | 
					
						
							|  |  |  | 	var currentRetry int
 | 
					
						
							|  |  |  | 	for {
 | 
					
						
							|  |  |  | 		err := f()
 | 
					
						
							|  |  |  | 		if err == nil {
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if strings.Contains(err.Error(), "Error 500000: Too many requests") {
 | 
					
						
							|  |  |  | 			currentRetry++
 | 
					
						
							|  |  |  | 			if currentRetry >= maxRetries {
 | 
					
						
							|  |  |  | 				return
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2018-10-08 13:10:44 -07:00
										 |  |  | 			printer.Printf("Namecheap rate limit exceeded. Waiting %s to retry.\n", sleepTime)
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 			time.Sleep(sleepTime)
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2017-10-23 11:15:22 -04:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 12:53:16 -05:00
										 |  |  | // GetDomainCorrections returns the corrections for the domain.
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | func (n *Namecheap) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
 | 
					
						
							|  |  |  | 	dc.Punycode()
 | 
					
						
							|  |  |  | 	sld, tld := splitDomain(dc.Name)
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 	var records *nc.DomainDNSGetHostsResult
 | 
					
						
							|  |  |  | 	var err error
 | 
					
						
							|  |  |  | 	doWithRetry(func() error {
 | 
					
						
							|  |  |  | 		records, err = n.client.DomainsDNSGetHosts(sld, tld)
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	})
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var actual []*models.RecordConfig
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// namecheap does not allow setting @ NS with basic DNS
 | 
					
						
							|  |  |  | 	dc.Filter(func(r *models.RecordConfig) bool {
 | 
					
						
							| 
									
										
										
										
											2018-03-19 17:18:58 -04:00
										 |  |  | 		if r.Type == "NS" && r.GetLabel() == "@" {
 | 
					
						
							|  |  |  | 			if !strings.HasSuffix(r.GetTargetField(), "registrar-servers.com.") {
 | 
					
						
							|  |  |  | 				fmt.Println("\n", r.GetTargetField(), "Namecheap does not support changing apex NS records. Skipping.")
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 			}
 | 
					
						
							|  |  |  | 			return false
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return true
 | 
					
						
							|  |  |  | 	})
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// namecheap has this really annoying feature where they add some parking records if you have no records.
 | 
					
						
							|  |  |  | 	// This causes a few problems for our purposes, specifically the integration tests.
 | 
					
						
							|  |  |  | 	// lets detect that one case and pretend it is a no-op.
 | 
					
						
							|  |  |  | 	if len(dc.Records) == 0 && len(records.Hosts) == 2 {
 | 
					
						
							|  |  |  | 		if records.Hosts[0].Type == "CNAME" &&
 | 
					
						
							|  |  |  | 			strings.Contains(records.Hosts[0].Address, "parkingpage") &&
 | 
					
						
							|  |  |  | 			records.Hosts[1].Type == "URL" {
 | 
					
						
							|  |  |  | 			return nil, nil
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, r := range records.Hosts {
 | 
					
						
							|  |  |  | 		if r.Type == "SOA" {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		rec := &models.RecordConfig{
 | 
					
						
							|  |  |  | 			Type:         r.Type,
 | 
					
						
							|  |  |  | 			TTL:          uint32(r.TTL),
 | 
					
						
							|  |  |  | 			MxPreference: uint16(r.MXPref),
 | 
					
						
							|  |  |  | 			Original:     r,
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2018-03-19 17:18:58 -04:00
										 |  |  | 		rec.SetLabel(r.Name, dc.Name)
 | 
					
						
							|  |  |  | 		rec.SetTarget(r.Address)
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 		actual = append(actual, rec)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-07 14:12:17 -08:00
										 |  |  | 	// Normalize
 | 
					
						
							| 
									
										
										
										
											2018-01-04 19:19:35 -05:00
										 |  |  | 	models.PostProcessRecords(actual)
 | 
					
						
							| 
									
										
										
										
											2017-11-07 14:12:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	differ := diff.New(dc)
 | 
					
						
							|  |  |  | 	_, create, delete, modify := differ.IncrementalDiff(actual)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// // because namecheap doesn't have selective create, delete, modify,
 | 
					
						
							|  |  |  | 	// // we bundle them all up to send at once.  We *do* want to see the
 | 
					
						
							|  |  |  | 	// // changes though
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var desc []string
 | 
					
						
							|  |  |  | 	for _, i := range create {
 | 
					
						
							|  |  |  | 		desc = append(desc, "\n"+i.String())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, i := range delete {
 | 
					
						
							|  |  |  | 		desc = append(desc, "\n"+i.String())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, i := range modify {
 | 
					
						
							|  |  |  | 		desc = append(desc, "\n"+i.String())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc)
 | 
					
						
							|  |  |  | 	corrections := []*models.Correction{}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// only create corrections if there are changes
 | 
					
						
							|  |  |  | 	if len(desc) > 0 {
 | 
					
						
							|  |  |  | 		corrections = append(corrections,
 | 
					
						
							|  |  |  | 			&models.Correction{
 | 
					
						
							|  |  |  | 				Msg: msg,
 | 
					
						
							|  |  |  | 				F: func() error {
 | 
					
						
							|  |  |  | 					return n.generateRecords(dc)
 | 
					
						
							|  |  |  | 				},
 | 
					
						
							|  |  |  | 			})
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return corrections, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (n *Namecheap) generateRecords(dc *models.DomainConfig) error {
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var recs []nc.DomainDNSHost
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	id := 1
 | 
					
						
							|  |  |  | 	for _, r := range dc.Records {
 | 
					
						
							|  |  |  | 		rec := nc.DomainDNSHost{
 | 
					
						
							|  |  |  | 			ID:      id,
 | 
					
						
							| 
									
										
										
										
											2018-03-19 17:18:58 -04:00
										 |  |  | 			Name:    r.GetLabel(),
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 			Type:    r.Type,
 | 
					
						
							| 
									
										
										
										
											2018-03-19 17:18:58 -04:00
										 |  |  | 			Address: r.GetTargetField(),
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 			MXPref:  int(r.MxPreference),
 | 
					
						
							|  |  |  | 			TTL:     int(r.TTL),
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		recs = append(recs, rec)
 | 
					
						
							|  |  |  | 		id++
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	sld, tld := splitDomain(dc.Name)
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 	var err error
 | 
					
						
							|  |  |  | 	doWithRetry(func() error {
 | 
					
						
							|  |  |  | 		_, err = n.client.DomainDNSSetHosts(sld, tld, recs)
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	})
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | 	return err
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 12:53:16 -05:00
										 |  |  | // GetNameservers returns the nameservers for a domain.
 | 
					
						
							| 
									
										
										
										
											2017-10-03 11:53:56 -04:00
										 |  |  | func (n *Namecheap) GetNameservers(domainName string) ([]*models.Nameserver, error) {
 | 
					
						
							|  |  |  | 	// return default namecheap nameservers
 | 
					
						
							|  |  |  | 	ns := NamecheapDefaultNs
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return models.StringsToNameservers(ns), nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 12:53:16 -05:00
										 |  |  | // GetRegistrarCorrections returns corrections to update nameservers.
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | func (n *Namecheap) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 	var info *nc.DomainInfo
 | 
					
						
							|  |  |  | 	var err error
 | 
					
						
							|  |  |  | 	doWithRetry(func() error {
 | 
					
						
							|  |  |  | 		info, err = n.client.DomainGetInfo(dc.Name)
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	})
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-04-10 19:26:19 -06:00
										 |  |  | 	sort.Strings(info.DNSDetails.Nameservers)
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	found := strings.Join(info.DNSDetails.Nameservers, ",")
 | 
					
						
							| 
									
										
										
										
											2017-04-10 19:26:19 -06:00
										 |  |  | 	desiredNs := []string{}
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	for _, d := range dc.Nameservers {
 | 
					
						
							| 
									
										
										
										
											2017-04-10 19:26:19 -06:00
										 |  |  | 		desiredNs = append(desiredNs, d.Name)
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-04-10 19:26:19 -06:00
										 |  |  | 	sort.Strings(desiredNs)
 | 
					
						
							|  |  |  | 	desired := strings.Join(desiredNs, ",")
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 	if found != desired {
 | 
					
						
							|  |  |  | 		parts := strings.SplitN(dc.Name, ".", 2)
 | 
					
						
							|  |  |  | 		sld, tld := parts[0], parts[1]
 | 
					
						
							|  |  |  | 		return []*models.Correction{
 | 
					
						
							| 
									
										
										
										
											2017-04-10 19:26:19 -06:00
										 |  |  | 			{
 | 
					
						
							|  |  |  | 				Msg: fmt.Sprintf("Change Nameservers from '%s' to '%s'", found, desired),
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 				F: func() (err error) {
 | 
					
						
							|  |  |  | 					doWithRetry(func() error {
 | 
					
						
							|  |  |  | 						_, err = n.client.DomainDNSSetCustom(sld, tld, desired)
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 						return err
 | 
					
						
							| 
									
										
										
										
											2017-10-24 09:41:59 -04:00
										 |  |  | 					})
 | 
					
						
							|  |  |  | 					return
 | 
					
						
							| 
									
										
										
										
											2016-08-22 18:31:50 -06:00
										 |  |  | 				}},
 | 
					
						
							|  |  |  | 		}, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return nil, nil
 | 
					
						
							|  |  |  | }
 |