mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-12-09 05:36:27 +08:00
<!--
## Before submiting a pull request
Please make sure you've run the following commands from the root
directory.
bin/generate-all.sh
(this runs commands like "go generate", fixes formatting, and so on)
## Release changelog section
Help keep the release changelog clear by pre-naming the proper section
in the GitHub pull request title.
Some examples:
* CICD: Add required GHA permissions for goreleaser
* DOCS: Fixed providers with "contributor support" table
* ROUTE53: Allow R53_ALIAS records to enable target health evaluation
More examples/context can be found in the file .goreleaser.yml under the
'build' > 'changelog' key.
!-->
https://github.com/StackExchange/dnscontrol/issues/420
Please create the GitHub label 'provider-ALIDNS'
---------
Co-authored-by: Tom Limoncelli <6293917+tlimoncelli@users.noreply.github.com>
187 lines
5.1 KiB
Go
187 lines
5.1 KiB
Go
package alidns
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/StackExchange/dnscontrol/v4/models"
|
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
|
|
)
|
|
|
|
func (a *aliDNSDsp) getDomainVersionInfo(domain string) (*domainVersionInfo, error) {
|
|
// Check cache first
|
|
a.cacheMu.Lock()
|
|
info, ok := a.domainVersionCache[domain]
|
|
a.cacheMu.Unlock()
|
|
if ok {
|
|
return info, nil
|
|
}
|
|
|
|
req := alidns.CreateDescribeDomainInfoRequest()
|
|
req.DomainName = domain
|
|
|
|
resp, err := a.client.DescribeDomainInfo(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Determine minTTL based on VersionCode
|
|
var minTTL uint32
|
|
switch resp.VersionCode {
|
|
case "version_enterprise_advanced":
|
|
minTTL = 1 // Enterprise Ultimate Edition
|
|
case "version_personal", "mianfei":
|
|
minTTL = 600 // Personal Edition and Free Edition
|
|
default:
|
|
// Use MinTtl from API if available, otherwise default to 600
|
|
if resp.MinTtl > 0 {
|
|
minTTL = uint32(resp.MinTtl)
|
|
} else {
|
|
minTTL = 600
|
|
}
|
|
}
|
|
|
|
info = &domainVersionInfo{
|
|
versionCode: resp.VersionCode,
|
|
minTTL: minTTL,
|
|
maxTTL: 86400,
|
|
}
|
|
a.cacheMu.Lock()
|
|
a.domainVersionCache[domain] = info
|
|
a.cacheMu.Unlock()
|
|
return info, nil
|
|
}
|
|
|
|
// GetNameservers returns the nameservers for a domain.
|
|
func (a *aliDNSDsp) getNameservers(domain string) ([]string, error) {
|
|
req := alidns.CreateDescribeDomainInfoRequest()
|
|
req.DomainName = domain
|
|
|
|
resp, err := a.client.DescribeDomainInfo(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Add trailing dot to each nameserver to make them FQDNs
|
|
nameservers := make([]string, len(resp.DnsServers.DnsServer))
|
|
for i, ns := range resp.DnsServers.DnsServer {
|
|
if ns != "" && ns[len(ns)-1] != '.' {
|
|
nameservers[i] = ns + "."
|
|
} else {
|
|
nameservers[i] = ns
|
|
}
|
|
}
|
|
|
|
return nameservers, nil
|
|
}
|
|
|
|
func (a *aliDNSDsp) deleteRecordset(records []*models.RecordConfig, domainName string) error {
|
|
for _, r := range records {
|
|
req := alidns.CreateDeleteDomainRecordRequest()
|
|
original, ok := r.Original.(*alidns.Record)
|
|
if !ok {
|
|
return fmt.Errorf("deleteRecordset: record original is not of type *alidns.Record")
|
|
}
|
|
req.RecordId = original.RecordId
|
|
|
|
_, err := a.client.DeleteDomainRecord(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *aliDNSDsp) createRecordset(records []*models.RecordConfig, domainName string) error {
|
|
for _, r := range records {
|
|
req := alidns.CreateAddDomainRecordRequest()
|
|
req.DomainName = domainName
|
|
req.RR = r.Name
|
|
req.Type = r.Type
|
|
req.TTL = requests.Integer(fmt.Sprintf("%d", r.TTL))
|
|
req.Value = recordToNativeContent(r)
|
|
|
|
// Set priority for MX and SRV records
|
|
if r.Type == "MX" || r.Type == "SRV" {
|
|
req.Priority = requests.Integer(fmt.Sprintf("%d", recordToNativePriority(r)))
|
|
}
|
|
|
|
_, err := a.client.AddDomainRecord(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *aliDNSDsp) updateRecordset(existing, desired []*models.RecordConfig, domainName string) error {
|
|
// Strategy: Delete all existing records, then create all desired records.
|
|
// This is the simplest and most reliable approach because:
|
|
// 1. The number of records in a recordset may change
|
|
// 2. There's no guaranteed 1:1 mapping between existing and desired records
|
|
// 3. Alibaba Cloud API requires RecordId for updates, which desired records don't have
|
|
|
|
// Delete all existing records first
|
|
if err := a.deleteRecordset(existing, domainName); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Then create all desired records
|
|
return a.createRecordset(desired, domainName)
|
|
}
|
|
|
|
// describeDomainRecordsAll fetches all domain records for 'domain', handling
|
|
// pagination transparently. It returns the slice of *alidns.Record or an error.
|
|
func (a *aliDNSDsp) describeDomainRecordsAll(domain string) ([]*alidns.Record, error) {
|
|
// The SDK returns a slice of value Records (not pointers). We fetch pages
|
|
// as values and then convert to pointers before returning.
|
|
fetch := func(pageNumber, pageSize int) ([]alidns.Record, int, error) {
|
|
req := alidns.CreateDescribeDomainRecordsRequest()
|
|
req.Status = "Enable"
|
|
req.DomainName = domain
|
|
req.PageNumber = requests.NewInteger(pageNumber)
|
|
req.PageSize = requests.NewInteger(pageSize)
|
|
|
|
resp, err := a.client.DescribeDomainRecords(req)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
total := int(resp.TotalCount)
|
|
return resp.DomainRecords.Record, total, nil
|
|
}
|
|
|
|
vals, err := paginateAll(fetch, 500)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]*alidns.Record, 0, len(vals))
|
|
for i := range vals {
|
|
out = append(out, &vals[i])
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (a *aliDNSDsp) describeDomainsAll() ([]string, error) {
|
|
// describeDomainsAll fetches all domains in the account, handling pagination.
|
|
fetch := func(pageNumber, pageSize int) ([]string, int, error) {
|
|
req := alidns.CreateDescribeDomainsRequest()
|
|
req.PageNumber = requests.NewInteger(pageNumber)
|
|
req.PageSize = requests.NewInteger(pageSize)
|
|
|
|
resp, err := a.client.DescribeDomains(req)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
domains := make([]string, 0, len(resp.Domains.Domain))
|
|
for _, d := range resp.Domains.Domain {
|
|
domains = append(domains, d.DomainName)
|
|
}
|
|
|
|
total := int(resp.TotalCount)
|
|
return domains, total, nil
|
|
}
|
|
|
|
return paginateAll(fetch, 100)
|
|
}
|