mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
New provider and new registrar: hosting.de (#1041)
* Add http.net provider * Rename httpnetProvider * Add SSHFP capability * Add paging for records * Sort documentation notes alphabetically * Add custom base URL * Extend documentation for custom base URL * - renamed to hosting.de - Fix EnsureDomainExists - GetNameservers read from NS Records * Replaced http.net with hosting.de Contributor Support from hosting.de * baseURL for hosting.de in documentation replaced %v with %w for errors special handling for txt records using .TxtStrings * removed last references to rc.Target fixed Trim of last dot * Re-engineer TXT records for simplicity and better compliance (#1063) Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com> Co-authored-by: Oliver Dick <o.dick@hosting.de> Co-authored-by: Oliver Dick <31733320+membero@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/hedns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/hetzner"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/hexonet"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/hostingde"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/internetbs"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/inwx"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/linode"
|
||||
|
273
providers/hostingde/api.go
Normal file
273
providers/hostingde/api.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package hostingde
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const endpoint = "%s/api/%s/v1/json/%s"
|
||||
|
||||
type hostingdeProvider struct {
|
||||
authToken string
|
||||
ownerAccountID string
|
||||
baseURL string
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) getDomainConfig(domain string) (*domainConfig, error) {
|
||||
zc, err := hp.getZoneConfig(domain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting zone config: %w", err)
|
||||
}
|
||||
|
||||
params := request{
|
||||
Filter: filter{
|
||||
Field: "domainName",
|
||||
Value: zc.Name,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := hp.get("domain", "domainsFind", params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting domain info: %w", err)
|
||||
}
|
||||
|
||||
domainConf := []*domainConfig{}
|
||||
if err := json.Unmarshal(resp.Data, &domainConf); err != nil {
|
||||
return nil, fmt.Errorf("error parsing response: %w", err)
|
||||
}
|
||||
|
||||
if len(domainConf) == 0 {
|
||||
return nil, fmt.Errorf("could not get domain config: %s", domain)
|
||||
}
|
||||
|
||||
return domainConf[0], nil
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) createZone(domain string) error {
|
||||
t, err := idna.ToASCII(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
records := []*record{}
|
||||
for _, ns := range defaultNameservers {
|
||||
records = append(records, &record{
|
||||
Name: domain,
|
||||
Type: "NS",
|
||||
Content: ns,
|
||||
TTL: 86400,
|
||||
})
|
||||
}
|
||||
|
||||
params := request{
|
||||
ZoneConfig: &zoneConfig{
|
||||
Name: t,
|
||||
Type: "NATIVE",
|
||||
},
|
||||
Records: records,
|
||||
}
|
||||
|
||||
_, err = hp.get("dns", "zoneCreate", params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating zone: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) getNameservers(domain string) ([]string, error) {
|
||||
t, err := idna.ToASCII(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainConf, err := hp.getDomainConfig(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting domain config: %w", err)
|
||||
}
|
||||
|
||||
nss := []string{}
|
||||
for _, ns := range domainConf.Nameservers {
|
||||
// Currently does not support glued IP addresses
|
||||
if len(ns.IPs) > 0 {
|
||||
return nil, fmt.Errorf("domain %s has glued IP addresses which are not supported", domain)
|
||||
}
|
||||
|
||||
nss = append(nss, ns.Name)
|
||||
}
|
||||
|
||||
return nss, nil
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) updateNameservers(nss []string, domain string) func() error {
|
||||
return func() error {
|
||||
domainConf, err := hp.getDomainConfig(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nameservers := []nameserver{}
|
||||
for _, ns := range nss {
|
||||
nameservers = append(nameservers, nameserver{Name: ns})
|
||||
}
|
||||
|
||||
domainConf.Nameservers = nameservers
|
||||
|
||||
params := request{
|
||||
Domain: domainConf,
|
||||
}
|
||||
|
||||
if _, err := hp.get("domain", "domainUpdate", params); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) getRecords(domain string) ([]*record, error) {
|
||||
zc, err := hp.getZoneConfig(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records := []*record{}
|
||||
page := uint(1)
|
||||
for {
|
||||
params := request{
|
||||
Filter: filter{
|
||||
Field: "ZoneConfigId",
|
||||
Value: zc.ID,
|
||||
},
|
||||
Limit: 1000,
|
||||
Page: page,
|
||||
}
|
||||
|
||||
resp, err := hp.get("dns", "recordsFind", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newRecords := []*record{}
|
||||
if err := json.Unmarshal(resp.Data, &newRecords); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, newRecords...)
|
||||
|
||||
if page >= resp.TotalPages {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) updateRecords(domain string, create, del, mod diff.Changeset) error {
|
||||
zc, err := hp.getZoneConfig(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
toAdd := []*record{}
|
||||
for _, c := range create {
|
||||
r := recordToNative(c.Desired)
|
||||
toAdd = append(toAdd, r)
|
||||
}
|
||||
|
||||
toDelete := []*record{}
|
||||
for _, d := range del {
|
||||
r := recordToNative(d.Existing)
|
||||
r.ID = d.Existing.Original.(*record).ID
|
||||
toDelete = append(toDelete, r)
|
||||
}
|
||||
|
||||
toModify := []*record{}
|
||||
for _, m := range mod {
|
||||
r := recordToNative(m.Desired)
|
||||
r.ID = m.Existing.Original.(*record).ID
|
||||
toModify = append(toModify, r)
|
||||
}
|
||||
|
||||
params := request{
|
||||
ZoneConfig: zc,
|
||||
RecordsToAdd: toAdd,
|
||||
RecordsToDelete: toDelete,
|
||||
RecordsToModify: toModify,
|
||||
}
|
||||
|
||||
_, err = hp.get("dns", "zoneUpdate", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) getZoneConfig(domain string) (*zoneConfig, error) {
|
||||
t, err := idna.ToASCII(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := request{
|
||||
Filter: filter{
|
||||
Field: "ZoneName",
|
||||
Value: t,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := hp.get("dns", "zoneConfigsFind", params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get zone config: %w", err)
|
||||
}
|
||||
|
||||
zc := []*zoneConfig{}
|
||||
if err := json.Unmarshal(resp.Data, &zc); err != nil {
|
||||
return nil, fmt.Errorf("could not parse response: %w", err)
|
||||
}
|
||||
|
||||
if len(zc) == 0 {
|
||||
return nil, errZoneNotFound
|
||||
}
|
||||
|
||||
return zc[0], nil
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) get(service, method string, params request) (*responseData, error) {
|
||||
params.AuthToken = hp.authToken
|
||||
params.OwnerAccountID = hp.ownerAccountID
|
||||
reqBody, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal request body: %w", err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(endpoint, hp.baseURL, service, method)
|
||||
resp, err := http.Post(url, "application/json", bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not carry out request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("error occurred: %s", resp.Status)
|
||||
}
|
||||
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read response body: %w", err)
|
||||
}
|
||||
|
||||
respData := &response{}
|
||||
if err := json.Unmarshal(bodyBytes, &respData); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal response body: %w", err)
|
||||
}
|
||||
if len(respData.Errors) > 0 && respData.Status == "error" {
|
||||
return nil, fmt.Errorf("%+v", respData.Errors)
|
||||
}
|
||||
|
||||
return respData.Response, nil
|
||||
}
|
11
providers/hostingde/auditrecords.go
Normal file
11
providers/hostingde/auditrecords.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package hostingde
|
||||
|
||||
import (
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
)
|
||||
|
||||
// AuditRecords returns an error if any records are not
|
||||
// supportable by this provider.
|
||||
func AuditRecords(records []*models.RecordConfig) error {
|
||||
return nil
|
||||
}
|
201
providers/hostingde/hostingdeProvider.go
Normal file
201
providers/hostingde/hostingdeProvider.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package hostingde
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
||||
"github.com/StackExchange/dnscontrol/v3/providers"
|
||||
)
|
||||
|
||||
var defaultNameservers = []string{"ns1.hosting.de", "ns2.hosting.de", "ns3.hosting.de"}
|
||||
|
||||
var features = providers.DocumentationNotes{
|
||||
providers.CanAutoDNSSEC: providers.Unimplemented("Supported but not implemented yet."),
|
||||
providers.CanGetZones: providers.Can(),
|
||||
providers.CanUseAlias: providers.Can(),
|
||||
providers.CanUseAzureAlias: providers.Cannot(),
|
||||
providers.CanUseCAA: providers.Can(),
|
||||
providers.CanUseDS: providers.Can(),
|
||||
providers.CanUseNAPTR: providers.Cannot(),
|
||||
providers.CanUsePTR: providers.Can(),
|
||||
providers.CanUseRoute53Alias: providers.Cannot(),
|
||||
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{
|
||||
Initializer: newHostingdeDsp,
|
||||
AuditRecordsor: AuditRecords,
|
||||
}
|
||||
providers.RegisterDomainServiceProviderType("HOSTINGDE", fns, features)
|
||||
}
|
||||
|
||||
func newHostingde(m map[string]string) (*hostingdeProvider, error) {
|
||||
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,
|
||||
}
|
||||
|
||||
return hp, nil
|
||||
}
|
||||
|
||||
func newHostingdeDsp(m map[string]string, raw json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||
return newHostingde(m)
|
||||
}
|
||||
|
||||
func newHostingdeReg(m map[string]string) (providers.Registrar, error) {
|
||||
return newHostingde(m)
|
||||
}
|
||||
|
||||
func (hp *hostingdeProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||
src, err := hp.getRecords(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nameservers []string
|
||||
for _, record := range src {
|
||||
if record.Type == "NS" {
|
||||
nameservers = append(nameservers, record.Content)
|
||||
}
|
||||
}
|
||||
|
||||
return models.ToNameservers(nameservers)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
differ := diff.New(dc)
|
||||
_, create, del, mod, err := differ.IncrementalDiff(records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NOPURGE
|
||||
if dc.KeepUnknown {
|
||||
del = []diff.Correlation{}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
corrections := []*models.Correction{
|
||||
{
|
||||
Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")),
|
||||
F: func() error {
|
||||
return hp.updateRecords(dc.Name, create, del, mod)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
}
|
182
providers/hostingde/types.go
Normal file
182
providers/hostingde/types.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package hostingde
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errZoneNotFound = errors.Errorf("zone not found")
|
||||
)
|
||||
|
||||
type request struct {
|
||||
AuthToken string `json:"authToken"`
|
||||
OwnerAccountID string `json:"ownerAccountId,omitempty"`
|
||||
Filter filter `json:"filter,omitempty"`
|
||||
Limit uint `json:"limit,omitempty"`
|
||||
Page uint `json:"page,omitempty"`
|
||||
|
||||
// Update Zone
|
||||
ZoneConfig *zoneConfig `json:"zoneConfig"`
|
||||
RecordsToAdd []*record `json:"recordsToAdd"`
|
||||
RecordsToModify []*record `json:"recordsToModify"`
|
||||
RecordsToDelete []*record `json:"recordsToDelete"`
|
||||
|
||||
// Create Zone
|
||||
Records []*record `json:"records"`
|
||||
|
||||
// Domain
|
||||
Domain *domainConfig `json:"domain"`
|
||||
}
|
||||
|
||||
type filter struct {
|
||||
Field string `json:"field"`
|
||||
Value string `json:"value"`
|
||||
Relation string `json:"relation,omitempty"`
|
||||
}
|
||||
|
||||
type nameserver struct {
|
||||
Name string `json:"name"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
type domainConfig struct {
|
||||
Name string `json:"name"`
|
||||
Contacts json.RawMessage `json:"contacts"`
|
||||
Nameservers []nameserver `json:"nameservers"`
|
||||
TransferLockEnabled bool `json:"transferLockEnabled"`
|
||||
}
|
||||
|
||||
type zoneConfig struct {
|
||||
ID string `json:"id"`
|
||||
DNSSECMode string `json:"dnsSecMode"`
|
||||
EmailAddress string `json:"emailAddress,omitempty"`
|
||||
MasterIP string `json:"masterIp"`
|
||||
Name string `json:"name"` // Not required per docs, but required IRL
|
||||
NameUnicode string `json:"nameUnicode"`
|
||||
// SOAValues struct {
|
||||
// Refresh uint32 `json:"refresh"`
|
||||
// Retry uint32 `json:"retry"`
|
||||
// Expire uint32 `json:"expire"`
|
||||
// TTL uint32 `json:"ttl"`
|
||||
// NegativeTTL uint32 `json:"negativeTtl"`
|
||||
// } `json:"soaValues,omitempty"`
|
||||
Type string `json:"type"`
|
||||
ZoneTransferWhitelist []string `json:"zoneTransferWhitelist"`
|
||||
}
|
||||
|
||||
type record struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
TTL uint32 `json:"ttl"`
|
||||
Priority uint16 `json:"priority"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Errors []apiError `json:"errors"`
|
||||
Response *responseData `json:"response"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
Code int `json:"code"`
|
||||
ContextObject string `json:"contextObject"`
|
||||
ContextPath string `json:"contextPath"`
|
||||
Text string `json:"text"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type responseData struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
Type string `json:"type"`
|
||||
|
||||
Limit uint `json:"limit"`
|
||||
Page uint `json:"page"`
|
||||
TotalPages uint `json:"totalPages"`
|
||||
}
|
||||
|
||||
func (r *record) nativeToRecord(domain string) *models.RecordConfig {
|
||||
// normalize cname,mx,ns records with dots to be consistent with our config format.
|
||||
if r.Type == "ALIAS" || r.Type == "CNAME" || r.Type == "MX" || r.Type == "NS" || r.Type == "SRV" {
|
||||
if r.Content != "." {
|
||||
r.Content = r.Content + "."
|
||||
}
|
||||
}
|
||||
|
||||
rc := &models.RecordConfig{
|
||||
Type: "",
|
||||
TTL: r.TTL,
|
||||
MxPreference: r.Priority,
|
||||
SrvPriority: r.Priority,
|
||||
Original: r,
|
||||
}
|
||||
rc.SetLabelFromFQDN(r.Name, domain)
|
||||
|
||||
var err error
|
||||
switch r.Type {
|
||||
case "ALIAS":
|
||||
rc.Type = r.Type
|
||||
rc.SetTarget(r.Content)
|
||||
case "NULLMX":
|
||||
err = rc.PopulateFromString("MX", "0 .", domain)
|
||||
case "MX":
|
||||
err = rc.SetTargetMX(uint16(r.Priority), r.Content)
|
||||
case "SRV":
|
||||
err = rc.SetTargetSRVPriorityString(uint16(r.Priority), r.Content)
|
||||
default:
|
||||
if err := rc.PopulateFromString(r.Type, r.Content, domain); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return rc
|
||||
}
|
||||
|
||||
func recordToNative(rc *models.RecordConfig) *record {
|
||||
record := &record{
|
||||
Name: rc.NameFQDN,
|
||||
Type: rc.Type,
|
||||
Content: strings.TrimSuffix(rc.GetTargetCombined(), "."),
|
||||
TTL: rc.TTL,
|
||||
}
|
||||
|
||||
switch rc.Type { // #rtype_variations
|
||||
case "A", "AAAA", "ALIAS", "CAA", "CNAME", "DNSKEY", "DS", "NS", "NSEC", "NSEC3", "NSEC3PARAM", "PTR", "RRSIG", "SSHFP", "TSLA":
|
||||
// Nothing special.
|
||||
case "TXT":
|
||||
if cap(rc.TxtStrings) == 1 {
|
||||
record.Content = "\"" + rc.TxtStrings[0] + "\""
|
||||
} else if cap(rc.TxtStrings) > 1 {
|
||||
record.Content = ""
|
||||
for _, str := range rc.TxtStrings {
|
||||
record.Content = record.Content + " \"" + str + "\""
|
||||
}
|
||||
record.Content = record.Content[1:len(record.Content)]
|
||||
}
|
||||
case "MX":
|
||||
record.Priority = rc.MxPreference
|
||||
record.Content = strings.TrimSuffix(rc.GetTargetField(), ".")
|
||||
if record.Content == "" {
|
||||
record.Type = "NULLMX"
|
||||
record.Priority = 10
|
||||
}
|
||||
case "SRV":
|
||||
record.Priority = rc.SrvPriority
|
||||
record.Content = fmt.Sprintf("%d %d %s", rc.SrvWeight, rc.SrvPort, strings.TrimSuffix(rc.GetTargetField(), "."))
|
||||
default:
|
||||
log.Printf("hosting.de rtype %v unimplemented", rc.Type)
|
||||
}
|
||||
|
||||
return record
|
||||
}
|
Reference in New Issue
Block a user