mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-03-22 02:51:50 +00:00
ddns: store configuation in database (#435)
* ddns: store configuation in database Co-authored-by: nap0o <144927971+nap0o@users.noreply.github.com> * feat: split domain with soa lookup * switch to libdns interface * ddns: add unit test * ddns: skip TestSplitDomainSOA on ci network is not steady * fix error handling * fix error handling --------- Co-authored-by: nap0o <144927971+nap0o@users.noreply.github.com>
This commit is contained in:
125
pkg/ddns/ddns.go
125
pkg/ddns/ddns.go
@@ -1,24 +1,121 @@
|
||||
package ddns
|
||||
|
||||
import "golang.org/x/net/publicsuffix"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
type DomainConfig struct {
|
||||
EnableIPv4 bool
|
||||
EnableIpv6 bool
|
||||
FullDomain string
|
||||
Ipv4Addr string
|
||||
Ipv6Addr string
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
)
|
||||
|
||||
var dnsTimeOut = 10 * time.Second
|
||||
|
||||
type IP struct {
|
||||
Ipv4Addr string
|
||||
Ipv6Addr string
|
||||
}
|
||||
|
||||
type Provider interface {
|
||||
// UpdateDomain Return is updated
|
||||
UpdateDomain(*DomainConfig) error
|
||||
type Provider struct {
|
||||
ctx context.Context
|
||||
ipAddr string
|
||||
recordType string
|
||||
domain string
|
||||
prefix string
|
||||
zone string
|
||||
|
||||
DDNSProfile *model.DDNSProfile
|
||||
IPAddrs *IP
|
||||
Setter libdns.RecordSetter
|
||||
}
|
||||
|
||||
func splitDomain(domain string) (prefix string, realDomain string) {
|
||||
realDomain, _ = publicsuffix.EffectiveTLDPlusOne(domain)
|
||||
prefix = domain[:len(domain)-len(realDomain)-1]
|
||||
return prefix, realDomain
|
||||
func (provider *Provider) UpdateDomain(ctx context.Context) {
|
||||
provider.ctx = ctx
|
||||
for _, domain := range provider.DDNSProfile.Domains {
|
||||
for retries := 0; retries < int(provider.DDNSProfile.MaxRetries); retries++ {
|
||||
provider.domain = domain
|
||||
log.Printf("NEZHA>> 正在尝试更新域名(%s)DDNS(%d/%d)", provider.domain, retries+1, provider.DDNSProfile.MaxRetries)
|
||||
if err := provider.updateDomain(); err != nil {
|
||||
log.Printf("NEZHA>> 尝试更新域名(%s)DDNS失败: %v", provider.domain, err)
|
||||
} else {
|
||||
log.Printf("NEZHA>> 尝试更新域名(%s)DDNS成功", provider.domain)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) updateDomain() error {
|
||||
var err error
|
||||
provider.prefix, provider.zone, err = splitDomainSOA(provider.domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 当IPv4和IPv6同时成功才算作成功
|
||||
if *provider.DDNSProfile.EnableIPv4 {
|
||||
provider.recordType = getRecordString(true)
|
||||
provider.ipAddr = provider.IPAddrs.Ipv4Addr
|
||||
if err = provider.addDomainRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if *provider.DDNSProfile.EnableIPv6 {
|
||||
provider.recordType = getRecordString(false)
|
||||
provider.ipAddr = provider.IPAddrs.Ipv6Addr
|
||||
if err = provider.addDomainRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Provider) addDomainRecord() error {
|
||||
_, err := provider.Setter.SetRecords(provider.ctx, provider.zone,
|
||||
[]libdns.Record{
|
||||
{
|
||||
Type: provider.recordType,
|
||||
Name: provider.prefix,
|
||||
Value: provider.ipAddr,
|
||||
TTL: time.Minute,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func splitDomainSOA(domain string) (prefix string, zone string, err error) {
|
||||
c := &dns.Client{Timeout: dnsTimeOut}
|
||||
|
||||
domain += "."
|
||||
indexes := dns.Split(domain)
|
||||
|
||||
var r *dns.Msg
|
||||
for _, idx := range indexes {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(domain[idx:], dns.TypeSOA)
|
||||
|
||||
for _, server := range utils.DNSServers {
|
||||
r, _, err = c.Exchange(m, server)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(r.Answer) > 0 {
|
||||
if soa, ok := r.Answer[0].(*dns.SOA); ok {
|
||||
zone = soa.Hdr.Name
|
||||
prefix = domain[:len(domain)-len(zone)-1]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("SOA record not found for domain: %s", domain)
|
||||
}
|
||||
|
||||
func getRecordString(isIpv4 bool) string {
|
||||
|
||||
Reference in New Issue
Block a user