provider.go raw

   1  package westcn
   2  
   3  import (
   4  	"context"
   5  	"errors"
   6  	"fmt"
   7  	"net/http"
   8  	"net/url"
   9  	"sync"
  10  	"time"
  11  
  12  	"github.com/go-acme/lego/v4/challenge"
  13  	"github.com/go-acme/lego/v4/challenge/dns01"
  14  	"github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
  15  	"github.com/go-acme/lego/v4/providers/dns/internal/westcn/internal"
  16  )
  17  
  18  var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
  19  
  20  // Config is used to configure the creation of the DNSProvider.
  21  type Config struct {
  22  	Username string
  23  	Password string
  24  
  25  	PropagationTimeout time.Duration
  26  	PollingInterval    time.Duration
  27  	TTL                int
  28  	HTTPClient         *http.Client
  29  }
  30  
  31  // DNSProvider implements the challenge.Provider interface.
  32  type DNSProvider struct {
  33  	config *Config
  34  	client *internal.Client
  35  
  36  	recordIDs   map[string]int
  37  	recordIDsMu sync.Mutex
  38  }
  39  
  40  // NewDNSProviderConfig return a DNSProvider instance configured for West.cn/西部数码.
  41  func NewDNSProviderConfig(config *Config, baseURL string) (*DNSProvider, error) {
  42  	if config == nil {
  43  		return nil, errors.New("the configuration of the DNS provider is nil")
  44  	}
  45  
  46  	client, err := internal.NewClient(config.Username, config.Password)
  47  	if err != nil {
  48  		return nil, fmt.Errorf("%w", err)
  49  	}
  50  
  51  	if baseURL != "" {
  52  		client.BaseURL, err = url.Parse(baseURL)
  53  		if err != nil {
  54  			return nil, err
  55  		}
  56  	}
  57  
  58  	if config.HTTPClient != nil {
  59  		client.HTTPClient = config.HTTPClient
  60  	}
  61  
  62  	client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
  63  
  64  	return &DNSProvider{
  65  		config:    config,
  66  		client:    client,
  67  		recordIDs: make(map[string]int),
  68  	}, nil
  69  }
  70  
  71  // Present creates a TXT record using the specified parameters.
  72  func (d *DNSProvider) Present(domain, token, keyAuth string) error {
  73  	info := dns01.GetChallengeInfo(domain, keyAuth)
  74  
  75  	authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
  76  	if err != nil {
  77  		return fmt.Errorf("could not find zone for domain %q: %w", domain, err)
  78  	}
  79  
  80  	subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
  81  	if err != nil {
  82  		return fmt.Errorf("%w", err)
  83  	}
  84  
  85  	record := internal.Record{
  86  		Domain: dns01.UnFqdn(authZone),
  87  		Host:   subDomain,
  88  		Type:   "TXT",
  89  		Value:  info.Value,
  90  		TTL:    d.config.TTL,
  91  	}
  92  
  93  	recordID, err := d.client.AddRecord(context.Background(), record)
  94  	if err != nil {
  95  		return fmt.Errorf("add record: %w", err)
  96  	}
  97  
  98  	d.recordIDsMu.Lock()
  99  	d.recordIDs[token] = recordID
 100  	d.recordIDsMu.Unlock()
 101  
 102  	return nil
 103  }
 104  
 105  // CleanUp removes the TXT record matching the specified parameters.
 106  func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
 107  	info := dns01.GetChallengeInfo(domain, keyAuth)
 108  
 109  	authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
 110  	if err != nil {
 111  		return fmt.Errorf("could not find zone for domain %q: %w", domain, err)
 112  	}
 113  
 114  	// gets the record's unique ID
 115  	d.recordIDsMu.Lock()
 116  	recordID, ok := d.recordIDs[token]
 117  	d.recordIDsMu.Unlock()
 118  
 119  	if !ok {
 120  		return fmt.Errorf("unknown record ID for '%s' '%s'", info.EffectiveFQDN, token)
 121  	}
 122  
 123  	err = d.client.DeleteRecord(context.Background(), dns01.UnFqdn(authZone), recordID)
 124  	if err != nil {
 125  		return fmt.Errorf("delete record: %w", err)
 126  	}
 127  
 128  	// deletes record ID from map
 129  	d.recordIDsMu.Lock()
 130  	delete(d.recordIDs, token)
 131  	d.recordIDsMu.Unlock()
 132  
 133  	return nil
 134  }
 135  
 136  // Timeout returns the timeout and interval to use when checking for DNS propagation.
 137  // Adjusting here to cope with spikes in propagation times.
 138  func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
 139  	return d.config.PropagationTimeout, d.config.PollingInterval
 140  }
 141