mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-11-04 16:40:21 +00:00
Update ddns.go
This commit is contained in:
1 parent
b18e085a2c
commit
f2f10ea7bc
1 file changed
+103
-77
+103
-77
@@ -35,57 +35,62 @@ func (provider *Provider) GetProfileID() uint64 {
|
||||
}
|
||||
|
||||
func (provider *Provider) UpdateDomain(ctx context.Context, overrideDomains ...string) {
|
||||
for _, domain := range utils.IfOr(len(overrideDomains) > 0, overrideDomains, provider.DDNSProfile.Domains) {
|
||||
for retries := range int(provider.DDNSProfile.MaxRetries) {
|
||||
log.Printf("NEZHA>> Updating DNS Record of domain %s: %d/%d", domain, retries+1, provider.DDNSProfile.MaxRetries)
|
||||
if err := provider.updateDomain(ctx, domain); err != nil {
|
||||
log.Printf("NEZHA>> Failed to update DNS record of domain %s: %v", domain, err)
|
||||
} else {
|
||||
log.Printf("NEZHA>> Update DNS record of domain %s succeeded", domain)
|
||||
break
|
||||
domains := utils.IfOr(len(overrideDomains) > 0, overrideDomains, provider.DDNSProfile.Domains)
|
||||
maxRetries := int(provider.DDNSProfile.MaxRetries)
|
||||
if maxRetries <= 0 {
|
||||
maxRetries = 1
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
var err error
|
||||
provider.prefix, provider.zone, err = provider.splitDomainSOA(ctx, domain)
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Failed to split domain SOA for %s: %v", domain, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 独立处理 IPv4 更新或删除
|
||||
if provider.DDNSProfile.EnableIPv4 != nil && *provider.DDNSProfile.EnableIPv4 {
|
||||
for retries := 0; retries < maxRetries; retries++ {
|
||||
log.Printf("NEZHA>> Updating IPv4 record of domain %s: %d/%d", domain, retries+1, maxRetries)
|
||||
var ipv4Err error
|
||||
if provider.IPAddrs.IPv4Addr == "" {
|
||||
ipv4Err = provider.deleteDomainRecord(ctx, "A")
|
||||
} else {
|
||||
ipv4Err = provider.addDomainRecord(ctx, "A", provider.IPAddrs.IPv4Addr)
|
||||
}
|
||||
|
||||
if ipv4Err != nil {
|
||||
log.Printf("NEZHA>> Failed to update IPv4 record of domain %s: %v", domain, ipv4Err)
|
||||
} else {
|
||||
log.Printf("NEZHA>> Update IPv4 record of domain %s succeeded", domain)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 独立处理 IPv6 更新或删除
|
||||
if provider.DDNSProfile.EnableIPv6 != nil && *provider.DDNSProfile.EnableIPv6 {
|
||||
for retries := 0; retries < maxRetries; retries++ {
|
||||
log.Printf("NEZHA>> Updating IPv6 record of domain %s: %d/%d", domain, retries+1, maxRetries)
|
||||
var ipv6Err error
|
||||
if provider.IPAddrs.IPv6Addr == "" {
|
||||
ipv6Err = provider.deleteDomainRecord(ctx, "AAAA")
|
||||
} else {
|
||||
ipv6Err = provider.addDomainRecord(ctx, "AAAA", provider.IPAddrs.IPv6Addr)
|
||||
}
|
||||
|
||||
if ipv6Err != nil {
|
||||
log.Printf("NEZHA>> Failed to update IPv6 record of domain %s: %v", domain, ipv6Err)
|
||||
} else {
|
||||
log.Printf("NEZHA>> Update IPv6 record of domain %s succeeded", domain)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) updateDomain(ctx context.Context, domain string) error {
|
||||
var err error
|
||||
provider.prefix, provider.zone, err = provider.splitDomainSOA(ctx, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 独立处理 IPv4 更新或删除
|
||||
if *provider.DDNSProfile.EnableIPv4 {
|
||||
if provider.IPAddrs.IPv4Addr == "" {
|
||||
log.Printf("NEZHA>> IPv4 address is empty for domain %s, deleting A record...", domain)
|
||||
if err = provider.deleteDomainRecord(ctx, "A"); err != nil {
|
||||
return fmt.Errorf("failed to delete IPv4 record: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err = provider.addDomainRecord(ctx, "A", provider.IPAddrs.IPv4Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 独立处理 IPv6 更新或删除
|
||||
if *provider.DDNSProfile.EnableIPv6 {
|
||||
if provider.IPAddrs.IPv6Addr == "" {
|
||||
log.Printf("NEZHA>> IPv6 address is empty for domain %s, deleting AAAA record...", domain)
|
||||
if err = provider.deleteDomainRecord(ctx, "AAAA"); err != nil {
|
||||
return fmt.Errorf("failed to delete IPv6 record: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err = provider.addDomainRecord(ctx, "AAAA", provider.IPAddrs.IPv6Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Provider) addDomainRecord(ctx context.Context, recType, addr string) error {
|
||||
netipAddr, err := netip.ParseAddr(addr)
|
||||
if err != nil {
|
||||
@@ -103,68 +108,90 @@ func (provider *Provider) addDomainRecord(ctx context.Context, recType, addr str
|
||||
return err
|
||||
}
|
||||
|
||||
// deleteDomainRecord 用于删除指定类型的解析记录
|
||||
// deleteDomainRecord 用于安全删除指定类型的解析记录
|
||||
func (provider *Provider) deleteDomainRecord(ctx context.Context, recType string) error {
|
||||
// 检查 libdns.RecordGetter 和 libdns.RecordDeleter
|
||||
getter, ok := provider.Setter.(libdns.RecordGetter)
|
||||
if !ok {
|
||||
return fmt.Errorf("dns provider does not support record getting")
|
||||
}
|
||||
// 同时断言 RecordGetter 与 RecordDeleter,防止提供商因缺少接口导致断言错误
|
||||
getter, okGetter := provider.Setter.(libdns.RecordGetter)
|
||||
deleter, okDeleter := provider.Setter.(libdns.RecordDeleter)
|
||||
|
||||
deleter, ok := provider.Setter.(libdns.RecordDeleter)
|
||||
if !ok {
|
||||
return fmt.Errorf("dns provider does not support record deletion")
|
||||
}
|
||||
|
||||
// 获取 DNS 记录
|
||||
allRecords, err := getter.GetRecords(ctx, provider.zone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get DNS records: %w", err)
|
||||
if !okGetter || !okDeleter {
|
||||
log.Printf("NEZHA>> DNS provider does not support record getting or deletion, safely skipping deletion for %s", recType)
|
||||
return nil
|
||||
}
|
||||
|
||||
cleanName := func(name string) string {
|
||||
return strings.ToLower(strings.TrimSuffix(name, "."))
|
||||
}
|
||||
|
||||
cleanPrefix := cleanName(provider.prefix)
|
||||
cleanZone := cleanName(provider.zone)
|
||||
targetRecType := strings.ToUpper(recType)
|
||||
|
||||
// 筛选和匹配
|
||||
// 通过特征匹配识别 Hurricane Electric (HE) 等特殊供应商
|
||||
providerType := strings.ToLower(fmt.Sprintf("%T", provider.Setter))
|
||||
isHeProvider := strings.Contains(providerType, "he") ||
|
||||
strings.Contains(providerType, "hurricane") ||
|
||||
strings.Contains(providerType, "dns.he")
|
||||
|
||||
// 针对 HE 供应商:仅返回根域(@),无法枚举子域名
|
||||
if isHeProvider {
|
||||
log.Printf("NEZHA>> Provider identified as Hurricane Electric (HE) variant, using direct set deletion path for %s.%s", provider.prefix, provider.zone)
|
||||
var dummyIP netip.Addr
|
||||
if targetRecType == "A" {
|
||||
dummyIP = netip.MustParseAddr("0.0.0.0")
|
||||
} else {
|
||||
dummyIP = netip.MustParseAddr("::")
|
||||
}
|
||||
_, err := provider.Setter.SetRecords(ctx, provider.zone,
|
||||
[]libdns.Record{
|
||||
libdns.Address{
|
||||
Name: provider.prefix,
|
||||
IP: dummyIP,
|
||||
TTL: time.Minute,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取 DNS 记录(针对标准提供商,如 Cloudflare)
|
||||
allRecords, err := getter.GetRecords(ctx, provider.zone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get DNS records: %w", err)
|
||||
}
|
||||
|
||||
cleanZone := cleanName(provider.zone)
|
||||
|
||||
// 筛选和匹配目标记录
|
||||
var targetRecords []libdns.Record
|
||||
for _, rec := range allRecords {
|
||||
rr := rec.RR()
|
||||
recName := rr.Name
|
||||
currentType := strings.ToUpper(rr.Type)
|
||||
|
||||
if currentType != targetRecType {
|
||||
if strings.ToUpper(rr.Type) != targetRecType {
|
||||
continue
|
||||
}
|
||||
|
||||
relName := libdns.RelativeName(recName, provider.zone)
|
||||
relName := libdns.RelativeName(rr.Name, provider.zone)
|
||||
cleanRel := cleanName(relName)
|
||||
cleanRRName := cleanName(recName)
|
||||
cleanRRName := cleanName(rr.Name)
|
||||
|
||||
isApex := cleanRel == "" || cleanRel == "@" || cleanRRName == cleanZone
|
||||
|
||||
var matchedName bool
|
||||
if cleanPrefix == "" {
|
||||
matchedName = cleanRel == "" || cleanRel == "@" || cleanRRName == cleanZone
|
||||
matchedName = isApex
|
||||
} else {
|
||||
matchedName = cleanRel == cleanPrefix || cleanRRName == cleanPrefix
|
||||
}
|
||||
|
||||
// 保留原始的 rec 接口(包含 Cloudflare 所需的 ProviderData 元数据),用于精准删除
|
||||
if matchedName {
|
||||
targetRecords = append(targetRecords, rec)
|
||||
}
|
||||
}
|
||||
|
||||
// 若未找到对应记录,则直接返回成功(视为已删除或不存在)
|
||||
// 若未找到对应记录(例如已经成功删除),则直接返回成功
|
||||
if len(targetRecords) == 0 {
|
||||
log.Printf("NEZHA>> No matching %s record found for deletion under zone %s", recType, provider.zone)
|
||||
log.Printf("NEZHA>> No matching %s record found for deletion under zone %s, already clean", recType, provider.zone)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行删除操作
|
||||
// 执行实际删除操作
|
||||
_, err = deleter.DeleteRecords(ctx, provider.zone, targetRecords)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleter.DeleteRecords failed: %w", err)
|
||||
@@ -174,7 +201,7 @@ func (provider *Provider) deleteDomainRecord(ctx context.Context, recType string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Provider) splitDomainSOA(ctx context.Context, domain string) (prefix string, zone string, err error) {
|
||||
func (provider *Provider) splitDomainSOA(ctx context.Context, domain string, overrideServers ...string) (prefix string, zone string, err error) {
|
||||
c := &dns.Client{Timeout: dnsTimeOut}
|
||||
|
||||
domain += "."
|
||||
@@ -201,7 +228,6 @@ func (provider *Provider) splitDomainSOA(ctx context.Context, domain string) (pr
|
||||
if soa, ok := r.Answer[0].(*dns.SOA); ok {
|
||||
zone := soa.Hdr.Name
|
||||
prefix := libdns.RelativeName(domain, zone)
|
||||
// Convert "@" to empty string for zone apex
|
||||
if prefix == "@" {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user