187 lines
5.4 KiB
Go
187 lines
5.4 KiB
Go
package ddns
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||
|
|
errorsdk "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
|
||
|
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||
|
|
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
|
||
|
|
)
|
||
|
|
|
||
|
|
type TencentCloud struct{}
|
||
|
|
|
||
|
|
func NewTencentCloud() *TencentCloud { return &TencentCloud{} }
|
||
|
|
|
||
|
|
func (t *TencentCloud) Name() string { return "tencentcloud" }
|
||
|
|
|
||
|
|
func (t *TencentCloud) Verify(ctx context.Context, cred Credentials) error {
|
||
|
|
client, err := t.client(cred)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
req := dnspod.NewDescribeDomainListRequest()
|
||
|
|
req.Limit = common.Int64Ptr(1)
|
||
|
|
_, err = client.DescribeDomainList(req)
|
||
|
|
return wrapTencentErr(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TencentCloud) HasZone(ctx context.Context, cred Credentials, zone string) (bool, error) {
|
||
|
|
client, err := t.client(cred)
|
||
|
|
if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
_, err = t.domainInfo(client, zone)
|
||
|
|
return zoneLookupOK(wrapTencentErr(err))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TencentCloud) GetRecordIP(ctx context.Context, cred Credentials, rootDomain, recordName, recordType string) (string, error) {
|
||
|
|
client, err := t.client(cred)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
zone, err := t.domainInfo(client, rootDomain)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
records, err := t.listRecords(client, zone, tencentSubdomain(recordName), recordType)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
for _, rec := range records {
|
||
|
|
if rec.Value != nil {
|
||
|
|
return *rec.Value, nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return "", nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TencentCloud) UpdateRecord(ctx context.Context, cred Credentials, rootDomain, recordName, recordType, ip string) error {
|
||
|
|
client, err := t.client(cred)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
zone, err := t.domainInfo(client, rootDomain)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
subDomain := tencentSubdomain(recordName)
|
||
|
|
records, err := t.listRecords(client, zone, subDomain, recordType)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if len(records) > 0 && records[0].RecordId != nil {
|
||
|
|
req := dnspod.NewModifyRecordRequest()
|
||
|
|
req.Domain = zone.Name
|
||
|
|
req.DomainId = zone.DomainId
|
||
|
|
req.RecordId = records[0].RecordId
|
||
|
|
req.SubDomain = common.StringPtr(subDomain)
|
||
|
|
req.RecordType = common.StringPtr(recordType)
|
||
|
|
req.RecordLine = common.StringPtr(tencentRecordLine(records[0]))
|
||
|
|
req.Value = common.StringPtr(ip)
|
||
|
|
req.TTL = common.Uint64Ptr(600)
|
||
|
|
_, err = client.ModifyRecord(req)
|
||
|
|
return wrapTencentErr(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
req := dnspod.NewCreateRecordRequest()
|
||
|
|
req.Domain = zone.Name
|
||
|
|
req.DomainId = zone.DomainId
|
||
|
|
req.SubDomain = common.StringPtr(subDomain)
|
||
|
|
req.RecordType = common.StringPtr(recordType)
|
||
|
|
req.RecordLine = common.StringPtr("默认")
|
||
|
|
req.Value = common.StringPtr(ip)
|
||
|
|
req.TTL = common.Uint64Ptr(600)
|
||
|
|
_, err = client.CreateRecord(req)
|
||
|
|
return wrapTencentErr(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TencentCloud) client(cred Credentials) (*dnspod.Client, error) {
|
||
|
|
if cred.Token == "" || cred.Secret == "" {
|
||
|
|
return nil, fmt.Errorf("腾讯云 DNS 凭证未配置")
|
||
|
|
}
|
||
|
|
credential := common.NewCredential(cred.Token, cred.Secret)
|
||
|
|
cpf := profile.NewClientProfile()
|
||
|
|
cpf.HttpProfile.Endpoint = "dnspod.tencentcloudapi.com"
|
||
|
|
return dnspod.NewClient(credential, "", cpf)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TencentCloud) domainInfo(client *dnspod.Client, rootDomain string) (*dnspod.DomainListItem, error) {
|
||
|
|
rootDomain = strings.ToLower(strings.TrimSpace(rootDomain))
|
||
|
|
req := dnspod.NewDescribeDomainListRequest()
|
||
|
|
var offset int64
|
||
|
|
for {
|
||
|
|
req.Offset = common.Int64Ptr(offset)
|
||
|
|
resp, err := client.DescribeDomainList(req)
|
||
|
|
if err != nil {
|
||
|
|
return nil, wrapTencentErr(err)
|
||
|
|
}
|
||
|
|
for _, zone := range resp.Response.DomainList {
|
||
|
|
if zone.Name != nil && strings.EqualFold(*zone.Name, rootDomain) {
|
||
|
|
return zone, nil
|
||
|
|
}
|
||
|
|
if zone.Punycode != nil && strings.EqualFold(*zone.Punycode, rootDomain) {
|
||
|
|
return zone, nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
total := int64(0)
|
||
|
|
if resp.Response.DomainCountInfo != nil && resp.Response.DomainCountInfo.AllTotal != nil {
|
||
|
|
total = int64(*resp.Response.DomainCountInfo.AllTotal)
|
||
|
|
}
|
||
|
|
offset += int64(len(resp.Response.DomainList))
|
||
|
|
if offset >= total || len(resp.Response.DomainList) == 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil, fmt.Errorf("未找到域名 %s 对应的腾讯云 DNS 域名", rootDomain)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TencentCloud) listRecords(client *dnspod.Client, zone *dnspod.DomainListItem, subDomain, recordType string) ([]*dnspod.RecordListItem, error) {
|
||
|
|
req := dnspod.NewDescribeRecordListRequest()
|
||
|
|
req.Domain = zone.Name
|
||
|
|
req.DomainId = zone.DomainId
|
||
|
|
req.Subdomain = common.StringPtr(subDomain)
|
||
|
|
req.RecordType = common.StringPtr(recordType)
|
||
|
|
resp, err := client.DescribeRecordList(req)
|
||
|
|
if err != nil {
|
||
|
|
var sdkErr *errorsdk.TencentCloudSDKError
|
||
|
|
if errors.As(err, &sdkErr) && sdkErr.Code == dnspod.RESOURCENOTFOUND_NODATAOFRECORD {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return nil, wrapTencentErr(err)
|
||
|
|
}
|
||
|
|
if resp.Response == nil {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return resp.Response.RecordList, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func tencentSubdomain(recordName string) string {
|
||
|
|
sub := subDomainForRecord(recordName)
|
||
|
|
if sub == "@" {
|
||
|
|
return "@"
|
||
|
|
}
|
||
|
|
return sub
|
||
|
|
}
|
||
|
|
|
||
|
|
func tencentRecordLine(rec *dnspod.RecordListItem) string {
|
||
|
|
if rec != nil && rec.Line != nil && *rec.Line != "" {
|
||
|
|
return *rec.Line
|
||
|
|
}
|
||
|
|
return "默认"
|
||
|
|
}
|
||
|
|
|
||
|
|
func wrapTencentErr(err error) error {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
var sdkErr *errorsdk.TencentCloudSDKError
|
||
|
|
if errors.As(err, &sdkErr) {
|
||
|
|
return fmt.Errorf("腾讯云 DNS API 错误:%s", sdkErr.Message)
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|