2024-01-06 22:19:40 +08:00
|
|
|
package bunnydns
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-08-06 04:38:40 +08:00
|
|
|
"strings"
|
|
|
|
|
2024-01-06 22:19:40 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v4/models"
|
|
|
|
"github.com/miekg/dns/dnsutil"
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
)
|
|
|
|
|
|
|
|
var fqdnTypes = []recordType{recordTypeCNAME, recordTypeMX, recordTypeNS, recordTypePTR, recordTypeSRV}
|
|
|
|
|
|
|
|
func fromRecordConfig(rc *models.RecordConfig) (*record, error) {
|
|
|
|
r := record{
|
|
|
|
Type: recordTypeFromString(rc.Type),
|
|
|
|
Name: rc.GetLabel(),
|
|
|
|
Value: rc.GetTargetField(),
|
|
|
|
TTL: rc.TTL,
|
|
|
|
}
|
|
|
|
|
|
|
|
// While Bunny DNS does not use trailing dots, it still accepts and even preserves them for certain record types.
|
|
|
|
// To avoid confusion, any trailing dots are removed from the record value.
|
|
|
|
if slices.Contains(fqdnTypes, r.Type) && strings.HasSuffix(r.Value, ".") {
|
|
|
|
r.Value = strings.TrimSuffix(r.Value, ".")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch r.Type {
|
|
|
|
case recordTypeNS:
|
|
|
|
if r.Name == "" {
|
|
|
|
r.TTL = 0
|
|
|
|
}
|
|
|
|
case recordTypeSRV:
|
|
|
|
r.Priority = rc.SrvPriority
|
|
|
|
r.Weight = rc.SrvWeight
|
|
|
|
r.Port = rc.SrvPort
|
|
|
|
case recordTypeCAA:
|
|
|
|
r.Flags = rc.CaaFlag
|
|
|
|
r.Tag = rc.CaaTag
|
|
|
|
case recordTypeMX:
|
|
|
|
r.Priority = rc.MxPreference
|
|
|
|
}
|
|
|
|
|
|
|
|
return &r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func toRecordConfig(domain string, r *record) (*models.RecordConfig, error) {
|
|
|
|
rc := models.RecordConfig{
|
|
|
|
Type: recordTypeToString(r.Type),
|
|
|
|
TTL: r.TTL,
|
|
|
|
Original: r,
|
|
|
|
}
|
|
|
|
rc.SetLabel(r.Name, domain)
|
|
|
|
|
|
|
|
// Bunny DNS always operates with fully-qualified names and does not use any trailing dots.
|
|
|
|
// If a record already contains a trailing dot, which the provider UI also accepts, the record value is left as-is.
|
|
|
|
recordValue := r.Value
|
|
|
|
if slices.Contains(fqdnTypes, r.Type) && !strings.HasSuffix(r.Value, ".") {
|
|
|
|
recordValue = dnsutil.AddOrigin(r.Value+".", domain)
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
switch rc.Type {
|
|
|
|
case "CAA":
|
|
|
|
err = rc.SetTargetCAA(r.Flags, r.Tag, recordValue)
|
|
|
|
case "MX":
|
|
|
|
err = rc.SetTargetMX(r.Priority, recordValue)
|
|
|
|
case "SRV":
|
|
|
|
err = rc.SetTargetSRV(r.Priority, r.Weight, r.Port, recordValue)
|
|
|
|
default:
|
|
|
|
err = rc.PopulateFromStringFunc(rc.Type, recordValue, domain, nil)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &rc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type recordType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
recordTypeA recordType = 0
|
|
|
|
recordTypeAAAA recordType = 1
|
|
|
|
recordTypeCNAME recordType = 2
|
|
|
|
recordTypeTXT recordType = 3
|
|
|
|
recordTypeMX recordType = 4
|
|
|
|
recordTypeRedirect recordType = 5
|
|
|
|
recordTypeFlatten recordType = 6
|
|
|
|
recordTypePullZone recordType = 7
|
|
|
|
recordTypeSRV recordType = 8
|
|
|
|
recordTypeCAA recordType = 9
|
|
|
|
recordTypePTR recordType = 10
|
|
|
|
recordTypeScript recordType = 11
|
|
|
|
recordTypeNS recordType = 12
|
|
|
|
)
|
|
|
|
|
|
|
|
func recordTypeFromString(t string) recordType {
|
|
|
|
switch t {
|
|
|
|
case "A":
|
|
|
|
return recordTypeA
|
|
|
|
case "AAAA":
|
|
|
|
return recordTypeAAAA
|
|
|
|
case "CNAME":
|
|
|
|
return recordTypeCNAME
|
|
|
|
case "TXT":
|
|
|
|
return recordTypeTXT
|
|
|
|
case "MX":
|
|
|
|
return recordTypeMX
|
|
|
|
case "REDIRECT":
|
|
|
|
return recordTypeRedirect
|
|
|
|
case "FLATTEN":
|
|
|
|
return recordTypeFlatten
|
|
|
|
case "PULL_ZONE":
|
|
|
|
return recordTypePullZone
|
|
|
|
case "SRV":
|
|
|
|
return recordTypeSRV
|
|
|
|
case "CAA":
|
|
|
|
return recordTypeCAA
|
|
|
|
case "PTR":
|
|
|
|
return recordTypePTR
|
|
|
|
case "SCRIPT":
|
|
|
|
return recordTypeScript
|
|
|
|
case "NS":
|
|
|
|
return recordTypeNS
|
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("BUNNY_DNS: rtype %v unimplemented", t))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func recordTypeToString(t recordType) string {
|
|
|
|
switch t {
|
|
|
|
case recordTypeA:
|
|
|
|
return "A"
|
|
|
|
case recordTypeAAAA:
|
|
|
|
return "AAAA"
|
|
|
|
case recordTypeCNAME:
|
|
|
|
return "CNAME"
|
|
|
|
case recordTypeTXT:
|
|
|
|
return "TXT"
|
|
|
|
case recordTypeMX:
|
|
|
|
return "MX"
|
|
|
|
case recordTypeRedirect:
|
|
|
|
return "REDIRECT"
|
|
|
|
case recordTypeFlatten:
|
|
|
|
return "FLATTEN"
|
|
|
|
case recordTypePullZone:
|
|
|
|
return "PULL_ZONE"
|
|
|
|
case recordTypeSRV:
|
|
|
|
return "SRV"
|
|
|
|
case recordTypeCAA:
|
|
|
|
return "CAA"
|
|
|
|
case recordTypePTR:
|
|
|
|
return "PTR"
|
|
|
|
case recordTypeScript:
|
|
|
|
return "SCRIPT"
|
|
|
|
case recordTypeNS:
|
|
|
|
return "NS"
|
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("BUNNY_DNS: native rtype %v unimplemented", t))
|
|
|
|
}
|
|
|
|
}
|