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
f2f10ea7bc
commit
cbc583dd3a
1 file changed
+54
-61
+54
-61
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/libdns/he"
|
||||||
"github.com/libdns/libdns"
|
"github.com/libdns/libdns"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
@@ -22,9 +23,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
prefix string
|
|
||||||
zone string
|
|
||||||
|
|
||||||
DDNSProfile *model.DDNSProfile
|
DDNSProfile *model.DDNSProfile
|
||||||
IPAddrs *model.IP
|
IPAddrs *model.IP
|
||||||
Setter libdns.RecordSetter
|
Setter libdns.RecordSetter
|
||||||
@@ -42,10 +40,19 @@ func (provider *Provider) UpdateDomain(ctx context.Context, overrideDomains ...s
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
var err error
|
var prefix, zone string
|
||||||
provider.prefix, provider.zone, err = provider.splitDomainSOA(ctx, domain)
|
var soaErr error
|
||||||
if err != nil {
|
|
||||||
log.Printf("NEZHA>> Failed to split domain SOA for %s: %v", domain, err)
|
for retries := 0; retries < maxRetries; retries++ {
|
||||||
|
prefix, zone, soaErr = provider.splitDomainSOA(ctx, domain)
|
||||||
|
if soaErr == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("NEZHA>> Failed to split domain SOA for %s (attempt %d/%d): %v", domain, retries+1, maxRetries, soaErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if soaErr != nil {
|
||||||
|
log.Printf("NEZHA>> Failed to split domain SOA for %s after %d retries, skipping domain", domain, maxRetries)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +62,9 @@ func (provider *Provider) UpdateDomain(ctx context.Context, overrideDomains ...s
|
|||||||
log.Printf("NEZHA>> Updating IPv4 record of domain %s: %d/%d", domain, retries+1, maxRetries)
|
log.Printf("NEZHA>> Updating IPv4 record of domain %s: %d/%d", domain, retries+1, maxRetries)
|
||||||
var ipv4Err error
|
var ipv4Err error
|
||||||
if provider.IPAddrs.IPv4Addr == "" {
|
if provider.IPAddrs.IPv4Addr == "" {
|
||||||
ipv4Err = provider.deleteDomainRecord(ctx, "A")
|
ipv4Err = provider.deleteDomainRecord(ctx, prefix, zone, "A")
|
||||||
} else {
|
} else {
|
||||||
ipv4Err = provider.addDomainRecord(ctx, "A", provider.IPAddrs.IPv4Addr)
|
ipv4Err = provider.addDomainRecord(ctx, prefix, zone, "A", provider.IPAddrs.IPv4Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipv4Err != nil {
|
if ipv4Err != nil {
|
||||||
@@ -75,9 +82,9 @@ func (provider *Provider) UpdateDomain(ctx context.Context, overrideDomains ...s
|
|||||||
log.Printf("NEZHA>> Updating IPv6 record of domain %s: %d/%d", domain, retries+1, maxRetries)
|
log.Printf("NEZHA>> Updating IPv6 record of domain %s: %d/%d", domain, retries+1, maxRetries)
|
||||||
var ipv6Err error
|
var ipv6Err error
|
||||||
if provider.IPAddrs.IPv6Addr == "" {
|
if provider.IPAddrs.IPv6Addr == "" {
|
||||||
ipv6Err = provider.deleteDomainRecord(ctx, "AAAA")
|
ipv6Err = provider.deleteDomainRecord(ctx, prefix, zone, "AAAA")
|
||||||
} else {
|
} else {
|
||||||
ipv6Err = provider.addDomainRecord(ctx, "AAAA", provider.IPAddrs.IPv6Addr)
|
ipv6Err = provider.addDomainRecord(ctx, prefix, zone, "AAAA", provider.IPAddrs.IPv6Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipv6Err != nil {
|
if ipv6Err != nil {
|
||||||
@@ -91,16 +98,16 @@ func (provider *Provider) UpdateDomain(ctx context.Context, overrideDomains ...s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Provider) addDomainRecord(ctx context.Context, recType, addr string) error {
|
func (provider *Provider) addDomainRecord(ctx context.Context, prefix, zone, recType, addr string) error {
|
||||||
netipAddr, err := netip.ParseAddr(addr)
|
netipAddr, err := netip.ParseAddr(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parse error: %v", err)
|
return fmt.Errorf("parse error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = provider.Setter.SetRecords(ctx, provider.zone,
|
_, err = provider.Setter.SetRecords(ctx, zone,
|
||||||
[]libdns.Record{
|
[]libdns.Record{
|
||||||
libdns.Address{
|
libdns.Address{
|
||||||
Name: provider.prefix,
|
Name: prefix,
|
||||||
IP: netipAddr,
|
IP: netipAddr,
|
||||||
TTL: time.Minute,
|
TTL: time.Minute,
|
||||||
},
|
},
|
||||||
@@ -109,8 +116,25 @@ func (provider *Provider) addDomainRecord(ctx context.Context, recType, addr str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deleteDomainRecord 用于安全删除指定类型的解析记录
|
// deleteDomainRecord 用于安全删除指定类型的解析记录
|
||||||
func (provider *Provider) deleteDomainRecord(ctx context.Context, recType string) error {
|
func (provider *Provider) deleteDomainRecord(ctx context.Context, prefix, zone, recType string) error {
|
||||||
// 同时断言 RecordGetter 与 RecordDeleter,防止提供商因缺少接口导致断言错误
|
targetRecType := strings.ToUpper(recType)
|
||||||
|
|
||||||
|
// 针对 HE 服务商使用标准的 libdns.RR
|
||||||
|
if _, ok := provider.Setter.(*he.Provider); ok {
|
||||||
|
deleter, okDeleter := provider.Setter.(libdns.RecordDeleter)
|
||||||
|
if !okDeleter {
|
||||||
|
return fmt.Errorf("he provider does not implement RecordDeleter")
|
||||||
|
}
|
||||||
|
_, err := deleter.DeleteRecords(ctx, zone, []libdns.Record{
|
||||||
|
libdns.RR{
|
||||||
|
Name: prefix,
|
||||||
|
Type: targetRecType,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查接口
|
||||||
getter, okGetter := provider.Setter.(libdns.RecordGetter)
|
getter, okGetter := provider.Setter.(libdns.RecordGetter)
|
||||||
deleter, okDeleter := provider.Setter.(libdns.RecordDeleter)
|
deleter, okDeleter := provider.Setter.(libdns.RecordDeleter)
|
||||||
|
|
||||||
@@ -122,44 +146,15 @@ func (provider *Provider) deleteDomainRecord(ctx context.Context, recType string
|
|||||||
cleanName := func(name string) string {
|
cleanName := func(name string) string {
|
||||||
return strings.ToLower(strings.TrimSuffix(name, "."))
|
return strings.ToLower(strings.TrimSuffix(name, "."))
|
||||||
}
|
}
|
||||||
cleanPrefix := cleanName(provider.prefix)
|
cleanPrefix := cleanName(prefix)
|
||||||
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)
|
// 获取 DNS 记录(针对标准提供商,如 Cloudflare)
|
||||||
allRecords, err := getter.GetRecords(ctx, provider.zone)
|
allRecords, err := getter.GetRecords(ctx, zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get DNS records: %w", err)
|
return fmt.Errorf("failed to get DNS records: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanZone := cleanName(provider.zone)
|
cleanZone := cleanName(zone)
|
||||||
|
|
||||||
// 筛选和匹配目标记录
|
|
||||||
var targetRecords []libdns.Record
|
var targetRecords []libdns.Record
|
||||||
for _, rec := range allRecords {
|
for _, rec := range allRecords {
|
||||||
rr := rec.RR()
|
rr := rec.RR()
|
||||||
@@ -167,7 +162,7 @@ func (provider *Provider) deleteDomainRecord(ctx context.Context, recType string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
relName := libdns.RelativeName(rr.Name, provider.zone)
|
relName := libdns.RelativeName(rr.Name, zone)
|
||||||
cleanRel := cleanName(relName)
|
cleanRel := cleanName(relName)
|
||||||
cleanRRName := cleanName(rr.Name)
|
cleanRRName := cleanName(rr.Name)
|
||||||
|
|
||||||
@@ -187,28 +182,26 @@ func (provider *Provider) deleteDomainRecord(ctx context.Context, recType string
|
|||||||
|
|
||||||
// 若未找到对应记录(例如已经成功删除),则直接返回成功
|
// 若未找到对应记录(例如已经成功删除),则直接返回成功
|
||||||
if len(targetRecords) == 0 {
|
if len(targetRecords) == 0 {
|
||||||
log.Printf("NEZHA>> No matching %s record found for deletion under zone %s, already clean", recType, provider.zone)
|
log.Printf("NEZHA>> No matching %s record found for deletion under zone %s, already clean", recType, zone)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行实际删除操作
|
_, err = deleter.DeleteRecords(ctx, zone, targetRecords)
|
||||||
_, err = deleter.DeleteRecords(ctx, provider.zone, targetRecords)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("deleter.DeleteRecords failed: %w", err)
|
return fmt.Errorf("deleter.DeleteRecords failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("NEZHA>> Successfully deleted %d matching %s record(s) for %s.%s", len(targetRecords), recType, provider.prefix, provider.zone)
|
log.Printf("NEZHA>> Successfully deleted %d matching %s record(s) for %s.%s", len(targetRecords), recType, prefix, zone)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Provider) splitDomainSOA(ctx context.Context, domain string, overrideServers ...string) (prefix string, zone string, err error) {
|
func (provider *Provider) splitDomainSOA(ctx context.Context, domain string) (prefix string, zone string, err error) {
|
||||||
c := &dns.Client{Timeout: dnsTimeOut}
|
c := &dns.Client{Timeout: dnsTimeOut}
|
||||||
|
|
||||||
domain += "."
|
domain += "."
|
||||||
indexes := dns.Split(domain)
|
indexes := dns.Split(domain)
|
||||||
|
|
||||||
servers := utils.DNSServers
|
servers := utils.DNSServers
|
||||||
|
|
||||||
customDNSServers, _ := ctx.Value(DNSServerKey{}).([]string)
|
customDNSServers, _ := ctx.Value(DNSServerKey{}).([]string)
|
||||||
if len(customDNSServers) > 0 {
|
if len(customDNSServers) > 0 {
|
||||||
servers = customDNSServers
|
servers = customDNSServers
|
||||||
@@ -224,14 +217,14 @@ func (provider *Provider) splitDomainSOA(ctx context.Context, domain string, ove
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.Answer) > 0 {
|
if r != nil && len(r.Answer) > 0 {
|
||||||
if soa, ok := r.Answer[0].(*dns.SOA); ok {
|
if soa, ok := r.Answer[0].(*dns.SOA); ok {
|
||||||
zone := soa.Hdr.Name
|
zoneName := soa.Hdr.Name
|
||||||
prefix := libdns.RelativeName(domain, zone)
|
pfx := libdns.RelativeName(domain, zoneName)
|
||||||
if prefix == "@" {
|
if pfx == "@" {
|
||||||
prefix = ""
|
pfx = ""
|
||||||
}
|
}
|
||||||
return prefix, zone, nil
|
return pfx, zoneName, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user