重构ddns相关代码
ddns前后端细节优化
修复webhook相关bug
This commit is contained in:
古大羊 2022-08-08 21:06:24 +08:00
parent a5b58811a3
commit 6a1ce52507
33 changed files with 1426 additions and 832 deletions

2
.gitignore vendored
View File

@ -15,7 +15,7 @@ lucky
*.out
*.log
*.upx
lucky.conf
# Dependency directories (remove the comment below to include it)
# vendor/

View File

@ -6,12 +6,13 @@ import (
)
type AppInfo struct {
AppName string
Version string
OS string
ARCH string
Date string
RunTime string
AppName string
Version string
OS string
ARCH string
Date string
RunTime string
GoVersion string
}
var appInfo AppInfo
@ -27,6 +28,7 @@ func InitAppInfo(version, date string) {
appInfo.OS = runtime.GOOS
appInfo.ARCH = runtime.GOARCH
appInfo.RunTime = time.Now().Format("2006-01-02 15:04:05")
appInfo.GoVersion = runtime.Version()
time.Now().Format("2006-01-02T15:04:05Z")

View File

@ -1,4 +1,4 @@
//Copyright 2022 gdy, 272288813@qq.com
// Copyright 2022 gdy, 272288813@qq.com
package config
import (
@ -6,6 +6,7 @@ import (
"fmt"
"log"
"net"
"runtime"
"strings"
"sync"
@ -68,7 +69,7 @@ var programConfigureMutex sync.RWMutex
var programConfigure *ProgramConfigure
var configurePath string
//var readConfigureFileOnce sync.Once
// var readConfigureFileOnce sync.Once
var checkConfigureFileOnce sync.Once
var configureFileSign int8 = -1
@ -108,8 +109,7 @@ func SetConfig(p *ProgramConfigure) error {
func GetConfig() *ProgramConfigure {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var conf ProgramConfigure
conf = *programConfigure
conf := *programConfigure
return &conf
}
@ -141,7 +141,7 @@ func GetDDNSConfigure() DDNSConfigure {
return conf
}
//保存基础配置
// 保存基础配置
func SetBaseConfigure(conf *BaseConfigure) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
@ -179,6 +179,11 @@ func SetDDNSConfigure(conf *DDNSConfigure) error {
func Read(filePath string) (err error) {
if runtime.GOOS == "windows" && filePath == "" {
filePath = "lucky.conf"
log.Printf("未指定配置文件路径,使用默认路径lucky所在位置,默认配置文件名lucky.conf")
}
pc, err := readProgramConfigure(filePath)
if err != nil {
return err

View File

@ -2,25 +2,11 @@ package config
import (
"fmt"
"io/ioutil"
"log"
"regexp"
"strings"
"sync"
"time"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
)
// Ipv4Reg IPv4正则
const Ipv4Reg = `((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])`
// Ipv6Reg IPv6正则
const Ipv6Reg = `((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))`
var ipUrlAddrMap sync.Map
type DDNSConfigure struct {
Enable bool `json:"Enable"`
HttpClientSecureVerify bool `json:"HttpClientSecureVerify"`
@ -45,23 +31,21 @@ type DDNSTask struct {
Webhook
TTL string `json:"TTL"`
HttpClientTimeout int `json:"HttpClientTimeout"`
//-------------------------------------
//IpCache IpCache `json:"-"`
DomainsState DomainsState `json:"-"`
}
type Webhook struct {
WebhookEnable bool `json:"WebhookEnable"` //Webhook开关
WebhookCallOnGetIPfail bool `json:"WebhookCallOnGetIPfail"` //获取IP失败时触发Webhook 开关
WebhookURL string `json:"WebhookURL"`
WebhookMethod string `json:"WebhookMethod"`
WebhookHeaders []string `json:"WebhookHeaders"`
WebhookRequestBody string `json:"WebhookRequestBody"`
WebhookSuccessContent []string `json:"WebhookSuccessContent"` //接口调用成功包含的内容
WebhookProxy string `json:"WebhookProxy"` //使用DNS代理设置 ""表示禁用,"dns"表示使用dns的代理设置
WebhookProxyAddr string `json:"WebhookProxyAddr"` //代理服务器IP
WebhookProxyUser string `json:"WebhookProxyUser"` //代理用户
WebhookProxyPassword string `json:"WebhookProxyPassword"` //代理密码
WebhookEnable bool `json:"WebhookEnable"` //Webhook开关
WebhookCallOnGetIPfail bool `json:"WebhookCallOnGetIPfail"` //获取IP失败时触发Webhook 开关
WebhookURL string `json:"WebhookURL"`
WebhookMethod string `json:"WebhookMethod"`
WebhookHeaders []string `json:"WebhookHeaders"`
WebhookRequestBody string `json:"WebhookRequestBody"`
WebhookDisableCallbackSuccessContentCheck bool `json:"WebhookDisableCallbackSuccessContentCheck"` //禁用成功调用返回检测
WebhookSuccessContent []string `json:"WebhookSuccessContent"` //接口调用成功包含的内容
WebhookProxy string `json:"WebhookProxy"` //使用DNS代理设置 ""表示禁用,"dns"表示使用dns的代理设置
WebhookProxyAddr string `json:"WebhookProxyAddr"` //代理服务器IP
WebhookProxyUser string `json:"WebhookProxyUser"` //代理用户
WebhookProxyPassword string `json:"WebhookProxyPassword"` //代理密码
}
// DNSConfig DNS配置
@ -81,26 +65,13 @@ type DNSConfig struct {
}
type DNSCallback struct {
URL string `json:"URL"` //请求地址
Method string `json:"Method"` //请求方法
Headers []string `json:"Headers"`
RequestBody string `json:"RequestBody"`
Server string `json:"Server"` //预设服务商
CallbackSuccessContent []string `json:"CallbackSuccessContent"` //接口调用成功包含内容
}
//Check 检测IP是否有改变
func (d *DDNSTask) IPChangeCheck(newAddr string) bool {
if newAddr == "" {
return true
}
// 地址改变
if d.DomainsState.IpAddr != newAddr {
//log.Printf("公网地址改变:[%s]===>[%s]", d.DomainsInfo.IpAddr, newAddr)
d.DomainsState.IpAddr = newAddr
return true
}
return false
URL string `json:"URL"` //请求地址
Method string `json:"Method"` //请求方法
Headers []string `json:"Headers"`
RequestBody string `json:"RequestBody"`
Server string `json:"Server"` //预设服务商
DisableCallbackSuccessContentCheck bool `json:"DisableCallbackSuccessContentCheck"` //禁用成功调用返回检测
CallbackSuccessContent []string `json:"CallbackSuccessContent"` //接口调用成功包含内容
}
var checkIPv4URLList = []string{"https://4.ipw.cn", "http://v4.ip.zxinc.org/getip", "https://myip4.ipip.net", "https://www.taobao.com/help/getip.php", "https://ddns.oray.com/checkip", "https://ip.3322.net", "https://v4.myip.la"}
@ -157,25 +128,10 @@ var DefaultIPv4DNSServerList = []string{
// programConfigure.DDNSTaskList[taskIndex].IpCache.ForceCompare = force
// }
func CleanIPUrlAddrMap() {
keys := []string{}
ipUrlAddrMap.Range(func(key, value any) bool {
keys = append(keys, key.(string))
return true
})
for _, k := range keys {
ipUrlAddrMap.Delete(k)
}
}
func DDNSTaskListTaskDetailsInit() {
func DDNSTaskListConfigureCheck() {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
for i := range programConfigure.DDNSTaskList {
programConfigure.DDNSTaskList[i].DomainsState.Init(programConfigure.DDNSTaskList[i].Domains)
programConfigure.DDNSTaskList[i].DomainsState.SetDomainUpdateStatus(UpdateWaiting, "")
//
if programConfigure.DDNSTaskList[i].DNS.ForceInterval < 60 {
programConfigure.DDNSTaskList[i].DNS.ForceInterval = 60
} else if programConfigure.DDNSTaskList[i].DNS.ForceInterval > 360000 {
@ -190,23 +146,6 @@ func DDNSTaskListTaskDetailsInit() {
}
}
func DDNSTaskIPCacheCheck(taskKey, ip string) (bool, error) {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
taskIndex := -1
for i := range programConfigure.DDNSTaskList {
if programConfigure.DDNSTaskList[i].TaskKey == taskKey {
taskIndex = i
break
}
}
if taskIndex == -1 {
return true, fmt.Errorf("找不到key对应的DDNS任务")
}
return programConfigure.DDNSTaskList[taskIndex].IPChangeCheck(ip), nil
}
func DDNSTaskSetWebhookCallResult(taskKey string, result bool, message string) {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
@ -226,29 +165,20 @@ func DDNSTaskSetWebhookCallResult(taskKey string, result bool, message string) {
}
type DDNSTaskDetails struct {
DDNSTask
TaskState DomainsState `json:"TaskState"`
}
func GetDDNSTaskList() []DDNSTaskDetails {
func GetDDNSTaskConfigureList() []*DDNSTask {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var resList []DDNSTaskDetails
var resList []*DDNSTask
for i := range programConfigure.DDNSTaskList {
var info DDNSTaskDetails
programConfigure.DDNSTaskList[i].DomainsState.Mutex.RLock()
info.DDNSTask = programConfigure.DDNSTaskList[i]
info.TaskState = programConfigure.DDNSTaskList[i].DomainsState
programConfigure.DDNSTaskList[i].DomainsState.Mutex.RUnlock()
resList = append(resList, info)
task := programConfigure.DDNSTaskList[i]
resList = append(resList, &task)
}
return resList
}
func GetDDNSTaskByKey(taskKey string) *DDNSTaskDetails {
func GetDDNSTaskByKey(taskKey string) *DDNSTask {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
taskIndex := -1
@ -262,51 +192,15 @@ func GetDDNSTaskByKey(taskKey string) *DDNSTaskDetails {
if taskIndex == -1 {
return nil
}
var info DDNSTaskDetails
programConfigure.DDNSTaskList[taskIndex].DomainsState.Mutex.RLock()
info.DDNSTask = programConfigure.DDNSTaskList[taskIndex]
info.TaskState = programConfigure.DDNSTaskList[taskIndex].DomainsState
programConfigure.DDNSTaskList[taskIndex].DomainsState.Mutex.RUnlock()
return &info
}
res := programConfigure.DDNSTaskList[taskIndex]
func DDNSTaskListFlushDomainsDetails(taskKey string, state *DomainsState) {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
taskIndex := -1
for i := range programConfigure.DDNSTaskList {
if programConfigure.DDNSTaskList[i].TaskKey == taskKey {
taskIndex = i
break
}
}
if taskIndex == -1 {
return
}
var checkDomains []*Domain
//防止有域名被删除
for _, new := range state.Domains {
for j, pre := range programConfigure.DDNSTaskList[taskIndex].DomainsState.Domains {
if strings.Compare(new.String(), pre.String()) == 0 {
checkDomains = append(checkDomains, programConfigure.DDNSTaskList[taskIndex].DomainsState.Domains[j])
break
}
}
}
state.Domains = checkDomains
programConfigure.DDNSTaskList[taskIndex].DomainsState = *state
return &res
}
func DDNSTaskListAdd(task *DDNSTask) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
task.TaskKey = stringsp.GetRandomString(16)
task.DomainsState.Init(task.Domains)
task.DomainsState.SetDomainUpdateStatus(UpdateWaiting, "")
programConfigure.DDNSTaskList = append(programConfigure.DDNSTaskList, *task)
return Save()
}
@ -382,31 +276,10 @@ func EnableDDNSTaskByKey(taskKey string, enable bool) error {
return fmt.Errorf("开关DDNS任务失败,TaskKey不存在")
}
programConfigure.DDNSTaskList[taskIndex].Enable = enable
if enable {
programConfigure.DDNSTaskList[taskIndex].DomainsState.SetDomainUpdateStatus(UpdateWaiting, "")
} else {
programConfigure.DDNSTaskList[taskIndex].DomainsState.SetDomainUpdateStatus(UpdateStop, "")
}
return Save()
}
func UpdateDomainsStateByTaskKey(taskKey string, status updateStatusType, message string) {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
taskIndex := -1
for i := range programConfigure.DDNSTaskList {
if programConfigure.DDNSTaskList[i].TaskKey == taskKey {
taskIndex = i
break
}
}
if taskIndex == -1 {
return
}
programConfigure.DDNSTaskList[taskIndex].DomainsState.SetDomainUpdateStatus(status, message)
}
func UpdateTaskToDDNSTaskList(taskKey string, task DDNSTask) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
@ -434,15 +307,7 @@ func UpdateTaskToDDNSTaskList(taskKey string, task DDNSTask) error {
programConfigure.DDNSTaskList[taskIndex].DNS = task.DNS
programConfigure.DDNSTaskList[taskIndex].Webhook = task.Webhook
programConfigure.DDNSTaskList[taskIndex].TTL = task.TTL
programConfigure.DDNSTaskList[taskIndex].DomainsState.IpAddr = ""
programConfigure.DDNSTaskList[taskIndex].DomainsState.Init(task.Domains)
programConfigure.DDNSTaskList[taskIndex].DomainsState.IPAddrHistory = task.DomainsState.IPAddrHistory
programConfigure.DDNSTaskList[taskIndex].DomainsState.WebhookCallHistroy = task.DomainsState.WebhookCallHistroy
programConfigure.DDNSTaskList[taskIndex].DomainsState.SetDomainUpdateStatus(UpdateWaiting, "")
programConfigure.DDNSTaskList[taskIndex].HttpClientTimeout = task.HttpClientTimeout
programConfigure.DDNSTaskList[taskIndex].DomainsState.WebhookCallErrorMsg = ""
programConfigure.DDNSTaskList[taskIndex].DomainsState.WebhookCallResult = false
programConfigure.DDNSTaskList[taskIndex].DomainsState.WebhookCallTime = ""
return Save()
}
@ -459,165 +324,3 @@ func DeleteDDNSTaskListlice(a []DDNSTask, deleteIndex int) []DDNSTask {
}
//****************************
func (d *DDNSTask) GetIpAddr() (result string) {
if d.TaskType == "IPv6" {
return d.getIpv6Addr()
}
return d.getIpv4Addr()
}
// getIpv4Addr 获得IPv4地址
func (d *DDNSTask) getIpv4Addr() (result string) {
// 判断从哪里获取IP
if d.GetType == "netInterface" {
result = GetIPFromNetInterface("IPv4", d.NetInterface, d.IPReg)
// 从网卡获取IP
// ipv4, _, err := GetNetInterface()
// if err != nil {
// log.Println("从网卡获得IPv4失败!")
// return
// }
// for _, netInterface := range ipv4 {
// if netInterface.NetInterfaceName == d.NetInterface && len(netInterface.AddressList) > 0 {
// return netInterface.AddressList[0]
// }
// }
// log.Println("从网卡中获得IPv4失败! 网卡名: ", d.NetInterface)
return
}
ddnsGlobalConf := GetDDNSConfigure()
client, err := httputils.CreateHttpClient(
ddnsGlobalConf.HttpClientSecureVerify,
"",
"",
"",
"",
time.Duration(d.HttpClientTimeout)*time.Second)
if err != nil {
log.Printf("%s", err.Error())
return
}
for _, url := range d.URL {
url = strings.TrimSpace(url)
mapIp, ok := ipUrlAddrMap.Load(url)
if ok {
//log.Printf("URL[%s]已缓存IP[%s]", url, mapIp)
result = mapIp.(string)
return
}
resp, err := client.Get(url)
if err != nil {
//log.Printf("连接失败!%s查看接口能否返回IPv4地址</a>,", url)
continue
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取IPv4结果失败! 接口:%s", url)
continue
}
comp := regexp.MustCompile(Ipv4Reg)
result = comp.FindString(string(body))
if result != "" {
ipUrlAddrMap.Store(url, result)
return
}
// else {
// log.Printf("获取IPv4结果失败! 接口: %s ,返回值: %s\n", url, result)
// }
}
log.Printf("所有查询公网IPv4的接口均获取IPv4结果失败,请检查接口或当前网络情况")
return
}
// getIpv6Addr 获得IPv6地址
func (d *DDNSTask) getIpv6Addr() (result string) {
// 判断从哪里获取IP
if d.GetType == "netInterface" {
// 从网卡获取IP
// _, ipv6, err := GetNetInterface()
// if err != nil {
// log.Println("从网卡获得IPv6失败!")
// return
// }
// for _, netInterface := range ipv6 {
// if netInterface.NetInterfaceName == d.NetInterface && len(netInterface.AddressList) > 0 {
// if d.IPReg != "" {
// log.Printf("IPv6将使用正则表达式 %s 进行匹配\n", d.IPReg)
// for i := 0; i < len(netInterface.AddressList); i++ {
// matched, err := regexp.MatchString(d.IPReg, netInterface.AddressList[i])
// if matched && err == nil {
// log.Println("匹配成功! 匹配到地址: ", netInterface.AddressList[i])
// return netInterface.AddressList[i]
// }
// log.Printf("第 %d 个地址 %s 不匹配, 将匹配下一个地址\n", i+1, netInterface.AddressList[i])
// }
// log.Println("没有匹配到任何一个IPv6地址, 将使用第一个地址")
// }
// return netInterface.AddressList[0]
// }
// }
// log.Println("从网卡中获得IPv6失败! 网卡名: ", d.NetInterface)
result = GetIPFromNetInterface("IPv6", d.NetInterface, d.IPReg)
return
}
ddnsGlobalConf := GetDDNSConfigure()
client, err := httputils.CreateHttpClient(
!ddnsGlobalConf.HttpClientSecureVerify,
"",
"",
"",
"",
time.Duration(d.HttpClientTimeout)*time.Second)
if err != nil {
log.Printf("%s", err.Error())
return
}
for _, url := range d.URL {
url = strings.TrimSpace(url)
mapIp, ok := ipUrlAddrMap.Load(url)
if ok {
//log.Printf("URL[%s]已缓存IP[%s]", url, mapIp)
result = mapIp.(string)
return
}
resp, err := client.Get(url)
if err != nil {
//log.Printf("连接失败! %s查看接口能否返回IPv6地址 ", url)
continue
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("读取IPv6结果失败! 接口: ", url)
continue
}
comp := regexp.MustCompile(Ipv6Reg)
result = comp.FindString(string(body))
if result != "" {
ipUrlAddrMap.Store(url, result)
return
}
}
log.Printf("所有查询公网IPv6的接口均获取IPv6结果失败,请检查接口或当前网络情况")
return
}

View File

@ -1,10 +1,10 @@
//Copyright 2022 gdy, 272288813@qq.com
// Copyright 2022 gdy, 272288813@qq.com
package config
import "time"
type WhiteListConfigure struct {
BaseConfigure WhiteListBaseConfigure `json:"BaseConfigure`
BaseConfigure WhiteListBaseConfigure `json:"BaseConfigure"`
WhiteList []WhiteListItem `json:"WhiteList"` //白名单列表
}

View File

@ -6,7 +6,7 @@ import (
"net/http"
"net/url"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
"github.com/gdy666/lucky/thirdlib/jeessy2/ddns-go/util"
)
@ -22,15 +22,18 @@ type Alidns struct {
TTL string
}
// AlidnsRecord record
type AlidnsRecord struct {
DomainName string
RecordID string
Value string
}
// AlidnsSubDomainRecords 记录
type AlidnsSubDomainRecords struct {
TotalCount int
DomainRecords struct {
Record []struct {
DomainName string
RecordID string
Value string
}
Record []AlidnsRecord
}
}
@ -41,7 +44,7 @@ type AlidnsResp struct {
}
// Init 初始化
func (ali *Alidns) Init(task *config.DDNSTask) {
func (ali *Alidns) Init(task *ddnscore.DDNSTaskInfo) {
ali.DNSCommon.Init(task)
if task.TTL == "" {
@ -53,26 +56,33 @@ func (ali *Alidns) Init(task *config.DDNSTask) {
ali.SetCreateUpdateDomainFunc(ali.createUpdateDomain)
}
func (ali *Alidns) createUpdateDomain(recordType, ipAddr string, domain *config.Domain) {
var record AlidnsSubDomainRecords
func (ali *Alidns) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
var records AlidnsSubDomainRecords
// 获取当前域名信息
params := url.Values{}
params := domain.GetCustomParams()
params.Set("Action", "DescribeSubDomainRecords")
params.Set("DomainName", domain.DomainName)
params.Set("SubDomain", domain.GetFullDomain())
params.Set("Type", recordType)
err := ali.request(params, &record)
err := ali.request(params, &records)
if err != nil {
errMsg := "更新失败[001]:\n"
errMsg += err.Error()
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
return
}
if record.TotalCount > 0 {
if records.TotalCount > 0 {
// 默认第一个
recordSelected := records.DomainRecords.Record[0]
if params.Has("RecordId") {
for i := 0; i < len(records.DomainRecords.Record); i++ {
if records.DomainRecords.Record[i].RecordID == params.Get("RecordId") {
recordSelected = records.DomainRecords.Record[i]
}
}
}
// 存在,更新
ali.modify(record, domain, recordType, ipAddr)
ali.modify(recordSelected, domain, recordType, ipAddr)
} else {
// 不存在,创建
ali.create(domain, recordType, ipAddr)
@ -80,8 +90,8 @@ func (ali *Alidns) createUpdateDomain(recordType, ipAddr string, domain *config.
}
// 创建
func (ali *Alidns) create(domain *config.Domain, recordType string, ipAddr string) {
params := url.Values{}
func (ali *Alidns) create(domain *ddnscore.Domain, recordType string, ipAddr string) {
params := domain.GetCustomParams()
params.Set("Action", "AddDomainRecord")
params.Set("DomainName", domain.DomainName)
params.Set("RR", domain.GetSubDomain())
@ -93,28 +103,29 @@ func (ali *Alidns) create(domain *config.Domain, recordType string, ipAddr strin
err := ali.request(params, &result)
if err == nil && result.RecordID != "" {
//log.Printf("新增域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("新增域名解析 %s 失败!", domain)
domain.SetDomainUpdateStatus(config.UpdatedFailed, err.Error())
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
}
}
// 修改
func (ali *Alidns) modify(record AlidnsSubDomainRecords, domain *config.Domain, recordType string, ipAddr string) {
func (ali *Alidns) modify(recordSelected AlidnsRecord, domain *ddnscore.Domain, recordType string, ipAddr string) {
// 相同不修改
if len(record.DomainRecords.Record) > 0 && record.DomainRecords.Record[0].Value == ipAddr {
//log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
domain.SetDomainUpdateStatus(config.UpdatedNothing, "")
if recordSelected.Value == ipAddr {
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
return
}
params := url.Values{}
params := domain.GetCustomParams()
params.Set("Action", "UpdateDomainRecord")
params.Set("RR", domain.GetSubDomain())
params.Set("RecordId", record.DomainRecords.Record[0].RecordID)
params.Set("RecordId", recordSelected.RecordID)
params.Set("Type", recordType)
params.Set("Value", ipAddr)
params.Set("TTL", ali.TTL)
@ -123,11 +134,9 @@ func (ali *Alidns) modify(record AlidnsSubDomainRecords, domain *config.Domain,
err := ali.request(params, &result)
if err == nil && result.RecordID != "" {
//log.Printf("更新域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("更新域名解析 %s 失败!", domain)
domain.SetDomainUpdateStatus(config.UpdatedFailed, err.Error())
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
}
}
@ -154,9 +163,9 @@ func (ali *Alidns) request(params url.Values, result interface{}) (err error) {
}
resp, err := client.Do(req)
//err = util.GetHTTPResponse(resp, alidnsEndpoint, err, result)
if err != nil {
return err
}
err = httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
return
return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
}

View File

@ -7,7 +7,7 @@ import (
"net/http"
"strconv"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
"github.com/gdy666/lucky/thirdlib/jeessy2/ddns-go/util"
)
@ -68,7 +68,7 @@ type BaiduCreateRequest struct {
ZoneName string `json:"zoneName"`
}
func (baidu *BaiduCloud) Init(task *config.DDNSTask) {
func (baidu *BaiduCloud) Init(task *ddnscore.DDNSTaskInfo) {
baidu.DNSCommon.Init(task)
if task.TTL == "" {
@ -85,7 +85,7 @@ func (baidu *BaiduCloud) Init(task *config.DDNSTask) {
baidu.SetCreateUpdateDomainFunc(baidu.createUpdateDomain)
}
func (baidu *BaiduCloud) createUpdateDomain(recordType, ipAddr string, domain *config.Domain) {
func (baidu *BaiduCloud) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
var records BaiduRecordsResp
requestBody := BaiduListRequest{
@ -98,7 +98,7 @@ func (baidu *BaiduCloud) createUpdateDomain(recordType, ipAddr string, domain *c
if err != nil {
errMsg := "更新失败[001]:\n"
errMsg += err.Error()
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
@ -117,8 +117,8 @@ func (baidu *BaiduCloud) createUpdateDomain(recordType, ipAddr string, domain *c
}
}
//create 创建新的解析
func (baidu *BaiduCloud) create(domain *config.Domain, recordType string, ipAddr string) {
// create 创建新的解析
func (baidu *BaiduCloud) create(domain *ddnscore.Domain, recordType string, ipAddr string) {
var baiduCreateRequest = BaiduCreateRequest{
Domain: domain.GetSubDomain(), //处理一下@
RdType: recordType,
@ -131,19 +131,22 @@ func (baidu *BaiduCloud) create(domain *config.Domain, recordType string, ipAddr
err := baidu.request("POST", baiduEndpoint+"/v1/domain/resolve/add", baiduCreateRequest, &result)
if err == nil {
//log.Printf("新增域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("新增域名解析 %s 失败!", domain)
domain.SetDomainUpdateStatus(config.UpdatedFailed, err.Error())
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
}
}
//modify 更新解析
func (baidu *BaiduCloud) modify(record BaiduRecord, domain *config.Domain, rdType string, ipAddr string) {
// modify 更新解析
func (baidu *BaiduCloud) modify(record BaiduRecord, domain *ddnscore.Domain, rdType string, ipAddr string) {
//没有变化直接跳过
if record.Rdata == ipAddr {
//log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
domain.SetDomainUpdateStatus(config.UpdatedNothing, "")
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
return
}
var baiduModifyRequest = BaiduModifyRequest{
@ -160,10 +163,10 @@ func (baidu *BaiduCloud) modify(record BaiduRecord, domain *config.Domain, rdTyp
err := baidu.request("POST", baiduEndpoint+"/v1/domain/resolve/edit", baiduModifyRequest, &result)
if err == nil {
//log.Printf("更新域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("更新域名解析 %s 失败!", domain)
domain.SetDomainUpdateStatus(config.UpdatedFailed, err.Error())
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
}
}
@ -193,7 +196,9 @@ func (baidu *BaiduCloud) request(method string, url string, data interface{}, re
}
resp, err := client.Do(req)
err = httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
if err != nil {
return err
}
return
return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
}

View File

@ -2,10 +2,12 @@ package ddns
import (
"fmt"
"net/http"
"strings"
"time"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
@ -15,7 +17,7 @@ type Callback struct {
}
// Init 初始化
func (cb *Callback) Init(task *config.DDNSTask) {
func (cb *Callback) Init(task *ddnscore.DDNSTaskInfo) {
cb.DNSCommon.Init(task)
if task.TTL == "" {
@ -37,7 +39,7 @@ func CopyHeadersMap(sm map[string]string) map[string]string {
return dm
}
func (cb *Callback) createUpdateDomain(recordType, ipAddr string, domain *config.Domain) {
func (cb *Callback) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
url := replacePara(cb.task.DNS.Callback.URL, ipAddr, domain, recordType, cb.TTL)
requestBody := replacePara(cb.task.DNS.Callback.RequestBody, ipAddr, domain, recordType, cb.TTL)
@ -60,19 +62,25 @@ func (cb *Callback) createUpdateDomain(recordType, ipAddr string, domain *config
callErr := cb.CallbackHttpClientDo(cb.task.DNS.Callback.Method, url, requestBody, headers, succcssCotentList)
if callErr != nil {
domain.SetDomainUpdateStatus(config.UpdatedFailed, callErr.Error())
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, callErr.Error())
return
}
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
}
// replacePara 替换参数
func replacePara(orgPara, ipAddr string, domain *config.Domain, recordType string, ttl string) (newPara string) {
func replacePara(orgPara, ipAddr string, domain *ddnscore.Domain, recordType string, ttl string) (newPara string) {
orgPara = strings.ReplaceAll(orgPara, "#{ip}", ipAddr)
orgPara = strings.ReplaceAll(orgPara, "#{domain}", domain.String())
orgPara = strings.ReplaceAll(orgPara, "#{recordType}", recordType)
orgPara = strings.ReplaceAll(orgPara, "#{ttl}", ttl)
for k, v := range domain.GetCustomParams() {
if len(v) == 1 {
orgPara = strings.ReplaceAll(orgPara, "#{"+k+"}", v[0])
}
}
return orgPara
}
@ -80,7 +88,9 @@ func (cb *Callback) CallbackHttpClientDo(method, url, requestBody string, header
globalDDNSConf := config.GetDDNSConfigure()
dnsConf := cb.task.DNS
respStr, err := httputils.GetStringGoutDoHttpRequest(
statusCode, respStr, err := httputils.GetStringGoutDoHttpRequest(
"tcp",
"",
method,
url,
requestBody,
@ -94,8 +104,18 @@ func (cb *Callback) CallbackHttpClientDo(method, url, requestBody string, header
if err != nil {
return fmt.Errorf("Callback 调用接口[%s]出错:%s", url, err.Error())
}
if cb.task.DNS.Callback.DisableCallbackSuccessContentCheck {
if statusCode == http.StatusOK {
return nil
}
return fmt.Errorf("调用接口失败:\n statusCode:%d\n%s", statusCode, respStr)
}
//log.Printf("接口[%s]调用响应:%s\n", url, respStr)
//fmt.Printf("statusCode:%d\n", statusCode)
for _, successContent := range callbackSuccessContent {
if strings.Contains(respStr, successContent) {
return nil

View File

@ -8,7 +8,7 @@ import (
"net/http"
"strconv"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
@ -56,7 +56,7 @@ type CloudflareStatus struct {
}
// Init 初始化
func (cf *Cloudflare) Init(task *config.DDNSTask) {
func (cf *Cloudflare) Init(task *ddnscore.DDNSTaskInfo) {
cf.DNSCommon.Init(task)
if task.TTL == "" {
@ -73,7 +73,7 @@ func (cf *Cloudflare) Init(task *config.DDNSTask) {
cf.SetCreateUpdateDomainFunc(cf.createUpdateDomain)
}
func (cf *Cloudflare) createUpdateDomain(recordType, ipAddr string, domain *config.Domain) {
func (cf *Cloudflare) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
result, err := cf.getZones(domain)
if err != nil || len(result.Result) != 1 {
errMsg := "更新失败[001]:\n"
@ -82,7 +82,7 @@ func (cf *Cloudflare) createUpdateDomain(recordType, ipAddr string, domain *conf
} else {
errMsg += fmt.Sprintf("%v", result)
}
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
zoneID := result.Result[0].ID
@ -101,7 +101,7 @@ func (cf *Cloudflare) createUpdateDomain(recordType, ipAddr string, domain *conf
if err != nil {
errMsg += err.Error()
}
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
@ -115,7 +115,7 @@ func (cf *Cloudflare) createUpdateDomain(recordType, ipAddr string, domain *conf
}
// 创建
func (cf *Cloudflare) create(zoneID string, domain *config.Domain, recordType string, ipAddr string) {
func (cf *Cloudflare) create(zoneID string, domain *ddnscore.Domain, recordType string, ipAddr string) {
record := &CloudflareRecord{
Type: recordType,
Name: domain.String(),
@ -132,21 +132,24 @@ func (cf *Cloudflare) create(zoneID string, domain *config.Domain, recordType st
)
if err == nil && status.Success {
//log.Printf("新增域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("新增域名解析 %s 失败Messages: %s", domain, status.Messages)
domain.SetDomainUpdateStatus(config.UpdatedFailed, err.Error())
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
}
}
// 修改
func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain *config.Domain, recordType string, ipAddr string) {
func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain *ddnscore.Domain, recordType string, ipAddr string) {
for _, record := range result.Result {
// 相同不修改
if record.Content == ipAddr {
//log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
domain.SetDomainUpdateStatus(config.UpdatedNothing, "")
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
continue
}
var status CloudflareStatus
@ -162,20 +165,20 @@ func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain
if err == nil && status.Success {
//log.Printf("更新域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("更新域名解析 %s 失败Messages: %s", domain, status.Messages)
errMsg := "更新失败"
if err != nil {
errMsg = err.Error()
}
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
}
}
}
// 获得域名记录列表
func (cf *Cloudflare) getZones(domain *config.Domain) (result CloudflareZonesResp, err error) {
func (cf *Cloudflare) getZones(domain *ddnscore.Domain) (result CloudflareZonesResp, err error) {
err = cf.request(
"GET",
fmt.Sprintf(zonesAPI+"?name=%s&status=%s&per_page=%s", domain.DomainName, "active", "50"),
@ -210,7 +213,9 @@ func (cf *Cloudflare) request(method string, url string, data interface{}, resul
}
resp, err := client.Do(req)
err = httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
if err != nil {
return err
}
return
return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
}

View File

@ -1,10 +1,10 @@
package ddns
import "github.com/gdy666/lucky/config"
import "github.com/gdy666/lucky/ddnscore.go"
// DNS interface
type DNS interface {
Init(task *config.DDNSTask)
Init(task *ddnscore.DDNSTaskInfo)
// 添加或更新IPv4/IPv6记录
AddUpdateDomainRecords() string
}

View File

@ -8,26 +8,28 @@ import (
"time"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
"github.com/miekg/dns"
"golang.org/x/net/idna"
)
type DNSCommon struct {
//Domains *config.Domains
createUpdateDomainFunc func(recordType, ipaddr string, domain *config.Domain)
task *config.DDNSTask
createUpdateDomainFunc func(recordType, ipaddr string, domain *ddnscore.Domain)
task *ddnscore.DDNSTaskInfo
}
func (d *DNSCommon) SetCreateUpdateDomainFunc(f func(recordType, ipaddr string, domain *config.Domain)) {
func (d *DNSCommon) SetCreateUpdateDomainFunc(f func(recordType, ipaddr string, domain *ddnscore.Domain)) {
d.createUpdateDomainFunc = f
}
func (d *DNSCommon) Init(task *config.DDNSTask) {
func (d *DNSCommon) Init(task *ddnscore.DDNSTaskInfo) {
d.task = task
}
//添加或更新IPv4/IPv6记录
// 添加或更新IPv4/IPv6记录
func (d *DNSCommon) AddUpdateDomainRecords() string {
if d.task.TaskType == "IPv6" {
@ -37,19 +39,26 @@ func (d *DNSCommon) AddUpdateDomainRecords() string {
}
func (d *DNSCommon) addUpdateDomainRecords(recordType string) string {
ipAddr, change, domains := d.task.DomainsState.CheckIPChange(recordType, d.task.TaskKey, d.task.GetIpAddr)
d.task.DomainsState.SetIPAddr(ipAddr)
ipAddr, change := d.task.CheckIPChange()
defer ddnscore.DDNSTaskInfoMapUpdateDomainInfo(d.task)
d.task.TaskState.SetIPAddr(ipAddr)
//及时刷新IP地址显示
config.DDNSTaskListFlushDomainsDetails(d.task.TaskKey, &d.task.DomainsState)
ddnscore.DDNSTaskInfoMapUpdateIPInfo(d.task)
if ipAddr == "" {
d.task.DomainsState.SetDomainUpdateStatus(config.UpdatePause, "获取公网IP失败")
d.task.TaskState.SetDomainUpdateStatus(ddnscore.UpdatePause, "获取公网IP失败")
return ipAddr
}
preFaildDomains := []*config.Domain{}
//var preFaildDomains []ddnscore.Domain
if time.Since(d.task.DomainsState.LastSyncTime) > time.Second*time.Duration(d.task.DNS.ForceInterval) {
checkDoamins := d.task.TaskState.Domains
//log.Printf("时间间隔:%d秒", time.Since(d.task.TaskState.LastSyncTime)/time.Second)
if time.Since(d.task.TaskState.LastSyncTime) > time.Second*time.Duration(d.task.DNS.ForceInterval-1) {
//log.Printf("DDNS任务[%s]强制更新", d.task.TaskName)
change = true
goto sync
@ -58,60 +67,87 @@ func (d *DNSCommon) addUpdateDomainRecords(recordType string) string {
//设置原先状态成功的为继续成功
//不成功的就更新
if !change { //公网IP没有改变
for i := range domains { //如果原先状态成功或不改变就刷新时间
if domains[i].UpdateStatus == config.UpdatedNothing || domains[i].UpdateStatus == config.UpdatedSuccess {
domains[i].SetDomainUpdateStatus(config.UpdatedNothing, "")
checkDoamins = []ddnscore.Domain{}
for i := range d.task.TaskState.Domains { //如果原先状态成功或不改变就刷新时间
if d.task.TaskState.Domains[i].UpdateStatus == ddnscore.UpdatedNothing ||
d.task.TaskState.Domains[i].UpdateStatus == ddnscore.UpdatedSuccess {
d.task.TaskState.Domains[i].SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
ddnscore.DDNSTaskInfoMapUpdateDomainInfo(d.task)
continue
}
preFaildDomains = append(preFaildDomains, domains[i])
checkDoamins = append(checkDoamins, d.task.TaskState.Domains[i])
}
if len(preFaildDomains) == 0 {
if len(checkDoamins) == 0 {
return ipAddr
}
domains = preFaildDomains
}
sync:
if change {
syncTime := time.Now()
defer func() {
//记录最近一次同步操作时间
d.task.DomainsState.LastSyncTime = time.Now()
d.task.TaskState.LastSyncTime = syncTime
}()
}
for _, domain := range domains {
for i, _ := range checkDoamins {
if d.createUpdateDomainFunc == nil {
log.Printf("ddns createUpdateDomainFunc undefine")
break
}
domain := getDomainItem(checkDoamins[i].String(), &d.task.TaskState.Domains)
if domain == nil {
log.Printf("getDomainItem nil")
continue
}
if d.task.DNS.ResolverDoaminCheck {
//<-time.After(time.Second)
domainResolverIPaddr, _ := ResolveDomainAtServerList(recordType, domain.String(), d.task.DNS.DNSServerList)
//log.Printf("domain:%s domainResolverIPaddr:%s ,ipaddr:%s", domain.String(), domainResolverIPaddr, ipAddr)
if domainResolverIPaddr == ipAddr {
if domain.UpdateStatus == config.UpdatedFailed {
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(config.UpdatedNothing, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
ddnscore.DDNSTaskInfoMapUpdateDomainInfo(d.task)
continue
}
}
d.createUpdateDomainFunc(recordType, ipAddr, domain)
ddnscore.DDNSTaskInfoMapUpdateDomainInfo(d.task)
}
return ipAddr
}
func getDomainItem(fullDomain string, domains *[]ddnscore.Domain) *ddnscore.Domain {
if domains == nil {
return nil
}
for i, domain := range *domains {
if domain.String() == fullDomain {
return &(*domains)[i]
}
}
return nil
}
//--------------------------------------------------------------------------------------------------
func (d *DNSCommon) CreateHTTPClient() (*http.Client, error) {
ddnsGlobalConf := config.GetDDNSConfigure()
return httputils.CreateHttpClient(
"tcp",
"",
!ddnsGlobalConf.HttpClientSecureVerify,
d.task.DNS.HttpClientProxyType,
d.task.DNS.HttpClientProxyAddr,
@ -120,7 +156,7 @@ func (d *DNSCommon) CreateHTTPClient() (*http.Client, error) {
time.Duration(d.task.HttpClientTimeout)*time.Second)
}
//---------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------
func ResolveDomainAtServerList(queryType, domain string, dnsServerList []string) (string, error) {
if len(dnsServerList) == 0 {
@ -138,6 +174,11 @@ func ResolveDomainAtServerList(queryType, domain string, dnsServerList []string)
return "", fmt.Errorf("queryType error:%s", queryType)
}
if strings.HasPrefix(domain, "*.") {
randomStr := stringsp.GetRandomString(8)
domain = strings.Replace(domain, "*", randomStr, 1)
}
domain = dns.Fqdn(domain)
domain, err := idna.ToASCII(domain)
if err != nil {

View File

@ -5,7 +5,7 @@ import (
"net/http"
"net/url"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
@ -43,7 +43,7 @@ type DnspodStatus struct {
}
// Init 初始化
func (dnspod *Dnspod) Init(task *config.DDNSTask) {
func (dnspod *Dnspod) Init(task *ddnscore.DDNSTaskInfo) {
dnspod.DNSCommon.Init(task)
if task.TTL == "" {
@ -55,12 +55,12 @@ func (dnspod *Dnspod) Init(task *config.DDNSTask) {
dnspod.SetCreateUpdateDomainFunc(dnspod.createUpdateDomain)
}
func (dnspod *Dnspod) createUpdateDomain(recordType, ipAddr string, domain *config.Domain) {
func (dnspod *Dnspod) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
result, err := dnspod.getRecordList(domain, recordType)
if err != nil {
errMsg := "更新失败[001]:\n"
errMsg += err.Error()
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
@ -74,85 +74,91 @@ func (dnspod *Dnspod) createUpdateDomain(recordType, ipAddr string, domain *conf
}
// 创建
func (dnspod *Dnspod) create(result DnspodRecordListResp, domain *config.Domain, recordType string, ipAddr string) {
status, err := dnspod.commonRequest(
recordCreateAPI,
url.Values{
"login_token": {dnspod.task.DNS.ID + "," + dnspod.task.DNS.Secret},
"domain": {domain.DomainName},
"sub_domain": {domain.GetSubDomain()},
"record_type": {recordType},
"record_line": {"默认"},
"value": {ipAddr},
"ttl": {dnspod.TTL},
"format": {"json"},
},
domain,
)
func (dnspod *Dnspod) create(result DnspodRecordListResp, domain *ddnscore.Domain, recordType string, ipAddr string) {
params := domain.GetCustomParams()
params.Add("login_token", dnspod.task.DNS.ID+","+dnspod.task.DNS.Secret)
params.Add("domain", domain.DomainName)
params.Add("sub_domain", domain.GetSubDomain())
params.Add("record_type", recordType)
params.Add("value", ipAddr)
params.Add("ttl", dnspod.TTL)
params.Add("format", "json")
if !params.Has("record_line") {
params.Add("record_line", "默认")
}
status, err := dnspod.commonRequest(recordCreateAPI, params, domain)
if err == nil && status.Status.Code == "1" {
//log.Printf("新增域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("新增域名解析 %s 失败Code: %s, Message: %s", domain, status.Status.Code, status.Status.Message)
domain.SetDomainUpdateStatus(config.UpdatedFailed, fmt.Sprintf("Code: %s, Message: %s", status.Status.Code, status.Status.Message))
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, fmt.Sprintf("Code: %s, Message: %s", status.Status.Code, status.Status.Message))
}
}
// 修改
func (dnspod *Dnspod) modify(result DnspodRecordListResp, domain *config.Domain, recordType string, ipAddr string) {
func (dnspod *Dnspod) modify(result DnspodRecordListResp, domain *ddnscore.Domain, recordType string, ipAddr string) {
for _, record := range result.Records {
// 相同不修改
if record.Value == ipAddr {
//log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
domain.SetDomainUpdateStatus(config.UpdatedNothing, "")
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
continue
}
status, err := dnspod.commonRequest(
recordModifyURL,
url.Values{
"login_token": {dnspod.task.DNS.ID + "," + dnspod.task.DNS.Secret},
"domain": {domain.DomainName},
"sub_domain": {domain.GetSubDomain()},
"record_type": {recordType},
"record_line": {"默认"},
"record_id": {record.ID},
"value": {ipAddr},
"ttl": {dnspod.TTL},
"format": {"json"},
},
domain,
)
params := domain.GetCustomParams()
params.Add("login_token", dnspod.task.DNS.ID+","+dnspod.task.DNS.Secret)
params.Add("domain", domain.DomainName)
params.Add("sub_domain", domain.GetSubDomain())
params.Add("record_type", recordType)
params.Add("value", ipAddr)
params.Add("ttl", dnspod.TTL)
params.Add("format", "json")
params.Add("record_id", record.ID)
if !params.Has("record_line") {
params.Add("record_line", "默认")
}
status, err := dnspod.commonRequest(recordModifyURL, params, domain)
if err == nil && status.Status.Code == "1" {
//log.Printf("更新域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("更新域名解析 %s 失败Code: %s, Message: %s", domain, status.Status.Code, status.Status.Message)
domain.SetDomainUpdateStatus(config.UpdatedFailed, fmt.Sprintf("Code: %s, Message: %s", status.Status.Code, status.Status.Message))
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, fmt.Sprintf("Code: %s, Message: %s", status.Status.Code, status.Status.Message))
}
}
}
// 公共
func (dnspod *Dnspod) commonRequest(apiAddr string, values url.Values, domain *config.Domain) (status DnspodStatus, err error) {
resp, err := http.PostForm(
func (dnspod *Dnspod) commonRequest(apiAddr string, values url.Values, domain *ddnscore.Domain) (status DnspodStatus, err error) {
resp, e := http.PostForm(
apiAddr,
values,
)
if err != nil {
err = e
return
}
err = httputils.GetAndParseJSONResponseFromHttpResponse(resp, &status)
return
}
// 获得域名记录列表
func (dnspod *Dnspod) getRecordList(domain *config.Domain, typ string) (result DnspodRecordListResp, err error) {
values := url.Values{
"login_token": {dnspod.task.DNS.ID + "," + dnspod.task.DNS.Secret},
"domain": {domain.DomainName},
"record_type": {typ},
"sub_domain": {domain.GetSubDomain()},
"format": {"json"},
}
func (dnspod *Dnspod) getRecordList(domain *ddnscore.Domain, typ string) (result DnspodRecordListResp, err error) {
params := domain.GetCustomParams()
params.Add("login_token", dnspod.task.DNS.ID+","+dnspod.task.DNS.Secret)
params.Add("domain", domain.DomainName)
params.Add("record_type", typ)
params.Add("sub_domain", domain.GetSubDomain())
params.Add("format", "json")
client, e := dnspod.CreateHTTPClient()
if e != nil {
@ -162,9 +168,15 @@ func (dnspod *Dnspod) getRecordList(domain *config.Domain, typ string) (result D
resp, err := client.PostForm(
recordListAPI,
values,
params,
)
if err != nil {
err = e
return
}
err = httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
return
}

View File

@ -8,7 +8,7 @@ import (
"net/http"
"strconv"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
"github.com/gdy666/lucky/thirdlib/jeessy2/ddns-go/util"
)
@ -50,7 +50,7 @@ type HuaweicloudRecordsets struct {
}
// Init 初始化
func (hw *Huaweicloud) Init(task *config.DDNSTask) {
func (hw *Huaweicloud) Init(task *ddnscore.DDNSTaskInfo) {
hw.DNSCommon.Init(task)
if task.TTL == "" {
@ -67,7 +67,7 @@ func (hw *Huaweicloud) Init(task *config.DDNSTask) {
hw.SetCreateUpdateDomainFunc(hw.createUpdateDomain)
}
func (hw *Huaweicloud) createUpdateDomain(recordType, ipAddr string, domain *config.Domain) {
func (hw *Huaweicloud) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
var records HuaweicloudRecordsResp
err := hw.request(
@ -80,7 +80,7 @@ func (hw *Huaweicloud) createUpdateDomain(recordType, ipAddr string, domain *con
if err != nil {
errMsg := "更新失败[001]:\n"
errMsg += err.Error()
domain.SetDomainUpdateStatus(config.UpdatedFailed, errMsg)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
@ -102,7 +102,7 @@ func (hw *Huaweicloud) createUpdateDomain(recordType, ipAddr string, domain *con
}
// 创建
func (hw *Huaweicloud) create(domain *config.Domain, recordType string, ipAddr string) {
func (hw *Huaweicloud) create(domain *ddnscore.Domain, recordType string, ipAddr string) {
zone, err := hw.getZones(domain)
if err != nil {
return
@ -135,20 +135,23 @@ func (hw *Huaweicloud) create(domain *config.Domain, recordType string, ipAddr s
)
if err == nil && (len(result.Records) > 0 && result.Records[0] == ipAddr) {
//log.Printf("新增域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("新增域名解析 %s 失败Status: %s", domain, result.Status)
domain.SetDomainUpdateStatus(config.UpdatedFailed, result.Status)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, result.Status)
}
}
// 修改
func (hw *Huaweicloud) modify(record HuaweicloudRecordsets, domain *config.Domain, recordType string, ipAddr string) {
func (hw *Huaweicloud) modify(record HuaweicloudRecordsets, domain *ddnscore.Domain, recordType string, ipAddr string) {
// 相同不修改
if len(record.Records) > 0 && record.Records[0] == ipAddr {
//log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
domain.SetDomainUpdateStatus(config.UpdatedNothing, "")
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
return
}
@ -167,15 +170,15 @@ func (hw *Huaweicloud) modify(record HuaweicloudRecordsets, domain *config.Domai
if err == nil && (len(result.Records) > 0 && result.Records[0] == ipAddr) {
//log.Printf("更新域名解析 %s 成功IP: %s, 状态: %s", domain, ipAddr, result.Status)
domain.SetDomainUpdateStatus(config.UpdatedSuccess, "")
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("更新域名解析 %s 失败Status: %s", domain, result.Status)
domain.SetDomainUpdateStatus(config.UpdatedFailed, result.Status)
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, result.Status)
}
}
// 获得域名记录列表
func (hw *Huaweicloud) getZones(domain *config.Domain) (result HuaweicloudZonesResp, err error) {
func (hw *Huaweicloud) getZones(domain *ddnscore.Domain) (result HuaweicloudZonesResp, err error) {
err = hw.request(
"GET",
fmt.Sprintf(huaweicloudEndpoint+"/v2/zones?name=%s", domain.DomainName),
@ -218,7 +221,8 @@ func (hw *Huaweicloud) request(method string, url string, data interface{}, resu
}
resp, err := client.Do(req)
err = httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
return
if err != nil {
return err
}
return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
}

193
ddns/porkbun.go Normal file
View File

@ -0,0 +1,193 @@
package ddns
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
const (
porkbunEndpoint string = "https://porkbun.com/api/json/v3/dns"
)
type Porkbun struct {
DNSCommon
TTL string
}
type PorkbunDomainRecord struct {
Name string `json:"name"` // subdomain
Type string `json:"type"` // record type, e.g. A AAAA CNAME
Content string `json:"content"` // value
Ttl string `json:"ttl"` // default 300
}
type PorkbunResponse struct {
Status string `json:"status"`
}
type PorkbunDomainQueryResponse struct {
*PorkbunResponse
Records []PorkbunDomainRecord `json:"records"`
}
type PorkbunApiKey struct {
AccessKey string `json:"apikey"`
SecretKey string `json:"secretapikey"`
}
type PorkbunDomainCreateOrUpdateVO struct {
*PorkbunApiKey
*PorkbunDomainRecord
}
// Init 初始化
func (pb *Porkbun) Init(task *ddnscore.DDNSTaskInfo) {
pb.DNSCommon.Init(task)
if task.TTL == "" {
// 默认600s
pb.TTL = "600"
} else {
pb.TTL = task.TTL
}
pb.SetCreateUpdateDomainFunc(pb.createUpdateDomain)
}
func (pb *Porkbun) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
var record PorkbunDomainQueryResponse
// 获取当前域名信息
err := pb.request(
porkbunEndpoint+fmt.Sprintf("/retrieveByNameType/%s/%s/%s", domain.DomainName, recordType, domain.GetSubDomain()),
&PorkbunApiKey{
AccessKey: pb.task.DNS.ID,
SecretKey: pb.task.DNS.Secret,
},
&record,
)
if err != nil {
return
}
if record.Status == "SUCCESS" {
if len(record.Records) > 0 {
// 存在,更新
pb.modify(&record, domain, recordType, ipAddr)
} else {
// 不存在,创建
pb.create(domain, recordType, ipAddr)
}
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, "查询现有域名记录失败")
}
}
// 创建
func (pb *Porkbun) create(domain *ddnscore.Domain, recordType string, ipAddr string) {
var response PorkbunResponse
err := pb.request(
porkbunEndpoint+fmt.Sprintf("/create/%s", domain.DomainName),
&PorkbunDomainCreateOrUpdateVO{
PorkbunApiKey: &PorkbunApiKey{
AccessKey: pb.task.DNS.ID,
SecretKey: pb.task.DNS.Secret,
},
PorkbunDomainRecord: &PorkbunDomainRecord{
Name: domain.SubDomain,
Type: recordType,
Content: ipAddr,
Ttl: pb.TTL,
},
},
&response,
)
if err == nil && response.Status == "SUCCESS" {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
errMsg := "新增域名失败"
if err != nil {
errMsg = err.Error()
}
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
}
}
// 修改
func (pb *Porkbun) modify(record *PorkbunDomainQueryResponse, domain *ddnscore.Domain, recordType string, ipAddr string) {
// 相同不修改
if len(record.Records) > 0 && record.Records[0].Content == ipAddr {
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
return
}
var response PorkbunResponse
err := pb.request(
porkbunEndpoint+fmt.Sprintf("/editByNameType/%s/%s/%s", domain.DomainName, recordType, domain.GetSubDomain()),
&PorkbunDomainCreateOrUpdateVO{
PorkbunApiKey: &PorkbunApiKey{
AccessKey: pb.task.DNS.ID,
SecretKey: pb.task.DNS.Secret,
},
PorkbunDomainRecord: &PorkbunDomainRecord{
Content: ipAddr,
Ttl: pb.TTL,
},
},
&response,
)
if err == nil && response.Status == "SUCCESS" {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
errMsg := "更新域名解析失败"
if err != nil {
errMsg = err.Error()
}
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
}
}
// request 统一请求接口
func (pb *Porkbun) request(url string, data interface{}, result interface{}) (err error) {
jsonStr := make([]byte, 0)
if data != nil {
jsonStr, _ = json.Marshal(data)
}
req, err := http.NewRequest(
"POST",
url,
bytes.NewBuffer(jsonStr),
)
if err != nil {
log.Println("http.NewRequest失败. Error: ", err)
return
}
req.Header.Set("Content-Type", "application/json")
client, e := pb.CreateHTTPClient()
if e != nil {
err = e
return
}
resp, err := client.Do(req)
if err != nil {
return err
}
return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
}

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/service"
)
@ -30,19 +31,29 @@ var wg sync.WaitGroup
// RunOnce RunOnce
func syncAllDomainsOnce(params ...any) {
ddnsTaskList := config.GetDDNSTaskList()
config.CleanIPUrlAddrMap()
ddnsTaskList := ddnscore.GetDDNSTaskInfoList()
ddnscore.CleanIPUrlAddrMap()
ddnsConf := config.GetDDNSConfigure()
//log.Printf("批量执行DDNS任务")
taskBeginTime := time.Now()
//fmt.Printf("ddnsTaskList:%v\n", ddnsTaskList)
for index := range ddnsTaskList {
task := ddnsTaskList[index]
if !task.Enable {
config.UpdateDomainsStateByTaskKey(task.TaskKey, config.UpdateStop, "")
continue
}
if time.Since(task.TaskState.LastWorkTime) < time.Second*15 {
//log.Printf("[%s]太接近,忽略", task.TaskName)
continue
}
//log.Printf("task[%s] enable\n", task.TaskName)
wg.Add(1)
go func() {
@ -55,10 +66,10 @@ func syncAllDomainsOnce(params ...any) {
log.Printf("syncDDNSTask[%s]panic:\n%v", task.TaskName, recoverErr)
log.Printf("%s", debug.Stack())
}()
syncDDNSTask(&task)
syncDDNSTask(task)
}()
<-time.After(time.Second)
<-time.After(time.Millisecond * 600)
}
wg.Wait()
@ -68,6 +79,7 @@ func syncAllDomainsOnce(params ...any) {
nextTaskTimer := time.Second*time.Duration(ddnsConf.Intervals) - usedTime
//debug.FreeOSMemory()
//log.Printf("syncAllDomainsOnce 任务完成")
DDNSService.Timer = time.NewTimer(nextTaskTimer)
}
@ -79,8 +91,8 @@ func syncTaskDomainsOnce(params ...any) {
case "syncDDNSTask":
{
//log.Printf("syncTaskDomainsOnce 单DDNS任务更新%s", taskKey)
config.CleanIPUrlAddrMap()
task := config.GetDDNSTaskByKey(taskKey)
ddnscore.CleanIPUrlAddrMap()
task := ddnscore.GetDDNSTaskInfoByKey(taskKey)
syncDDNSTask(task)
}
default:
@ -89,7 +101,7 @@ func syncTaskDomainsOnce(params ...any) {
}
func syncDDNSTask(task *config.DDNSTaskDetails) {
func syncDDNSTask(task *ddnscore.DDNSTaskInfo) {
if task == nil {
return
}
@ -107,15 +119,18 @@ func syncDDNSTask(task *config.DDNSTaskDetails) {
dnsSelected = &Callback{}
case "baiducloud":
dnsSelected = &BaiduCloud{}
case "porkbun":
dnsSelected = &Porkbun{}
default:
return
}
dnsSelected.Init(&task.DDNSTask)
dnsSelected.Init(task)
dnsSelected.AddUpdateDomainRecords()
task.ExecWebhook(&task.TaskState)
ddnscore.DDNSTaskInfoMapUpdate(task)
task.TaskState.LastWorkTime = time.Now() //记录最近一次检测时间,防止批量检测和单个检测时间间隔过于接近
//task.DomainsState.IpAddr = ipaddr
task.ExecWebhook(&task.DomainsState)
config.DDNSTaskListFlushDomainsDetails(task.TaskKey, &task.DomainsState)
//
}

164
ddnscore.go/cache.go Normal file
View File

@ -0,0 +1,164 @@
package ddnscore
import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/gdy666/lucky/config"
)
var taskInfoMap sync.Map
var taskInfoMapMutex sync.RWMutex
var webLastAccessDDNSTaskListLastTime int64
// 记录最后的前端请求DDNS任务列表时间
func FLushWebLastAccessDDNSTaskListLastTime() {
atomic.StoreInt64(&webLastAccessDDNSTaskListLastTime, time.Now().Unix())
}
// webAccessAvalid 判断前端访问是否处于活跃时间内
func webAccessAvalid() bool {
lastTime := atomic.LoadInt64(&webLastAccessDDNSTaskListLastTime)
return time.Now().Unix()-lastTime <= 5
}
func EnableDDNSTaskByKey(key string, enable bool) error {
taskInfoMapMutex.Lock()
defer taskInfoMapMutex.Unlock()
taskInfo, ok := taskInfoMap.Load(key)
if !ok {
return fmt.Errorf("DDNSTaskInfoMap key[%s] no found", key)
}
if enable {
taskInfo.(*DDNSTaskState).SetDomainUpdateStatus(UpdateWaiting, "")
} else {
taskInfo.(*DDNSTaskState).SetDomainUpdateStatus(UpdateStop, "")
}
return config.EnableDDNSTaskByKey(key, enable)
}
func DDNSTaskInfoMapUpdate(task *DDNSTaskInfo) {
taskInfoMapMutex.Lock()
defer taskInfoMapMutex.Unlock()
preInfo, ok := taskInfoMap.Load(task.TaskKey)
if ok {
var checkDomains []Domain
//防止有域名被删除
for i, new := range task.TaskState.Domains {
for _, pre := range preInfo.(*DDNSTaskState).Domains {
if strings.Compare(new.String(), pre.String()) == 0 {
checkDomains = append(checkDomains, task.TaskState.Domains[i])
break
}
}
}
task.TaskState.Domains = checkDomains
if len(preInfo.(*DDNSTaskState).Domains) > 0 && preInfo.(*DDNSTaskState).Domains[0].UpdateStatus == UpdateStop {
task.TaskState.SetDomainUpdateStatus(UpdateStop, "")
}
taskInfoMap.Store(task.TaskKey, &task.TaskState)
return
}
}
// 即时更新IP相关数据信息
func DDNSTaskInfoMapUpdateIPInfo(task *DDNSTaskInfo) {
if !webAccessAvalid() {
//log.Printf("前端没有访问,不即时更新")
return
}
//log.Printf("前端没有访问,不即时更新")
taskInfoMapMutex.Lock()
defer taskInfoMapMutex.Unlock()
state, ok := taskInfoMap.Load(task.TaskKey)
if !ok {
return
}
state.(*DDNSTaskState).IpAddr = task.TaskState.IpAddr
state.(*DDNSTaskState).IPAddrHistory = task.TaskState.IPAddrHistory
}
func DDNSTaskInfoMapUpdateDomainInfo(task *DDNSTaskInfo) {
if !webAccessAvalid() {
//log.Printf("前端没有访问,不即时更新")
return
}
//log.Printf("前端有访问,即时更新")
taskInfoMapMutex.Lock()
defer taskInfoMapMutex.Unlock()
state, ok := taskInfoMap.Load(task.TaskKey)
if !ok {
return
}
state.(*DDNSTaskState).Domains = task.TaskState.Domains
}
//func DDNSTaskInfo
func DDNSTaskInfoMapDelete(key string) {
taskInfoMapMutex.Lock()
defer taskInfoMapMutex.Unlock()
taskInfoMap.Delete(key)
}
func UpdateDomainsStateByTaskKey(key, status, message string) {
taskInfoMapMutex.Lock()
defer taskInfoMapMutex.Unlock()
preInfo, ok := taskInfoMap.Load(key)
if !ok {
return
}
preInfo.(*DDNSTaskState).SetDomainUpdateStatus(status, message)
}
func GetDDNSTaskInfoList() []*DDNSTaskInfo {
taskInfoMapMutex.RLock()
defer taskInfoMapMutex.RUnlock()
ddnsTaskList := config.GetDDNSTaskConfigureList()
var res []*DDNSTaskInfo
for i := range ddnsTaskList {
ti := CreateDDNSTaskInfo(ddnsTaskList[i])
res = append(res, &ti)
taskInfoMap.Store(ddnsTaskList[i].TaskKey, &ti.TaskState)
}
return res
}
func GetDDNSTaskInfoByKey(key string) *DDNSTaskInfo {
taskInfoMapMutex.RLock()
defer taskInfoMapMutex.RUnlock()
ddnsConf := config.GetDDNSTaskByKey(key)
if ddnsConf == nil {
return nil
}
info := CreateDDNSTaskInfo(ddnsConf)
return &info
}
func CreateDDNSTaskInfo(task *config.DDNSTask) DDNSTaskInfo {
var res DDNSTaskInfo
res.DDNSTask = *task
info, ok := taskInfoMap.Load(task.TaskKey)
if ok {
res.TaskState = *info.(*DDNSTaskState)
} else {
var ds DDNSTaskState
ds.Init(res.Domains)
if task.Enable {
ds.SetDomainUpdateStatus(UpdateWaiting, "")
} else {
ds.SetDomainUpdateStatus(UpdateStop, "")
}
res.TaskState = ds
}
return res
}

91
ddnscore.go/domain.go Normal file
View File

@ -0,0 +1,91 @@
package ddnscore
import (
"net/url"
"time"
)
const (
// UpdatedNothing 未改变
UpdatedNothing string = "域名IP和公网IP一致"
// UpdatedFailed 更新失败
UpdatedFailed = "失败"
// UpdatedSuccess 更新成功
UpdatedSuccess = "成功"
// UpdateStop 暂停
UpdateStop = "停止同步"
//UpdatePause 暂停 获取IP失败时暂停
UpdatePause = "暂停同步"
// UpdateWaiting
UpdateWaiting = "等待更新"
)
// Domain 域名实体
type Domain struct {
DomainName string
SubDomain string
CustomParams string
UpdateStatus string // 更新状态
LastUpdateStatusTime string //最后更新状态的时间
Message string
UpdateHistroy []any
}
type UpdateHistroyItem struct {
UpdateStatus string
UpdateTime string
}
func (d *Domain) String() string {
if d.SubDomain != "" {
return d.SubDomain + "." + d.DomainName
}
return d.DomainName
}
// GetFullDomain 返回完整子域名
func (d *Domain) GetFullDomain() string {
if d.SubDomain != "" {
return d.SubDomain + "." + d.DomainName
}
return "@" + "." + d.DomainName
}
// GetCustomParams not be nil
func (d *Domain) GetCustomParams() url.Values {
if d.CustomParams != "" {
q, err := url.ParseQuery(d.CustomParams)
if err == nil {
return q
}
}
return url.Values{}
}
// GetSubDomain 获得子域名,为空返回@
// 阿里云dnspod需要
func (d *Domain) GetSubDomain() string {
if d.SubDomain != "" {
return d.SubDomain
}
return "@"
}
func (d *Domain) SetDomainUpdateStatus(status string, message string) {
if status != UpdateWaiting {
if status != UpdateStop || d.UpdateStatus != UpdateStop {
d.LastUpdateStatusTime = time.Now().Format("2006-01-02 15:04:05")
// 状态更新历史记录
hi := UpdateHistroyItem{UpdateStatus: string(status), UpdateTime: d.LastUpdateStatusTime}
d.UpdateHistroy = append(d.UpdateHistroy, hi)
if len(d.UpdateHistroy) > 10 {
d.UpdateHistroy = DeleteAnyListlice(d.UpdateHistroy, 0)
}
}
}
d.UpdateStatus = status
d.Message = message
}

View File

@ -1,4 +1,4 @@
package config
package ddnscore
import (
"log"

177
ddnscore.go/taskinfo.go Normal file
View File

@ -0,0 +1,177 @@
package ddnscore
import (
"io"
"log"
"regexp"
"strings"
"sync"
"time"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
// Ipv4Reg IPv4正则
const Ipv4Reg = `((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])`
// Ipv6Reg IPv6正则
const Ipv6Reg = `((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))`
var ipUrlAddrMap sync.Map
type DDNSTaskInfo struct {
config.DDNSTask
TaskState DDNSTaskState `json:"TaskState"`
}
func (d *DDNSTaskInfo) getIpAddr() (result string) {
if d.TaskType == "IPv6" {
return d.getIpv6Addr()
}
return d.getIpv4Addr()
}
// CheckIPChange 检测公网IP是否改变
func (d *DDNSTaskInfo) CheckIPChange() (ipAddr string, change bool) {
ipAddr = d.getIpAddr()
checkIPChange := d.TaskState.IPChangeCheck(ipAddr)
if checkIPChange {
return ipAddr, true
}
//IP没变化
return ipAddr, false
}
// getIpv4Addr 获得IPv4地址
func (d *DDNSTaskInfo) getIpv4Addr() (result string) {
// 判断从哪里获取IP
if d.GetType == "netInterface" {
result = GetIPFromNetInterface("IPv4", d.NetInterface, d.IPReg)
return
}
ddnsGlobalConf := config.GetDDNSConfigure()
client, err := httputils.CreateHttpClient(
"tcp4",
"",
ddnsGlobalConf.HttpClientSecureVerify,
"",
"",
"",
"",
time.Duration(d.HttpClientTimeout)*time.Second)
if err != nil {
log.Printf("%s", err.Error())
return
}
for _, url := range d.URL {
url = strings.TrimSpace(url)
mapIp, ok := ipUrlAddrMap.Load(url)
if ok {
//log.Printf("URL[%s]已缓存IP[%s]", url, mapIp)
result = mapIp.(string)
return
}
resp, err := client.Get(url)
if err != nil {
//log.Printf("连接失败!%s查看接口能否返回IPv4地址</a>,", url)
continue
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("读取IPv4结果失败! 接口:%s", url)
continue
}
comp := regexp.MustCompile(Ipv4Reg)
result = comp.FindString(string(body))
if result != "" {
ipUrlAddrMap.Store(url, result)
return
}
// else {
// log.Printf("获取IPv4结果失败! 接口: %s ,返回值: %s\n", url, result)
// }
}
log.Printf("所有查询公网IPv4的接口均获取IPv4结果失败,请检查接口或当前网络情况")
return
}
// getIpv6Addr 获得IPv6地址
func (d *DDNSTaskInfo) getIpv6Addr() (result string) {
// 判断从哪里获取IP
if d.GetType == "netInterface" {
result = GetIPFromNetInterface("IPv6", d.NetInterface, d.IPReg)
return
}
ddnsGlobalConf := config.GetDDNSConfigure()
client, err := httputils.CreateHttpClient(
"tcp6",
"",
!ddnsGlobalConf.HttpClientSecureVerify,
"",
"",
"",
"",
time.Duration(d.HttpClientTimeout)*time.Second)
if err != nil {
log.Printf("%s", err.Error())
return
}
for _, url := range d.URL {
url = strings.TrimSpace(url)
mapIp, ok := ipUrlAddrMap.Load(url)
if ok {
//log.Printf("URL[%s]已缓存IP[%s]", url, mapIp)
result = mapIp.(string)
return
}
resp, err := client.Get(url)
if err != nil {
//log.Printf("连接失败! %s查看接口能否返回IPv6地址 ", url)
continue
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Println("读取IPv6结果失败! 接口: ", url)
continue
}
comp := regexp.MustCompile(Ipv6Reg)
result = comp.FindString(string(body))
if result != "" {
ipUrlAddrMap.Store(url, result)
return
}
}
log.Printf("所有查询公网IPv6的接口均获取IPv6结果失败,请检查接口或当前网络情况")
return
}
func CleanIPUrlAddrMap() {
keys := []string{}
ipUrlAddrMap.Range(func(key, value any) bool {
keys = append(keys, key.(string))
return true
})
for _, k := range keys {
ipUrlAddrMap.Delete(k)
}
}

View File

@ -1,45 +1,29 @@
package config
package ddnscore
import (
"log"
"net/url"
"strings"
"sync"
"time"
)
const (
// UpdatedNothing 未改变
UpdatedNothing updateStatusType = "域名IP和公网IP一致"
// UpdatedFailed 更新失败
UpdatedFailed = "失败"
// UpdatedSuccess 更新成功
UpdatedSuccess = "成功"
// UpdateStop 暂停
UpdateStop = "停止同步"
//UpdatePause 暂停 获取IP失败时暂停
UpdatePause = "暂停同步"
// UpdateWaiting
UpdateWaiting = "等待更新"
)
// 固定的主域名
var staticMainDomains = []string{"com.cn", "org.cn", "net.cn", "ac.cn", "eu.org"}
// 获取ip失败的次数
// Domains Ipv4/Ipv6 DomainsState
type DomainsState struct {
// Domains Ipv4/Ipv6 DDNSTaskState
type DDNSTaskState struct {
IpAddr string
Domains []*Domain
Domains []Domain
WebhookCallTime string `json:"WebhookCallTime"` //最后触发时间
WebhookCallResult bool `json:"WebhookCallResult"` //触发结果
WebhookCallErrorMsg string `json:"WebhookCallErrorMsg"` //触发错误信息
LastSyncTime time.Time `json:"-"` //记录最新一次同步操作时间
LastWorkTime time.Time `json:"-"`
IPAddrHistory []any `json:"IPAddrHistory"`
WebhookCallHistroy []any `json:"WebhookCallHistroy"`
Mutex *sync.RWMutex `json:"-"`
}
type IPAddrHistoryItem struct {
@ -47,33 +31,12 @@ type IPAddrHistoryItem struct {
RecordTime string
}
// Domain 域名实体
type Domain struct {
DomainName string
SubDomain string
UpdateStatus updateStatusType // 更新状态
LastUpdateStatusTime string //最后更新状态的时间
Message string
UpdateHistroy []any
rwmutex *sync.RWMutex
}
type UpdateHistroyItem struct {
UpdateStatus string
UpdateTime string
}
type WebhookCallHistroyItem struct {
CallTime string
CallResult string
}
func (d *DomainsState) SetIPAddr(ipaddr string) {
d.Mutex.Lock()
defer d.Mutex.Unlock()
func (d *DDNSTaskState) SetIPAddr(ipaddr string) {
if d.IpAddr == ipaddr {
return
}
@ -82,18 +45,12 @@ func (d *DomainsState) SetIPAddr(ipaddr string) {
hi := IPAddrHistoryItem{IPaddr: ipaddr, RecordTime: time.Now().Local().Format("2006-01-02 15:04:05")}
d.IPAddrHistory = append(d.IPAddrHistory, hi)
if len(d.IPAddrHistory) > 10 {
d.IPAddrHistory = DeleteAnyListlice(d.IPAddrHistory, 0)
}
}
func (d Domain) String() string {
if d.SubDomain != "" {
return d.SubDomain + "." + d.DomainName
}
return d.DomainName
}
func DeleteAnyListlice(a []any, deleteIndex int) []any {
j := 0
for i := range a {
@ -105,13 +62,13 @@ func DeleteAnyListlice(a []any, deleteIndex int) []any {
return a[:j]
}
func (d *DomainsState) SetDomainUpdateStatus(status updateStatusType, message string) {
func (d *DDNSTaskState) SetDomainUpdateStatus(status string, message string) {
for i := range d.Domains {
d.Domains[i].SetDomainUpdateStatus(status, message)
}
}
func (d *DomainsState) SetWebhookResult(result bool, errMsg string) {
func (d *DDNSTaskState) SetWebhookResult(result bool, errMsg string) {
d.WebhookCallResult = result
d.WebhookCallErrorMsg = errMsg
d.WebhookCallTime = time.Now().Format("2006-01-02 15:04:05")
@ -128,60 +85,21 @@ func (d *DomainsState) SetWebhookResult(result bool, errMsg string) {
}
}
func (d *Domain) SetDomainUpdateStatus(status updateStatusType, message string) {
d.rwmutex.Lock()
defer d.rwmutex.Unlock()
d.UpdateStatus = status
if status != UpdateWaiting && status != UpdateStop {
d.LastUpdateStatusTime = time.Now().Format("2006-01-02 15:04:05")
// 状态更新历史记录
hi := UpdateHistroyItem{UpdateStatus: string(status), UpdateTime: d.LastUpdateStatusTime}
d.UpdateHistroy = append(d.UpdateHistroy, hi)
if len(d.UpdateHistroy) > 10 {
d.UpdateHistroy = DeleteAnyListlice(d.UpdateHistroy, 0)
}
}
d.Message = message
}
// GetFullDomain 获得全部的,子域名
func (d Domain) GetFullDomain() string {
if d.SubDomain != "" {
return d.SubDomain + "." + d.DomainName
}
return "@" + "." + d.DomainName
}
// GetSubDomain 获得子域名,为空返回@
// 阿里云dnspod需要
func (d Domain) GetSubDomain() string {
if d.SubDomain != "" {
return d.SubDomain
}
return "@"
}
func (d *DomainsState) Init(domains []string) {
if d.Mutex == nil {
d.Mutex = &sync.RWMutex{}
}
func (d *DDNSTaskState) Init(domains []string) {
d.Domains = d.checkParseDomains(domains)
}
// checkParseDomains 校验并解析用户输入的域名
func (d *DomainsState) checkParseDomains(domainArr []string) (domains []*Domain) {
func (d *DDNSTaskState) checkParseDomains(domainArr []string) (domains []Domain) {
for _, domainStr := range domainArr {
domainStr = strings.TrimSpace(domainStr)
if domainStr != "" {
domain := &Domain{}
dp := strings.Split(domainStr, ":")
dplen := len(dp)
if dplen == 1 { // 自动识别域名
domain := &Domain{}
domain.rwmutex = d.Mutex
sp := strings.Split(domainStr, ".")
length := len(sp)
if length <= 1 {
@ -205,10 +123,7 @@ func (d *DomainsState) checkParseDomains(domainArr []string) (domains []*Domain)
domain.SubDomain = domainStr[:domainLen]
}
domains = append(domains, domain)
} else if dplen == 2 { // 主机记录:域名 格式
domain := &Domain{}
domain.rwmutex = d.Mutex
sp := strings.Split(dp[1], ".")
length := len(sp)
if length <= 1 {
@ -217,29 +132,38 @@ func (d *DomainsState) checkParseDomains(domainArr []string) (domains []*Domain)
}
domain.DomainName = dp[1]
domain.SubDomain = dp[0]
domains = append(domains, domain)
} else {
log.Println(domainStr, "域名不正确")
continue
}
// 参数条件
if strings.Contains(domain.DomainName, "?") {
u, err := url.Parse("http://" + domain.DomainName)
if err != nil {
log.Println(domainStr, "域名解析失败")
continue
}
domain.DomainName = u.Host
domain.CustomParams = u.Query().Encode()
}
domains = append(domains, *domain)
}
}
return
}
// CheckIPChange 检测公网IP是否改变
func (domains *DomainsState) CheckIPChange(recordType, taskKey string, getAddrFunc func() string) (ipAddr string, change bool, retDomains []*Domain) {
ipAddr = getAddrFunc()
checkIPChange, err := DDNSTaskIPCacheCheck(taskKey, domains.IpAddr)
if err != nil {
log.Printf("DDNSTaskIPCacheCheck 失败:%s", err.Error())
// Check 检测IP是否有改变
func (state *DDNSTaskState) IPChangeCheck(newAddr string) bool {
if newAddr == "" {
return true
}
// 地址改变
if state.IpAddr != newAddr {
//log.Printf("公网地址改变:[%s]===>[%s]", d.DomainsInfo.IpAddr, newAddr)
//domains.IpAddr = newAddr
return true
}
if err != nil || checkIPChange {
return ipAddr, true, domains.Domains
}
//IP没变化
return ipAddr, false, domains.Domains
return false
}

View File

@ -1,41 +1,40 @@
package config
package ddnscore
import (
"fmt"
"net/http"
"strings"
"time"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
// updateStatusType 更新状态
type updateStatusType string
// ExecWebhook 添加或更新IPv4/IPv6记录
func (d *DDNSTask) ExecWebhook(domains *DomainsState) {
func (d *DDNSTaskInfo) ExecWebhook(state *DDNSTaskState) {
if !d.WebhookEnable {
return
}
if domains.IpAddr == "" && !d.WebhookCallOnGetIPfail {
if state.IpAddr == "" && !d.WebhookCallOnGetIPfail {
return
}
tryUpdate := hasDomainTryToUpdate(domains.Domains)
hasUpdate := hasDomainTryToUpdate(&state.Domains)
if d.WebhookURL != "" && (tryUpdate || (domains.IpAddr == "" && d.WebhookCallOnGetIPfail)) {
if d.WebhookURL != "" && (hasUpdate || (state.IpAddr == "" && d.WebhookCallOnGetIPfail)) {
//log.Printf("DDNS任务【%s】触发Webhook", d.TaskName)
nowTime := time.Now().Format("2006-01-02 15:04:05")
url := replaceWebhookPara(domains, nowTime, d.WebhookURL)
requestBody := replaceWebhookPara(domains, nowTime, d.WebhookRequestBody)
url := d.replaceWebhookPara(nowTime, d.WebhookURL)
requestBody := d.replaceWebhookPara(nowTime, d.WebhookRequestBody)
//headersStr := cb.task.DNS.Callback.Headers
var headerStrList []string
for i := range d.WebhookHeaders {
header := replaceWebhookPara(domains, nowTime, d.WebhookHeaders[i])
header := d.replaceWebhookPara(nowTime, d.WebhookHeaders[i])
headerStrList = append(headerStrList, header)
}
@ -43,7 +42,7 @@ func (d *DDNSTask) ExecWebhook(domains *DomainsState) {
succcssCotentList := []string{}
for i := range d.WebhookSuccessContent {
content := replaceWebhookPara(domains, nowTime, d.WebhookSuccessContent[i])
content := d.replaceWebhookPara(nowTime, d.WebhookSuccessContent[i])
succcssCotentList = append(succcssCotentList, content)
}
@ -51,17 +50,17 @@ func (d *DDNSTask) ExecWebhook(domains *DomainsState) {
if callErr != nil {
//log.Printf("WebHook 调用出错:%s", callErr.Error())
domains.SetWebhookResult(false, callErr.Error())
state.SetWebhookResult(false, callErr.Error())
return
}
//log.Printf("Webhook 调用成功")
domains.SetWebhookResult(true, "")
state.SetWebhookResult(true, "")
}
}
func WebhookTest(d *DDNSTask, url, method, WebhookRequestBody, proxy, addr, user, passwd string, headerList, successContentListraw []string) (string, error) {
func WebhookTest(d *DDNSTaskInfo, url, method, WebhookRequestBody, proxy, addr, user, passwd string, headerList, successContentListraw []string) (string, error) {
nowTime := time.Now().Format("2006-01-02 15:04:05")
url = replaceWebhookTestPara(url, nowTime)
requestBody := replaceWebhookTestPara(WebhookRequestBody, nowTime)
@ -83,7 +82,7 @@ func WebhookTest(d *DDNSTask, url, method, WebhookRequestBody, proxy, addr, user
succcssCotentList = append(succcssCotentList, content)
}
globalDDNSConf := GetDDNSConfigure()
globalDDNSConf := config.GetDDNSConfigure()
proxyType := ""
proxyAddr := ""
proxyUser := ""
@ -112,7 +111,9 @@ func WebhookTest(d *DDNSTask, url, method, WebhookRequestBody, proxy, addr, user
//fmt.Printf("proxyType:%s\taddr:%s\t,user[%s]passwd[%s]\n", proxyType, proxyAddr, proxyUser, proxyPasswd)
//dnsConf := cb.task.DNS
respStr, err := httputils.GetStringGoutDoHttpRequest(
_, respStr, err := httputils.GetStringGoutDoHttpRequest(
"tcp",
"",
method,
url,
requestBody,
@ -136,9 +137,9 @@ func WebhookTest(d *DDNSTask, url, method, WebhookRequestBody, proxy, addr, user
return respStr, fmt.Errorf("接口调用出错,未匹配预设成功返回的字符串")
}
func (d *DDNSTask) webhookHttpClientDo(method, url, requestBody string, headers map[string]string, callbackSuccessContent []string) error {
func (d *DDNSTaskInfo) webhookHttpClientDo(method, url, requestBody string, headers map[string]string, callbackSuccessContent []string) error {
globalDDNSConf := GetDDNSConfigure()
globalDDNSConf := config.GetDDNSConfigure()
proxyType := ""
proxyAddr := ""
proxyUser := ""
@ -165,7 +166,9 @@ func (d *DDNSTask) webhookHttpClientDo(method, url, requestBody string, headers
}
//dnsConf := cb.task.DNS
respStr, err := httputils.GetStringGoutDoHttpRequest(
statusCode, respStr, err := httputils.GetStringGoutDoHttpRequest(
"tcp",
"",
method,
url,
requestBody,
@ -180,6 +183,13 @@ func (d *DDNSTask) webhookHttpClientDo(method, url, requestBody string, headers
return fmt.Errorf("webhook 调用接口[%s]出错:%s", url, err.Error())
}
if d.WebhookDisableCallbackSuccessContentCheck {
if statusCode == http.StatusOK {
return nil
}
return fmt.Errorf("webhook调用接口失败:\n statusCode:%d\n%s", statusCode, respStr)
}
for _, successContent := range callbackSuccessContent {
if strings.Contains(respStr, successContent) {
@ -191,8 +201,8 @@ func (d *DDNSTask) webhookHttpClientDo(method, url, requestBody string, headers
}
// DomainsIsChange
func hasDomainTryToUpdate(domains []*Domain) bool {
for _, v46 := range domains {
func hasDomainTryToUpdate(domains *[]Domain) bool {
for _, v46 := range *domains {
switch v46.UpdateStatus {
case UpdatedFailed:
return true
@ -221,10 +231,10 @@ func replaceWebhookTestPara(orgPara, nowTime string) (newPara string) {
}
// replacePara 替换参数 #{successDomains},#{failedDomains}
func replaceWebhookPara(d *DomainsState, nowTime, orgPara string) (newPara string) {
ipAddrText := d.IpAddr
func (d *DDNSTaskInfo) replaceWebhookPara(nowTime, orgPara string) (newPara string) {
ipAddrText := d.TaskState.IpAddr
successDomains, failedDomains := getDomainsStr(d.Domains)
successDomains, failedDomains := d.getDomainsStr(&d.TaskState.Domains)
if ipAddrText == "" {
ipAddrText = "获取IP失败"
successDomains = ""
@ -245,11 +255,11 @@ func replaceWebhookPara(d *DomainsState, nowTime, orgPara string) (newPara strin
}
// getDomainsStr 用逗号分割域名,分类域名返回,成功和失败的
func getDomainsStr(domains []*Domain) (string, string) {
func (d *DDNSTaskInfo) getDomainsStr(domains *[]Domain) (string, string) {
var successDomainBuf strings.Builder
var failedDomainsBuf strings.Builder
for _, v46 := range domains {
if v46.UpdateStatus == UpdatedFailed {
for _, v46 := range *domains {
if v46.UpdateStatus == UpdatedFailed || (d.Webhook.WebhookCallOnGetIPfail && v46.UpdateStatus == UpdatePause) {
if failedDomainsBuf.Len() > 0 {
failedDomainsBuf.WriteString(",")
}
@ -257,7 +267,8 @@ func getDomainsStr(domains []*Domain) (string, string) {
continue
}
if v46.UpdateStatus == UpdatedNothing || v46.UpdateStatus == UpdatedSuccess {
//if v46.UpdateStatus == UpdatedNothing || v46.UpdateStatus == UpdatedSuccess {
if v46.UpdateStatus == UpdatedSuccess {
if successDomainBuf.Len() > 0 {
successDomainBuf.WriteString(",")
}

View File

@ -82,7 +82,8 @@ func main() {
rule.EnableAllRelayRule() //开启规则
config.DDNSTaskListTaskDetailsInit()
//config.DDNSTaskListTaskDetailsInit()
config.DDNSTaskListConfigureCheck()
ddnsConf := config.GetDDNSConfigure()
if ddnsConf.Enable {
ddns.Run(time.Duration(ddnsConf.FirstCheckDelay)*time.Second, time.Duration(ddnsConf.Intervals)*time.Second)

View File

@ -11,11 +11,19 @@ import (
"net/http"
"net/url"
"strings"
"sync"
"time"
"golang.org/x/net/proxy"
)
var globalTransportMap map[string]*http.Transport
var globalTransportMapMutex sync.Mutex
func init() {
globalTransportMap = make(map[string]*http.Transport)
}
func GetAndParseJSONResponseFromHttpResponse(resp *http.Response, result interface{}) error {
bytes, err := GetBytesFromHttpResponse(resp)
if err != nil {
@ -31,7 +39,7 @@ func GetAndParseJSONResponseFromHttpResponse(resp *http.Response, result interfa
return nil
}
//GetStringFromHttpResponse 从response获取
// GetStringFromHttpResponse 从response获取
func GetBytesFromHttpResponse(resp *http.Response) ([]byte, error) {
if resp == nil || resp.Body == nil {
return []byte{}, fmt.Errorf("resp.Body = nil")
@ -51,7 +59,7 @@ func GetBytesFromHttpResponse(resp *http.Response) ([]byte, error) {
return body, err
}
//GetStringFromHttpResponse 从response获取
// GetStringFromHttpResponse 从response获取
func GetStringFromHttpResponse(resp *http.Response) (string, error) {
respBytes, err := GetBytesFromHttpResponse(resp)
if err != nil {
@ -60,12 +68,55 @@ func GetStringFromHttpResponse(resp *http.Response) (string, error) {
return string(respBytes), nil
}
func NewTransport(secureSkipVerify bool, proxyType, proxyUrl, user, passwd string) (*http.Transport, error) {
type transportIt struct {
Network string
LocalAddr string
ProxyType string
ProxyUrl string
User string
Passwd string
SecureSkipVerify bool
}
func (t *transportIt) String() string {
return fmt.Sprintf("%s,%s,%s,%s,%s,%s,%v", t.Network, t.LocalAddr, t.ProxyType, t.ProxyUrl, t.User, t.Passwd, t.SecureSkipVerify)
}
// NewTransport
// transportNetwork 网络类型 tcp tcp4 tcp6
// localAddr 指定网卡出口
func NewTransport(transportNetwork,
localAddrStr string,
secureSkipVerify bool,
proxyType,
proxyUrl,
user,
passwd string) (*http.Transport, error) {
var transport *http.Transport
proxyType = strings.ToLower(proxyType)
ti := transportIt{
Network: transportNetwork,
LocalAddr: localAddrStr,
ProxyType: proxyType,
ProxyUrl: proxyUrl,
User: user,
Passwd: passwd,
SecureSkipVerify: secureSkipVerify}
globalTransportMapMutex.Lock()
defer globalTransportMapMutex.Unlock()
tr, ok := globalTransportMap[ti.String()]
if ok {
//log.Printf("map[%s]已存在", ti.String())
return tr, nil
}
//log.Printf("map[%s]未存在", ti.String())
switch proxyType {
case "http", "https":
{
//log.Printf("http proxy Transport network:%s", transportNetwork)
if !strings.Contains(proxyUrl, "http") {
proxyUrl = fmt.Sprintf("%s://%s", proxyType, proxyUrl)
}
@ -78,25 +129,47 @@ func NewTransport(secureSkipVerify bool, proxyType, proxyUrl, user, passwd strin
urlProxy.User = url.UserPassword(user, passwd)
}
var localAddr net.Addr
localAddr = nil
if localAddrStr != "" {
lAddr, err := net.ResolveTCPAddr(transportNetwork, localAddrStr+":0")
if err != nil {
return nil, fmt.Errorf("NewTransport=> ResolveTCPAddr localAddr:%s error:%s", localAddrStr, err.Error())
}
localAddr = lAddr
}
dialer := (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
LocalAddr: localAddr,
})
transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: secureSkipVerify},
Proxy: http.ProxyURL(urlProxy),
Dial: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
return dialer.Dial(transportNetwork, addr)
},
ForceAttemptHTTP2: true,
Dial: dialer.Dial,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
case "socket5", "socks5":
{
//log.Printf("socket5 proxy Transport network:%s", transportNetwork)
var userAuth proxy.Auth
if user != "" && passwd != "" {
userAuth.User = user
userAuth.Password = passwd
}
dialer, err := proxy.SOCKS5("tcp", proxyUrl, &userAuth, proxy.Direct)
if err != nil {
return nil, fmt.Errorf("NewTransport=>proxy.SOCKS5 error:%s", err.Error())
@ -105,31 +178,58 @@ func NewTransport(secureSkipVerify bool, proxyType, proxyUrl, user, passwd strin
transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: secureSkipVerify},
DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
return dialer.Dial(network, addr)
return dialer.Dial(transportNetwork, addr)
},
Dial: (&net.Dialer{
Timeout: 10 * time.Second,
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
default:
{
//log.Printf("default Transport network:%s", transportNetwork)
var localAddr net.Addr
localAddr = nil
if localAddrStr != "" {
lAddr, err := net.ResolveTCPAddr(transportNetwork, localAddrStr+":0")
if err != nil {
return nil, fmt.Errorf("NewTransport=> ResolveTCPAddr localAddr:%s error:%s", localAddrStr, err.Error())
}
localAddr = lAddr
}
dialer := (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
LocalAddr: localAddr,
})
transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: secureSkipVerify},
Dial: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{InsecureSkipVerify: secureSkipVerify},
DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
return dialer.Dial(transportNetwork, addr)
},
Dial: dialer.Dial,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
}
globalTransportMap[ti.String()] = transport
return transport, nil
}
@ -147,5 +247,6 @@ func CreateHeadersMap(headers []string) map[string]string {
value := header[kvSpliteIndex+1:]
hm[key] = value
}
return hm
}

View File

@ -10,8 +10,8 @@ import (
"github.com/guonaihong/gout/dataflow"
)
func NewGout(secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, timeout time.Duration) (*dataflow.Gout, error) {
httpClient, err := CreateHttpClient(secureSkipVerify, proxyType, proxyUrl, user, passwd, timeout)
func NewGout(transportNetwork, localAddr string, secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, timeout time.Duration) (*dataflow.Gout, error) {
httpClient, err := CreateHttpClient(transportNetwork, localAddr, secureSkipVerify, proxyType, proxyUrl, user, passwd, timeout)
if err != nil {
return nil, fmt.Errorf("CreateHttpClient error:%s", err.Error())
}
@ -19,8 +19,16 @@ func NewGout(secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, ti
return gout.New(httpClient), nil
}
func GetAndParseJSONResponseFromGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl, user, passwd string, headers map[string]string, secureSkipVerify bool, timeout time.Duration, result interface{}) error {
bytes, err := GetBytesFromGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl, user, passwd, headers, secureSkipVerify, timeout)
func GetAndParseJSONResponseFromGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd string,
headers map[string]string, secureSkipVerify bool, timeout time.Duration, result interface{}) error {
_, bytes, err := GetBytesFromGoutDoHttpRequest(
transportNetwork,
localAddr,
method,
url,
requestBody,
proxyType,
proxyUrl, user, passwd, headers, secureSkipVerify, timeout)
if err != nil {
return fmt.Errorf("GetBytesFromHttpResponse err:%s", err.Error())
}
@ -33,23 +41,27 @@ func GetAndParseJSONResponseFromGoutDoHttpRequest(method, url, requestBody, prox
return nil
}
func GetStringGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl, user, passwd string, headers map[string]string, secureSkipVerify bool, timeout time.Duration) (string, error) {
bytes, err := GetBytesFromGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl, user, passwd, headers, secureSkipVerify, timeout)
func GetStringGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd string,
headers map[string]string, secureSkipVerify bool, timeout time.Duration) (int, string, error) {
statusCode, bytes, err := GetBytesFromGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd, headers, secureSkipVerify, timeout)
if err != nil {
return "", err
return 0, "", err
}
return string(bytes), nil
return statusCode, string(bytes), nil
}
func GetBytesFromGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl, user, passwd string, headers map[string]string, secureSkipVerify bool, timeout time.Duration) ([]byte, error) {
func GetBytesFromGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd string,
headers map[string]string, secureSkipVerify bool, timeout time.Duration) (int, []byte, error) {
gout, err := NewGout(
transportNetwork,
localAddr,
secureSkipVerify,
proxyType,
proxyUrl,
user,
passwd, timeout)
if err != nil {
return []byte{}, fmt.Errorf("GoutDoHttpRequest err:%s", err.Error())
return 0, []byte{}, fmt.Errorf("GoutDoHttpRequest err:%s", err.Error())
}
switch strings.ToLower(method) {
@ -62,7 +74,7 @@ func GetBytesFromGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl
case "delete":
gout.DELETE(url)
default:
return []byte{}, fmt.Errorf("未支持的Callback请求方法:%s", method)
return 0, []byte{}, fmt.Errorf("未支持的Callback请求方法:%s", method)
}
basicAuthUserName, BasicAuthUserNameOk := headers["BasicAuthUserName"]
@ -86,9 +98,9 @@ func GetBytesFromGoutDoHttpRequest(method, url, requestBody, proxyType, proxyUrl
resp, err := gout.Response()
if err != nil {
return []byte{}, fmt.Errorf("gout.Response() error:%s", err.Error())
return 0, []byte{}, fmt.Errorf("gout.Response() error:%s", err.Error())
}
respByte, respErr := GetBytesFromHttpResponse(resp)
return GetBytesFromHttpResponse(resp)
return resp.StatusCode, respByte, respErr
}

View File

@ -5,8 +5,9 @@ import (
"time"
)
func CreateHttpClient(secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, timeout time.Duration) (*http.Client, error) {
transport, err := NewTransport(secureSkipVerify, proxyType, proxyUrl, user, passwd)
func CreateHttpClient(transportNetwork, localAddr string, secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, timeout time.Duration) (*http.Client, error) {
transport, err := NewTransport(transportNetwork, localAddr, secureSkipVerify, proxyType, proxyUrl, user, passwd)
if err != nil {
return nil, err
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,8 @@
<title>Lucky(大吉)</title>
<script type="module" crossorigin src="/assets/index.85a8bea4.js"></script>
<link rel="stylesheet" href="/assets/index.c38c8b21.css">
<script type="module" crossorigin src="/assets/index.eb4bbca4.js"></script>
<link rel="stylesheet" href="/assets/index.633495a9.css">
</head>
<body style="margin:0">
<div id="app"></div>

View File

@ -6,13 +6,13 @@
<div class="InfoDivRadius">
<div class="line">
{{Info.AppName}}&nbsp;&nbsp;&nbsp;version:{{Info.Version}}
{{Info.AppName}}&nbsp;&nbsp;&nbsp;version:{{Info.Version}}&nbsp; {{Info.OS}}({{Info.ARCH}})
</div>
<div class="line">
{{Info.OS}}({{Info.ARCH}})
</div>
<div class="line">
作者:古大羊 &nbsp;编译于{{Info.Date}}
作者:古大羊 &nbsp;{{Info.GoVersion}}&nbsp;编译时间:{{Info.Date}}
</div>
<div class="line">
@ -58,7 +58,8 @@ var Info = ref({
Version:"1.0.0",
OS:"unknow",
ARCH:"unknow",
Date:"2022-07-25"
Date:"2022-07-25",
GoVersion:""
})

View File

@ -350,6 +350,15 @@
</el-form-item>
</div>
<div v-if="DDNSForm.DNS.Name == 'porkbun'">
<el-form-item label-width="auto">
<el-link type="primary" style="font-size: small;"
href="https://porkbun.com/account/api" target="_blank">
创建 Access
</el-link>
</el-form-item>
</div>
<div v-if="DDNSForm.DNS.Name == 'callback'">
<el-form-item label-width="auto">
<p style="font-size:1px">支持的变量 #{ip}, #{domain}, #{recordType}, #{ttl}</p>
@ -511,20 +520,30 @@
</el-tooltip>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>用于判断记录接口是否成功调用,多种表示成功的不同字符串请分多行写<br />
支持的变量<br />
#{ip} : 外网IP<br />
#{domain} : 域名<br />
#{recordType} : A 或者 AAAA <br />
#{ttl} : TTL值</template>
<el-form-item label="接口调用成功包含的字符串" label-width="auto">
<el-input v-model="DDNSFormCallbackSuccessContentArea"
:autosize="{ minRows: 3, maxRows: 5 }" type="textarea" autocomplete="off"
placeholder="" />
<el-tooltip content="禁用接口调用成功字符串检测,开启后仅以http StatusCode==200判断接口是否成功调用." placement="top">
<el-form-item label="禁用接口调用成功字符串检测" label-width="auto">
<el-switch v-model="DDNSForm.DNS.Callback.DisableCallbackSuccessContentCheck"
inline-prompt width="50px" active-text="是" inactive-text="否" />
</el-form-item>
</el-tooltip>
<div v-show="!DDNSForm.DNS.Callback.DisableCallbackSuccessContentCheck">
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>用于判断记录接口是否成功调用,多种表示成功的不同字符串请分多行写<br />
支持的变量<br />
#{ip} : 外网IP<br />
#{domain} : 域名<br />
#{recordType} : A 或者 AAAA <br />
#{ttl} : TTL值</template>
<el-form-item label="接口调用成功包含的字符串" label-width="auto">
<el-input v-model="DDNSFormCallbackSuccessContentArea"
:autosize="{ minRows: 3, maxRows: 5 }" type="textarea" autocomplete="off"
placeholder="" />
</el-form-item>
</el-tooltip>
</div>
</div>
@ -710,11 +729,10 @@
<div v-show="DDNSForm.WebhookEnable">
<el-tooltip class="box-item" effect="dark"
content="获取IP失败时同样触发Webhook,默认不开启">
<el-tooltip class="box-item" effect="dark" content="获取IP失败时同样触发Webhook,默认不开启">
<el-form-item label="获取IP失败时触发Webhook" label-width="auto">
<el-switch v-model="DDNSForm.WebhookCallOnGetIPfail" inline-prompt width="50px" active-text="启用"
inactive-text="禁用" />
<el-switch v-model="DDNSForm.WebhookCallOnGetIPfail" inline-prompt width="50px"
active-text="启用" inactive-text="禁用" />
</el-form-item>
</el-tooltip>
@ -825,7 +843,7 @@
<el-tooltip class="box-item" effect="dark" content="">
<template #content>支持的变量 <br />
#{ipAddr} : 当前公网IP<br />
#{time} : 触发Webhook的时间 <br />
#{time} : 触发Webhook的时间 <br />
#{successDomains} : 更新/添加成功的域名列表,域名之间用,号分隔<br />
#{successDomainsLine} : 更新/添加成功的域名列表,域名之间用'\n'分隔<br />
#{failedDomains} : 更新/添加失败的域名列表,域名之间用,号分隔<br />
@ -886,6 +904,18 @@
</el-tooltip>
<el-tooltip content="禁用Webhook接口调用成功字符串检测,开启后仅以http StatusCode==200判断接口是否成功调用."
placement="top">
<el-form-item label="禁用Webhook接口调用成功字符串检测" label-width="auto">
<el-switch v-model="DDNSForm.WebhookDisableCallbackSuccessContentCheck"
inline-prompt width="50px" active-text="是" inactive-text="否" />
</el-form-item>
</el-tooltip>
<div v-show="!DDNSForm.WebhookDisableCallbackSuccessContentCheck">
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>用于判断记录Webhook 接口是否成功调用<br />
多种表示成功的不同字符串请分多行写<br />
@ -901,7 +931,7 @@
placeholder="" />
</el-form-item>
</el-tooltip>
</div>
</div>
@ -1021,6 +1051,7 @@ var taskList = ref([{
Headers: [""],
RequestBody: "",
Server: "",
DisableCallbackSuccessContentCheck:false,
CallbackSuccessContent: [""]
},
@ -1030,6 +1061,7 @@ var taskList = ref([{
WebhookMethod: "",
WebhookHeaders: [""],
WebhookRequestBody: "",
WebhookDisableCallbackSuccessContentCheck:false,
WebhookSuccessContent: [""],
WebhookProxy: "",
WebhookProxyAddr: "",
@ -1249,6 +1281,10 @@ const DNSServerList = [
value: 'huaweicloud',
label: '华为云',
},
{
value: 'porkbun',
label: 'Porkbun',
},
{
value: 'callback',
label: '自定义(Callback)',
@ -1331,7 +1367,7 @@ const WebhookServerSelectChange = (server : string)=>{
content:[
[{tag:"text",text:"IP地址#{ipAddr}"}],
[{tag:"text",text:"域名更新成功列表:#{successDomainsLine}"}],
[{tag:"text",text:"域名更新失败列表:#{successDomainsLine}"}],
[{tag:"text",text:"域名更新失败列表:#{failedDomainsLine}"}],
[{tag:"text",text:"Webhook触发时间: \n#{time}"}],
]
}
@ -1419,6 +1455,8 @@ const getIDLabel = () => {
return "Access Key Id"
case "baiducloud":
return "AccessKey ID"
case "porkbun":
return "API Key"
case "callback":
return "URL"
default:
@ -1440,6 +1478,8 @@ const getSecretLabel = () => {
return "Secret Access Key"
case "baiducloud":
return "AccessKey Secret"
case "porkbun":
return "Secret Key"
case "callback":
return "RequestBody"
default:
@ -1458,6 +1498,8 @@ const showDNSIDFormItem = () => {
return true
case "baiducloud":
return true
case "porkbun":
return true
case "callback":
return false
default:
@ -1478,6 +1520,8 @@ const showDNSSecretFormItem = () => {
return true
case "baiducloud":
return true
case "porkbun":
return true
case "callback":
return false
default:
@ -1539,6 +1583,7 @@ const DDNSForm = ref(
Headers: [""],
RequestBody: "",
Server: "",
DisableCallbackSuccessContentCheck:false,
CallbackSuccessContent: [""],
},
},
@ -1548,6 +1593,7 @@ const DDNSForm = ref(
WebhookMethod: "",
WebhookHeaders: [""],
WebhookRequestBody: "",
WebhookDisableCallbackSuccessContentCheck:false,
WebhookSuccessContent: [""],
WebhookProxy: "",
WebhookProxyAddr: "",
@ -1585,6 +1631,7 @@ const preDDNSFrom = ref(
Headers: [""],
RequestBody: "",
Server: "",
DisableCallbackSuccessContentCheck:false,
CallbackSuccessContent: [""],
},
},
@ -1594,6 +1641,7 @@ const preDDNSFrom = ref(
WebhookMethod: "",
WebhookHeaders: [""],
WebhookRequestBody: "",
WebhookDisableCallbackSuccessContentCheck:false,
WebhookSuccessContent: [""],
WebhookProxy: "",
WebhookProxyAddr: "",
@ -1650,6 +1698,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
Headers: [""],
RequestBody: "",
Server: "other",
DisableCallbackSuccessContentCheck:false,
CallbackSuccessContent: [],
}
}
@ -1659,6 +1708,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
DDNSForm.value.WebhookMethod = "get"
DDNSForm.value.WebhookHeaders = []
DDNSForm.value.WebhookRequestBody = ""
DDNSForm.value.WebhookDisableCallbackSuccessContentCheck=false,
DDNSForm.value.WebhookSuccessContent = []
DDNSForm.value.WebhookProxy = ""
DDNSForm.value.WebhookProxyAddr = ""
@ -1701,6 +1751,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
Method: "get",
Headers: [""],
RequestBody: "",
DisableCallbackSuccessContentCheck:false,
CallbackSuccessContent: [],
Server: "other",
}
@ -1711,6 +1762,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
preDDNSFrom.value.WebhookMethod = "get"
preDDNSFrom.value.WebhookHeaders = []
preDDNSFrom.value.WebhookRequestBody = ""
preDDNSFrom.value.WebhookDisableCallbackSuccessContentCheck=false,
preDDNSFrom.value.WebhookSuccessContent = []
preDDNSFrom.value.WebhookProxy = ""
preDDNSFrom.value.WebhookProxyAddr = ""
@ -1745,6 +1797,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
Headers: task.DNS.Callback.Headers,
RequestBody: task.DNS.Callback.RequestBody,
Server: task.DNS.Callback.Server,
DisableCallbackSuccessContentCheck:task.DNS.Callback.DisableCallbackSuccessContentCheck,
CallbackSuccessContent: task.DNS.Callback.CallbackSuccessContent
}
}
@ -1754,6 +1807,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
DDNSForm.value.WebhookRequestBody = task.WebhookRequestBody
DDNSForm.value.WebhookMethod = task.WebhookMethod
DDNSForm.value.WebhookHeaders = task.WebhookHeaders
DDNSForm.value.WebhookDisableCallbackSuccessContentCheck = task.WebhookDisableCallbackSuccessContentCheck
DDNSForm.value.WebhookSuccessContent = task.WebhookSuccessContent
DDNSForm.value.WebhookProxy = task.WebhookProxy
DDNSForm.value.WebhookProxyAddr = task.WebhookProxyAddr
@ -1795,6 +1849,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
Headers: task.DNS.Callback.Headers,
RequestBody: task.DNS.Callback.RequestBody,
CallbackSuccessContent: task.DNS.Callback.CallbackSuccessContent,
DisableCallbackSuccessContentCheck:task.DNS.Callback.DisableCallbackSuccessContentCheck,
Server: task.DNS.Callback.Server,
}
}
@ -1803,6 +1858,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
preDDNSFrom.value.WebhookHeaders = task.WebhookHeaders
preDDNSFrom.value.WebhookCallOnGetIPfail = task.WebhookCallOnGetIPfail
preDDNSFrom.value.WebhookURL = task.WebhookURL
preDDNSFrom.value.WebhookDisableCallbackSuccessContentCheck = task.WebhookDisableCallbackSuccessContentCheck
preDDNSFrom.value.WebhookSuccessContent = task.WebhookSuccessContent
preDDNSFrom.value.WebhookRequestBody = task.WebhookRequestBody
preDDNSFrom.value.WebhookProxy = task.WebhookProxy
@ -2085,8 +2141,16 @@ const checkDDNSFormAvalid = () => {
return "Webhook 代理设置服务器地址不能为空"
}
}
if (!data.WebhookDisableCallbackSuccessContentCheck && data.WebhookSuccessContent.length == 0) {
return "Webhook接口调用成功包含的字符串不能为空,如果要指定为空请禁用检测"
}
}
//
if (data.DNS.Name == "callback") {
data.DNS.ID = ""
@ -2094,8 +2158,15 @@ const checkDDNSFormAvalid = () => {
if (data.DNS.Callback.Method == "get") {
data.DNS.Callback.RequestBody = ""
}
} else {
data.DNS.Callback = { URL: "", Method: "", Headers: [], RequestBody: "", Server: "", CallbackSuccessContent: [] }
data.DNS.Callback = {
URL: "",
Method: "",
Headers: [],
RequestBody: "", Server: "", CallbackSuccessContent: [],DisableCallbackSuccessContentCheck:false }
}
if (data.WebhookEnable) {
@ -2122,11 +2193,14 @@ const checkDNSData = (dns: any) => {
return "请选择Callback的请求方法"
}
if (dns.Callback.URL == "") {
return "Callback 接口地址不能为空"
}
if (!dns.Callback.DisableCallbackSuccessContentCheck && dns.Callback.CallbackSuccessContent.length==0){
return "接口调用成功包含的字符串不能为空,如果要指定为空请禁用检测"
}
break;
default:
if (dns.Secret == "" || dns.ID == "") {
@ -2159,6 +2233,7 @@ const autocompleteCallbackForm = () => {
DDNSForm.value.DNS.Callback.Headers = []
DDNSForm.value.DNS.Callback.Method = 'get'
DDNSForm.value.DNS.Callback.RequestBody = ""
DDNSForm.value.DNS.Callback.DisableCallbackSuccessContentCheck = false
DDNSForm.value.DNS.Callback.CallbackSuccessContent = ["chenggong", "chongfu", "ok"]
DDNSFormCallbackSuccessContentArea.value = 'chenggong\nchongfu\nok'
break;
@ -2168,6 +2243,7 @@ const autocompleteCallbackForm = () => {
DDNSForm.value.DNS.Callback.Headers = []
DDNSForm.value.DNS.Callback.Method = 'get'
DDNSForm.value.DNS.Callback.RequestBody = ""
DDNSForm.value.DNS.Callback.DisableCallbackSuccessContentCheck = false
DDNSForm.value.DNS.Callback.CallbackSuccessContent = ["nochg #{ip}", "good #{ip}"]
DDNSFormCallbackSuccessContentArea.value = 'nochg #{ip}\ngood #{ip}'
break;
@ -2181,6 +2257,7 @@ const autocompleteCallbackForm = () => {
DDNSForm.value.DNS.Callback.Headers = []
DDNSForm.value.DNS.Callback.Method = 'get'
DDNSForm.value.DNS.Callback.RequestBody = ""
DDNSForm.value.DNS.Callback.DisableCallbackSuccessContentCheck = false
DDNSForm.value.DNS.Callback.CallbackSuccessContent = ["addresses updated"]
DDNSFormCallbackSuccessContentArea.value = 'addresses updated\n'
break;
@ -2194,6 +2271,7 @@ const autocompleteCallbackForm = () => {
DDNSForm.value.DNS.Callback.Headers = []
DDNSForm.value.DNS.Callback.Method = 'get'
DDNSForm.value.DNS.Callback.RequestBody = ""
DDNSForm.value.DNS.Callback.DisableCallbackSuccessContentCheck = false
DDNSForm.value.DNS.Callback.CallbackSuccessContent = ["nochg", "good #{ip}"]
DDNSFormCallbackSuccessContentArea.value = 'nochg\ngood #{ip}'
break;

View File

@ -32,8 +32,8 @@ type WebLog struct {
func (wlog *WebLogs) Write(p []byte) (n int, err error) {
nowTime := time.Now()
tripContent := strings.TrimSpace(string(p)[20:])
content := fmt.Sprintf("%s %s", nowTime.Format("2006-01-02 15:04:05"), tripContent)
// tripContent := strings.TrimSpace(string(p)[20:])
// content := fmt.Sprintf("%s %s", nowTime.Format("2006-01-02 15:04:05"), tripContent)
if webLogs.preTimeStamp == nowTime.UnixNano() {
webLogs.timeStampIndex++
@ -41,7 +41,7 @@ func (wlog *WebLogs) Write(p []byte) (n int, err error) {
webLogs.timeStampIndex = 0
}
l := WebLog{Timestamp: nowTime.UnixNano() + webLogs.timeStampIndex, Log: content}
l := WebLog{Timestamp: nowTime.UnixNano() + webLogs.timeStampIndex, Log: strings.TrimSpace(string(p))}
webLogsStore = append(webLogsStore, l)
webLogs.preTimeStamp = nowTime.UnixNano()

View File

@ -6,8 +6,8 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/fs"
"io/ioutil"
"log"
"net"
"net/http"
@ -25,6 +25,7 @@ import (
"github.com/gdy666/lucky/base"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/rule"
"github.com/gdy666/lucky/thirdlib/gdylib/fileutils"
"github.com/gdy666/lucky/thirdlib/gdylib/ginutils"
@ -212,7 +213,7 @@ func restoreConfigure(c *gin.Context) {
}
defer src.Close()
fileBytes, err := ioutil.ReadAll(src)
fileBytes, err := io.ReadAll(src)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("ioutil.ReadAll err:%s", err.Error())})
return
@ -253,12 +254,12 @@ func enableddns(c *gin.Context) {
var err error
if enable == "true" {
err = config.EnableDDNSTaskByKey(key, true)
err = ddnscore.EnableDDNSTaskByKey(key, true)
if err == nil {
service.Message("ddns", "syncDDNSTask", key)
}
} else {
err = config.EnableDDNSTaskByKey(key, false)
err = ddnscore.EnableDDNSTaskByKey(key, false)
}
if err != nil {
@ -277,6 +278,8 @@ func deleteDDNSTask(c *gin.Context) {
return
}
ddnscore.DDNSTaskInfoMapDelete(taskKey)
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
}
@ -289,7 +292,8 @@ func ddnsTaskList(c *gin.Context) {
return
}
taskList := config.GetDDNSTaskList()
taskList := ddnscore.GetDDNSTaskInfoList()
ddnscore.FLushWebLastAccessDDNSTaskListLastTime()
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": taskList})
}
@ -346,6 +350,8 @@ func alterDDNSTask(c *gin.Context) {
return
}
ddnscore.DDNSTaskInfoMapDelete(taskKey)
if requestObj.Enable {
service.Message("ddns", "syncDDNSTask", taskKey)
}
@ -397,6 +403,12 @@ func dealRequestDDNSTask(t *config.DDNSTask) {
t.URL = []string{}
}
if t.DNS.Name == "callback" {
if t.DNS.Callback.DisableCallbackSuccessContentCheck {
t.DNS.Callback.CallbackSuccessContent = []string{}
}
}
if !t.WebhookEnable {
t.WebhookHeaders = []string{}
t.WebhookMethod = ""
@ -528,7 +540,7 @@ func whilelistAdd(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("IP已记录进白名单"), "ip": c.ClientIP(), " effective_time": lifeTime})
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "IP已记录进白名单", "ip": c.ClientIP(), " effective_time": lifeTime})
}
func whitelistConfigure(c *gin.Context) {
@ -556,7 +568,6 @@ func alterWhitelistConfigure(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
return
}
func checkLocalIP(c *gin.Context) {
@ -729,7 +740,7 @@ func baseconfigure(c *gin.Context) {
}
func netinterfaces(c *gin.Context) {
ipv4NetInterfaces, ipv6Netinterfaces, err := config.GetNetInterface()
ipv4NetInterfaces, ipv6Netinterfaces, err := ddnscore.GetNetInterface()
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("获取网卡列表出错:%s", err.Error())})
return
@ -764,16 +775,16 @@ func webhookTest(c *gin.Context) {
return
}
responseStr, err := config.WebhookTest(&ddnsTask.DDNSTask,
request.WebhookURL,
request.WebhookMethod,
request.WebhookRequestBody,
request.WebhookProxy,
request.WebhookProxyAddr,
request.WebhookProxyUser,
request.WebhookProxyPassword,
request.WebhookHeaders,
request.WebhookSuccessContent)
// responseStr, err := config.WebhookTest(&ddnsTask.DDNSTask,
// request.WebhookURL,
// request.WebhookMethod,
// request.WebhookRequestBody,
// request.WebhookProxy,
// request.WebhookProxyAddr,
// request.WebhookProxyUser,
// request.WebhookProxyPassword,
// request.WebhookHeaders,
// request.WebhookSuccessContent)
//fmt.Printf("request:%s\n", request)
@ -782,7 +793,7 @@ func webhookTest(c *gin.Context) {
msg = err.Error()
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": msg, "Response": responseStr})
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": msg, "Response": "responseStr"})
}
func IPRegTest(c *gin.Context) {
@ -790,7 +801,7 @@ func IPRegTest(c *gin.Context) {
netinterface := c.Query("netinterface")
ipreg := c.Query("ipreg")
ip := config.GetIPFromNetInterface(iptype, netinterface, ipreg)
ip := ddnscore.GetIPFromNetInterface(iptype, netinterface, ipreg)
c.JSON(http.StatusOK, gin.H{"ret": 0, "ip": ip})
}
@ -1067,7 +1078,7 @@ func GetCpuPercent() float64 {
return percent[0]
}
//跨域访问cross origin resource share
// 跨域访问cross origin resource share
func CrosHandler() gin.HandlerFunc {
return func(context *gin.Context) {
method := context.Request.Method