libdns-vultr/helpers.go
2025-06-01 15:14:24 +02:00

169 lines
4.2 KiB
Go

package vultr
import (
"fmt"
"net/netip"
"strconv"
"strings"
"time"
"github.com/libdns/libdns"
"github.com/vultr/govultr/v3"
)
// Converts `govultr.DomainRecord` to `libdns.Record“
// Taken from libdns/cloudflare, adapted for Vultr's specific format
func libdnsRecord(r govultr.DomainRecord, zone string) (libdns.Record, error) {
name := libdns.RelativeName(r.Name, zone)
ttl := time.Duration(r.TTL) * time.Second
switch r.Type {
case "A", "AAAA":
addr, err := netip.ParseAddr(r.Data)
if err != nil {
return libdns.Address{}, fmt.Errorf("invalid IP address %q: %v", r.Data, err)
}
return libdns.Address{
Name: name,
TTL: ttl,
IP: addr,
ProviderData: r.ID,
}, nil
case "CAA":
dataParts := strings.SplitN(r.Data, " ", 3)
if len(dataParts) < 3 {
return libdns.SRV{}, fmt.Errorf("record %v does not contain enough data fields; expected format: '<flags> <tag> <value>'", name)
}
flags, err := strconv.Atoi(dataParts[0])
if err != nil {
return libdns.SRV{}, fmt.Errorf("record %v contains invalid value for flags: %v", name, err)
}
return libdns.CAA{
Name: name,
TTL: ttl,
Flags: uint8(flags),
Tag: dataParts[1],
Value: dataParts[2],
ProviderData: r.ID,
}, nil
case "CNAME":
return libdns.CNAME{
Name: name,
TTL: ttl,
Target: r.Data,
ProviderData: r.ID,
}, nil
case "MX":
return libdns.MX{
Name: name,
TTL: ttl,
Preference: uint16(r.Priority),
Target: r.Data,
ProviderData: r.ID,
}, nil
case "NS":
return libdns.NS{
Name: name,
TTL: ttl,
Target: r.Data,
ProviderData: r.ID,
}, nil
case "SRV":
// Vultr doesn't append the zone to the SRV record name, so we just need
// to parse 2 parts
parts := strings.SplitN(r.Name, ".", 2)
if len(parts) < 2 {
return libdns.SRV{}, fmt.Errorf("name %v does not contain enough fields; expected format: '_service._proto.name'", name)
}
dataParts := strings.SplitN(r.Data, " ", 3)
if len(dataParts) < 3 {
return libdns.SRV{}, fmt.Errorf("record %v does not contain enough data fields; expected format: 'weight port target'", name)
}
weight, err := strconv.Atoi(dataParts[0])
if err != nil {
return libdns.SRV{}, fmt.Errorf("record %v contains invalid value for weight: %v", name, err)
}
port, err := strconv.Atoi(dataParts[1])
if err != nil {
return libdns.SRV{}, fmt.Errorf("record %v contains invalid value for port: %v", name, err)
}
return libdns.SRV{
Service: strings.TrimPrefix(parts[0], "_"),
Transport: strings.TrimPrefix(parts[1], "_"),
Name: zone,
TTL: ttl,
Priority: uint16(r.Priority),
Weight: uint16(weight),
Port: uint16(port),
Target: dataParts[2],
ProviderData: r.ID,
}, nil
case "TXT":
return libdns.TXT{
Name: name,
TTL: ttl,
Text: r.Data,
ProviderData: r.ID,
}, nil
default:
return libdns.RR{
Name: name,
TTL: ttl,
Type: r.Type,
Data: r.Data,
}.Parse()
}
}
// Converts `libdns.Record` to `govultr.DomainRecordReq`, to be used with API
// requests.
func vultrRecordReq(r libdns.Record) (govultr.DomainRecordReq, error) {
return govultr.DomainRecordReq{
Name: r.RR().Name,
Type: r.RR().Type,
TTL: int(r.RR().TTL.Seconds()),
Data: r.RR().Data,
}, nil
}
func GetRecordID(r libdns.Record) (string, error) {
var recordId string
switch r := r.(type) {
case libdns.Address:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.CAA:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.CNAME:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.MX:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.NS:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.SRV:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.ServiceBinding:
recordId = r.ProviderData.(string)
return recordId, nil
case libdns.TXT:
recordId = r.ProviderData.(string)
return recordId, nil
default:
}
return "", fmt.Errorf("libdns record has no provider record ID")
}