2021-03-09 08:25:55 +08:00
|
|
|
package hostingde
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/StackExchange/dnscontrol/v3/models"
|
|
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
2022-12-12 04:02:58 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
|
2021-03-09 08:25:55 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v3/providers"
|
|
|
|
)
|
|
|
|
|
2022-02-22 23:54:02 +08:00
|
|
|
var defaultNameservers = []string{"ns1.hosting.de.", "ns2.hosting.de.", "ns3.hosting.de."}
|
2021-03-09 08:25:55 +08:00
|
|
|
|
|
|
|
var features = providers.DocumentationNotes{
|
|
|
|
providers.CanAutoDNSSEC: providers.Unimplemented("Supported but not implemented yet."),
|
|
|
|
providers.CanGetZones: providers.Can(),
|
|
|
|
providers.CanUseAlias: providers.Can(),
|
|
|
|
providers.CanUseCAA: providers.Can(),
|
|
|
|
providers.CanUseDS: providers.Can(),
|
|
|
|
providers.CanUseNAPTR: providers.Cannot(),
|
|
|
|
providers.CanUsePTR: providers.Can(),
|
|
|
|
providers.CanUseSRV: providers.Can(),
|
|
|
|
providers.CanUseSSHFP: providers.Can(),
|
|
|
|
providers.CanUseTLSA: providers.Can(),
|
|
|
|
providers.DocCreateDomains: providers.Can(),
|
|
|
|
providers.DocDualHost: providers.Can(),
|
|
|
|
providers.DocOfficiallySupported: providers.Cannot(),
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
providers.RegisterRegistrarType("HOSTINGDE", newHostingdeReg)
|
|
|
|
fns := providers.DspFuncs{
|
2021-05-05 02:15:31 +08:00
|
|
|
Initializer: newHostingdeDsp,
|
2021-03-09 09:14:30 +08:00
|
|
|
RecordAuditor: AuditRecords,
|
2021-03-09 08:25:55 +08:00
|
|
|
}
|
|
|
|
providers.RegisterDomainServiceProviderType("HOSTINGDE", fns, features)
|
|
|
|
}
|
|
|
|
|
2022-02-22 23:54:02 +08:00
|
|
|
type providerMeta struct {
|
|
|
|
DefaultNS []string `json:"default_ns"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHostingde(m map[string]string, providermeta json.RawMessage) (*hostingdeProvider, error) {
|
2021-03-09 08:25:55 +08:00
|
|
|
authToken, ownerAccountID, baseURL := m["authToken"], m["ownerAccountId"], m["baseURL"]
|
|
|
|
|
|
|
|
if authToken == "" {
|
|
|
|
return nil, fmt.Errorf("hosting.de: authtoken must be provided")
|
|
|
|
}
|
|
|
|
|
|
|
|
if baseURL == "" {
|
|
|
|
baseURL = "https://secure.hosting.de"
|
|
|
|
}
|
|
|
|
baseURL = strings.TrimSuffix(baseURL, "/")
|
|
|
|
|
|
|
|
hp := &hostingdeProvider{
|
|
|
|
authToken: authToken,
|
|
|
|
ownerAccountID: ownerAccountID,
|
|
|
|
baseURL: baseURL,
|
2022-02-22 23:54:02 +08:00
|
|
|
nameservers: defaultNameservers,
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(providermeta) > 0 {
|
|
|
|
var pm providerMeta
|
|
|
|
if err := json.Unmarshal(providermeta, &pm); err != nil {
|
|
|
|
return nil, fmt.Errorf("hosting.de: could not parse providermeta: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(pm.DefaultNS) > 0 {
|
|
|
|
hp.nameservers = pm.DefaultNS
|
|
|
|
}
|
2021-03-09 08:25:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return hp, nil
|
|
|
|
}
|
|
|
|
|
2022-02-22 23:54:02 +08:00
|
|
|
func newHostingdeDsp(m map[string]string, providermeta json.RawMessage) (providers.DNSServiceProvider, error) {
|
|
|
|
return newHostingde(m, providermeta)
|
2021-03-09 08:25:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func newHostingdeReg(m map[string]string) (providers.Registrar, error) {
|
2022-02-22 23:54:02 +08:00
|
|
|
return newHostingde(m, json.RawMessage{})
|
2021-03-09 08:25:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (hp *hostingdeProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
2022-02-22 23:54:02 +08:00
|
|
|
return models.ToNameserversStripTD(hp.nameservers)
|
2021-03-09 08:25:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (hp *hostingdeProvider) GetZoneRecords(domain string) (models.Records, error) {
|
|
|
|
src, err := hp.getRecords(domain)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
records := []*models.RecordConfig{}
|
|
|
|
for _, r := range src {
|
|
|
|
if r.Type == "SOA" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
records = append(records, r.nativeToRecord(domain))
|
|
|
|
}
|
|
|
|
|
|
|
|
return records, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
|
|
err := dc.Punycode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TTL must be between (inclusive) 1m and 1y (in fact, a little bit more)
|
|
|
|
for _, r := range dc.Records {
|
|
|
|
if r.TTL < 60 {
|
|
|
|
r.TTL = 60
|
|
|
|
}
|
|
|
|
if r.TTL > 31556926 {
|
|
|
|
r.TTL = 31556926
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
records, err := hp.GetZoneRecords(dc.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-12-12 04:02:58 +08:00
|
|
|
var corrections []*models.Correction
|
|
|
|
if !diff2.EnableDiff2 || true { // Remove "|| true" when diff2 version arrives
|
2021-03-09 08:25:55 +08:00
|
|
|
|
2022-12-12 04:02:58 +08:00
|
|
|
differ := diff.New(dc)
|
|
|
|
_, create, del, mod, err := differ.IncrementalDiff(records)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-09 08:25:55 +08:00
|
|
|
|
2022-12-12 04:02:58 +08:00
|
|
|
// NOPURGE
|
|
|
|
if dc.KeepUnknown {
|
|
|
|
del = []diff.Correlation{}
|
|
|
|
}
|
2021-03-09 08:25:55 +08:00
|
|
|
|
2022-12-12 04:02:58 +08:00
|
|
|
msg := []string{}
|
|
|
|
for _, c := range append(del, append(create, mod...)...) {
|
|
|
|
msg = append(msg, c.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(create) == 0 && len(del) == 0 && len(mod) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2021-03-09 08:25:55 +08:00
|
|
|
|
2022-12-12 04:02:58 +08:00
|
|
|
corrections := []*models.Correction{
|
|
|
|
{
|
|
|
|
Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")),
|
|
|
|
F: func() error {
|
|
|
|
return hp.updateRecords(dc.Name, create, del, mod)
|
|
|
|
},
|
2021-03-09 08:25:55 +08:00
|
|
|
},
|
2022-12-12 04:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return corrections, nil
|
2021-03-09 08:25:55 +08:00
|
|
|
}
|
|
|
|
|
2022-12-12 04:02:58 +08:00
|
|
|
// Insert Future diff2 version here.
|
|
|
|
|
2021-03-09 08:25:55 +08:00
|
|
|
return corrections, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hp *hostingdeProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
|
|
err := dc.Punycode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
found, err := hp.getNameservers(dc.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error getting nameservers: %w", err)
|
|
|
|
}
|
|
|
|
sort.Strings(found)
|
|
|
|
foundNameservers := strings.Join(found, ",")
|
|
|
|
|
|
|
|
expected := []string{}
|
|
|
|
for _, ns := range dc.Nameservers {
|
|
|
|
expected = append(expected, ns.Name)
|
|
|
|
}
|
|
|
|
sort.Strings(expected)
|
|
|
|
expectedNameservers := strings.Join(expected, ",")
|
|
|
|
|
|
|
|
// We don't care about glued records because we disallowed them
|
|
|
|
if foundNameservers != expectedNameservers {
|
|
|
|
return []*models.Correction{
|
|
|
|
{
|
|
|
|
Msg: fmt.Sprintf("Update nameservers %s -> %s", foundNameservers, expectedNameservers),
|
|
|
|
F: hp.updateNameservers(expected, dc.Name),
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
|
|
|
// TODO: Handle AutoDNSSEC
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hp *hostingdeProvider) EnsureDomainExists(domain string) error {
|
|
|
|
_, err := hp.getZoneConfig(domain)
|
|
|
|
if err == errZoneNotFound {
|
|
|
|
if err := hp.createZone(domain); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|