2018-07-03 10:44:04 +00:00
// Package duckdns Adds lego support for http://duckdns.org.
2018-04-06 15:04:03 +00:00
// See http://www.duckdns.org/spec.jsp for more info on updating TXT records.
package duckdns
import (
"errors"
"fmt"
"io/ioutil"
2018-09-17 13:16:03 +00:00
"net/http"
"time"
2018-04-06 15:04:03 +00:00
2018-05-31 07:30:04 +00:00
"github.com/xenolf/lego/acme"
2018-07-03 10:44:04 +00:00
"github.com/xenolf/lego/platform/config/env"
2018-04-06 15:04:03 +00:00
)
2018-09-17 13:16:03 +00:00
// Config is used to configure the creation of the DNSProvider
type Config struct {
Token string
PropagationTimeout time . Duration
PollingInterval time . Duration
HTTPClient * http . Client
}
// NewDefaultConfig returns a default configuration for the DNSProvider
func NewDefaultConfig ( ) * Config {
client := acme . HTTPClient
client . Timeout = env . GetOrDefaultSecond ( "DUCKDNS_HTTP_TIMEOUT" , 30 * time . Second )
return & Config {
PropagationTimeout : env . GetOrDefaultSecond ( "DUCKDNS_PROPAGATION_TIMEOUT" , acme . DefaultPropagationTimeout ) ,
PollingInterval : env . GetOrDefaultSecond ( "DUCKDNS_POLLING_INTERVAL" , acme . DefaultPollingInterval ) ,
HTTPClient : & client ,
}
}
2018-04-06 15:04:03 +00:00
// DNSProvider adds and removes the record for the DNS challenge
type DNSProvider struct {
2018-09-17 13:16:03 +00:00
config * Config
2018-04-06 15:04:03 +00:00
}
// NewDNSProvider returns a new DNS provider using
// environment variable DUCKDNS_TOKEN for adding and removing the DNS record.
func NewDNSProvider ( ) ( * DNSProvider , error ) {
2018-07-03 10:44:04 +00:00
values , err := env . Get ( "DUCKDNS_TOKEN" )
if err != nil {
2018-09-17 13:16:03 +00:00
return nil , fmt . Errorf ( "duckdns: %v" , err )
2018-07-03 10:44:04 +00:00
}
2018-04-06 15:04:03 +00:00
2018-09-17 13:16:03 +00:00
config := NewDefaultConfig ( )
config . Token = values [ "DUCKDNS_TOKEN" ]
return NewDNSProviderConfig ( config )
2018-04-06 15:04:03 +00:00
}
2018-09-17 13:16:03 +00:00
// NewDNSProviderCredentials uses the supplied credentials
// to return a DNSProvider instance configured for http://duckdns.org
// Deprecated
2018-07-23 15:30:03 +00:00
func NewDNSProviderCredentials ( token string ) ( * DNSProvider , error ) {
2018-09-17 13:16:03 +00:00
config := NewDefaultConfig ( )
config . Token = token
return NewDNSProviderConfig ( config )
}
// NewDNSProviderConfig return a DNSProvider instance configured for DuckDNS.
func NewDNSProviderConfig ( config * Config ) ( * DNSProvider , error ) {
if config == nil {
return nil , errors . New ( "duckdns: the configuration of the DNS provider is nil" )
2018-04-06 15:04:03 +00:00
}
2018-09-17 13:16:03 +00:00
if config . Token == "" {
return nil , errors . New ( "duckdns: credentials missing" )
}
return & DNSProvider { config : config } , nil
2018-04-06 15:04:03 +00:00
}
2018-07-23 15:30:03 +00:00
// Present creates a TXT record to fulfil the dns-01 challenge.
func ( d * DNSProvider ) Present ( domain , token , keyAuth string ) error {
_ , txtRecord , _ := acme . DNS01Record ( domain , keyAuth )
2018-09-17 13:16:03 +00:00
return updateTxtRecord ( domain , d . config . Token , txtRecord , false )
2018-07-23 15:30:03 +00:00
}
// CleanUp clears DuckDNS TXT record
func ( d * DNSProvider ) CleanUp ( domain , token , keyAuth string ) error {
2018-09-17 13:16:03 +00:00
return updateTxtRecord ( domain , d . config . Token , "" , true )
}
// Timeout returns the timeout and interval to use when checking for DNS propagation.
// Adjusting here to cope with spikes in propagation times.
func ( d * DNSProvider ) Timeout ( ) ( timeout , interval time . Duration ) {
return d . config . PropagationTimeout , d . config . PollingInterval
2018-04-06 15:04:03 +00:00
}
2018-07-23 15:30:03 +00:00
// updateTxtRecord Update the domains TXT record
// To update the TXT record we just need to make one simple get request.
// In DuckDNS you only have one TXT record shared with the domain and all sub domains.
func updateTxtRecord ( domain , token , txt string , clear bool ) error {
u := fmt . Sprintf ( "https://www.duckdns.org/update?domains=%s&token=%s&clear=%t&txt=%s" , domain , token , clear , txt )
response , err := acme . HTTPClient . Get ( u )
2018-04-06 15:04:03 +00:00
if err != nil {
return err
}
defer response . Body . Close ( )
bodyBytes , err := ioutil . ReadAll ( response . Body )
if err != nil {
return err
}
2018-07-23 15:30:03 +00:00
2018-04-06 15:04:03 +00:00
body := string ( bodyBytes )
if body != "OK" {
2018-07-23 15:30:03 +00:00
return fmt . Errorf ( "request to change TXT record for DuckDNS returned the following result (%s) this does not match expectation (OK) used url [%s]" , body , u )
2018-04-06 15:04:03 +00:00
}
return nil
}