mirror of
https://github.com/jeessy2/ddns-go.git
synced 2025-10-20 15:33:46 +08:00
@ -18,7 +18,7 @@
|
||||
## 特性
|
||||
|
||||
- 支持Mac、Windows、Linux系统,支持ARM、x86架构
|
||||
- 支持的域名服务商 `阿里云` `腾讯云` `Dnspod` `Cloudflare` `华为云` `Callback` `百度云` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` `DNSLA` `时代互联` `Eranet`
|
||||
- 支持的域名服务商 `阿里云` `腾讯云` `Dnspod` `Cloudflare` `华为云` `Callback` `百度云` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` `DNSLA` `时代互联` `Eranet` `Gcore`
|
||||
- 支持接口/网卡/[命令](https://github.com/jeessy2/ddns-go/wiki/通过命令获取IP参考)获取IP
|
||||
- 支持以服务的方式运行
|
||||
- 默认间隔5分钟同步一次
|
||||
|
@ -16,7 +16,7 @@ Automatically obtain your public IPv4 or IPv6 address and resolve it to the corr
|
||||
## Features
|
||||
|
||||
- Support Mac, Windows, Linux system, support ARM, x86 architecture
|
||||
- Support domain service providers `Aliyun` `Tencent` `Dnspod` `Cloudflare` `Huawei` `Callback` `Baidu` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` `DNSLA` `Nowcn` `Eranet`
|
||||
- Support domain service providers `Aliyun` `Tencent` `Dnspod` `Cloudflare` `Huawei` `Callback` `Baidu` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` `DNSLA` `Nowcn` `Eranet` `Gcore`
|
||||
- Support interface / netcard / command to get IP
|
||||
- Support running as a service
|
||||
- Default interval is 5 minutes
|
||||
|
301
dns/gcore.go
Normal file
301
dns/gcore.go
Normal file
@ -0,0 +1,301 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/jeessy2/ddns-go/v6/config"
|
||||
"github.com/jeessy2/ddns-go/v6/util"
|
||||
)
|
||||
|
||||
const gcoreAPIEndpoint = "https://api.gcore.com/dns/v2"
|
||||
|
||||
// Gcore Gcore DNS实现
|
||||
type Gcore struct {
|
||||
DNS config.DNS
|
||||
Domains config.Domains
|
||||
TTL int
|
||||
}
|
||||
|
||||
// GcoreZoneResponse zones返回结果
|
||||
type GcoreZoneResponse struct {
|
||||
Zones []GcoreZone `json:"zones"`
|
||||
TotalAmount int `json:"total_amount"`
|
||||
}
|
||||
|
||||
// GcoreZone 域名信息
|
||||
type GcoreZone struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// GcoreRRSetListResponse RRSet列表返回结果
|
||||
type GcoreRRSetListResponse struct {
|
||||
RRSets []GcoreRRSet `json:"rrsets"`
|
||||
TotalAmount int `json:"total_amount"`
|
||||
}
|
||||
|
||||
// GcoreRRSet RRSet记录实体
|
||||
type GcoreRRSet struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TTL int `json:"ttl"`
|
||||
ResourceRecords []GcoreResourceRecord `json:"resource_records"`
|
||||
Meta map[string]interface{} `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// GcoreResourceRecord 资源记录
|
||||
type GcoreResourceRecord struct {
|
||||
Content []interface{} `json:"content"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ID int `json:"id,omitempty"`
|
||||
Meta map[string]interface{} `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// GcoreInputRRSet 输入的RRSet
|
||||
type GcoreInputRRSet struct {
|
||||
TTL int `json:"ttl"`
|
||||
ResourceRecords []GcoreInputResourceRecord `json:"resource_records"`
|
||||
Meta map[string]interface{} `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// GcoreInputResourceRecord 输入的资源记录
|
||||
type GcoreInputResourceRecord struct {
|
||||
Content []interface{} `json:"content"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Meta map[string]interface{} `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (gc *Gcore) Init(dnsConf *config.DnsConfig, ipv4cache *util.IpCache, ipv6cache *util.IpCache) {
|
||||
gc.Domains.Ipv4Cache = ipv4cache
|
||||
gc.Domains.Ipv6Cache = ipv6cache
|
||||
gc.DNS = dnsConf.DNS
|
||||
gc.Domains.GetNewIp(dnsConf)
|
||||
if dnsConf.TTL == "" {
|
||||
// 默认 120 秒(免费版最低值)
|
||||
gc.TTL = 120
|
||||
} else {
|
||||
ttl, err := strconv.Atoi(dnsConf.TTL)
|
||||
if err != nil {
|
||||
gc.TTL = 120
|
||||
} else {
|
||||
gc.TTL = ttl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddUpdateDomainRecords 添加或更新 IPv4 / IPv6 记录
|
||||
func (gc *Gcore) AddUpdateDomainRecords() config.Domains {
|
||||
gc.addUpdateDomainRecords("A")
|
||||
gc.addUpdateDomainRecords("AAAA")
|
||||
return gc.Domains
|
||||
}
|
||||
|
||||
func (gc *Gcore) addUpdateDomainRecords(recordType string) {
|
||||
ipAddr, domains := gc.Domains.GetNewIpResult(recordType)
|
||||
|
||||
if ipAddr == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
// get zone
|
||||
zoneInfo, err := gc.getZoneByDomain(domain)
|
||||
if err != nil {
|
||||
util.Log("查询域名信息发生异常! %s", err)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
continue
|
||||
}
|
||||
|
||||
if zoneInfo == nil {
|
||||
util.Log("在DNS服务商中未找到根域名: %s", domain.DomainName)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
continue
|
||||
}
|
||||
|
||||
// 查询现有记录
|
||||
existingRecord, err := gc.getRRSet(zoneInfo.Name, domain.GetSubDomain(), recordType)
|
||||
if err != nil {
|
||||
util.Log("查询域名信息发生异常! %s", err)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
continue
|
||||
}
|
||||
|
||||
if existingRecord != nil {
|
||||
// 更新现有记录
|
||||
gc.updateRecord(zoneInfo.Name, domain, recordType, ipAddr, existingRecord)
|
||||
} else {
|
||||
// 创建新记录
|
||||
gc.createRecord(zoneInfo.Name, domain, recordType, ipAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取域名对应的Zone信息
|
||||
func (gc *Gcore) getZoneByDomain(domain *config.Domain) (*GcoreZone, error) {
|
||||
var result GcoreZoneResponse
|
||||
params := url.Values{}
|
||||
params.Set("name", domain.DomainName)
|
||||
|
||||
err := gc.request(
|
||||
"GET",
|
||||
fmt.Sprintf("%s/zones?%s", gcoreAPIEndpoint, params.Encode()),
|
||||
nil,
|
||||
&result,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result.Zones) > 0 {
|
||||
return &result.Zones[0], nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 获取指定的RRSet记录
|
||||
func (gc *Gcore) getRRSet(zoneName, recordName, recordType string) (*GcoreRRSet, error) {
|
||||
var result GcoreRRSetListResponse
|
||||
|
||||
err := gc.request(
|
||||
"GET",
|
||||
fmt.Sprintf("%s/zones/%s/rrsets", gcoreAPIEndpoint, zoneName),
|
||||
nil,
|
||||
&result,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查找匹配的记录
|
||||
fullRecordName := recordName
|
||||
if recordName != "" && recordName != "@" {
|
||||
fullRecordName = recordName + "." + zoneName
|
||||
} else {
|
||||
fullRecordName = zoneName
|
||||
}
|
||||
|
||||
for _, rrset := range result.RRSets {
|
||||
if rrset.Name == fullRecordName && rrset.Type == recordType {
|
||||
return &rrset, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 创建新记录
|
||||
func (gc *Gcore) createRecord(zoneName string, domain *config.Domain, recordType string, ipAddr string) {
|
||||
recordName := domain.GetSubDomain()
|
||||
if recordName == "" || recordName == "@" {
|
||||
recordName = zoneName
|
||||
} else {
|
||||
recordName = recordName + "." + zoneName
|
||||
}
|
||||
|
||||
inputRRSet := GcoreInputRRSet{
|
||||
TTL: gc.TTL,
|
||||
ResourceRecords: []GcoreInputResourceRecord{
|
||||
{
|
||||
Content: []interface{}{ipAddr},
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var result interface{}
|
||||
err := gc.request(
|
||||
"POST",
|
||||
fmt.Sprintf("%s/zones/%s/%s/%s", gcoreAPIEndpoint, zoneName, recordName, recordType),
|
||||
inputRRSet,
|
||||
&result,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
return
|
||||
}
|
||||
|
||||
util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr)
|
||||
domain.UpdateStatus = config.UpdatedSuccess
|
||||
}
|
||||
|
||||
// 更新现有记录
|
||||
func (gc *Gcore) updateRecord(zoneName string, domain *config.Domain, recordType string, ipAddr string, existingRecord *GcoreRRSet) {
|
||||
// 检查IP是否相同
|
||||
if len(existingRecord.ResourceRecords) > 0 && len(existingRecord.ResourceRecords[0].Content) > 0 {
|
||||
if existingRecord.ResourceRecords[0].Content[0] == ipAddr {
|
||||
util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
recordName := domain.GetSubDomain()
|
||||
if recordName == "" || recordName == "@" {
|
||||
recordName = zoneName
|
||||
} else {
|
||||
recordName = recordName + "." + zoneName
|
||||
}
|
||||
|
||||
inputRRSet := GcoreInputRRSet{
|
||||
TTL: gc.TTL,
|
||||
ResourceRecords: []GcoreInputResourceRecord{
|
||||
{
|
||||
Content: []interface{}{ipAddr},
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var result interface{}
|
||||
err := gc.request(
|
||||
"PUT",
|
||||
fmt.Sprintf("%s/zones/%s/%s/%s", gcoreAPIEndpoint, zoneName, recordName, recordType),
|
||||
inputRRSet,
|
||||
&result,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
return
|
||||
}
|
||||
|
||||
util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr)
|
||||
domain.UpdateStatus = config.UpdatedSuccess
|
||||
}
|
||||
|
||||
// request 统一请求接口
|
||||
func (gc *Gcore) request(method string, url string, data interface{}, result interface{}) (err error) {
|
||||
jsonStr := make([]byte, 0)
|
||||
if data != nil {
|
||||
jsonStr, _ = json.Marshal(data)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(
|
||||
method,
|
||||
url,
|
||||
bytes.NewBuffer(jsonStr),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "APIKey "+gc.DNS.Secret)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := util.CreateHTTPClient()
|
||||
resp, err := client.Do(req)
|
||||
err = util.GetHTTPResponse(resp, err, result)
|
||||
|
||||
return
|
||||
}
|
@ -27,6 +27,7 @@ var (
|
||||
tencentCloudEndPoint,
|
||||
dynadotEndpoint,
|
||||
dynv6Endpoint,
|
||||
gcoreAPIEndpoint,
|
||||
}
|
||||
|
||||
Ipcache = [][2]util.IpCache{}
|
||||
@ -94,6 +95,8 @@ func RunOnce() {
|
||||
dnsSelected = &Nowcn{}
|
||||
case "eranet":
|
||||
dnsSelected = &Eranet{}
|
||||
case "gcore":
|
||||
dnsSelected = &Gcore{}
|
||||
default:
|
||||
dnsSelected = &Alidns{}
|
||||
}
|
||||
|
@ -216,6 +216,17 @@ const DNS_PROVIDERS = {
|
||||
"zh-cn": "<a target='_blank' href='https://partner.eranet.com/admin/mode_Http_Api_detail.php'>获取 api-key</a>",
|
||||
}
|
||||
},
|
||||
gcore: {
|
||||
name: {
|
||||
"en": "Gcore",
|
||||
},
|
||||
idLabel: "",
|
||||
secretLabel: "API Token",
|
||||
helpHtml: {
|
||||
"en": "<a target='_blank' href='https://portal.gcore.com/accounts/profile/api-tokens/create'>Create API Token</a>",
|
||||
"zh-cn": "<a target='_blank' href='https://portal.gcore.com/accounts/profile/api-tokens/create'>创建 API Token</a>",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const SVG_CODE = {
|
||||
|
Reference in New Issue
Block a user