mirror of
https://github.com/jeessy2/ddns-go.git
synced 2025-10-20 15:33:46 +08:00
177
dns/alidns.go
177
dns/alidns.go
@ -1,28 +1,47 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"ddns-go/config"
|
||||
"ddns-go/util"
|
||||
"log"
|
||||
|
||||
alidnssdk "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Alidns 阿里云dns实现
|
||||
const (
|
||||
alidnsEndpoint string = "https://alidns.aliyuncs.com/"
|
||||
)
|
||||
|
||||
// Alidns Alidns
|
||||
type Alidns struct {
|
||||
client *alidnssdk.Client
|
||||
Domains config.Domains
|
||||
DNSConfig config.DNSConfig
|
||||
Domains config.Domains
|
||||
}
|
||||
|
||||
// AlidnsSubDomainRecords 记录
|
||||
type AlidnsSubDomainRecords struct {
|
||||
TotalCount int
|
||||
DomainRecords struct {
|
||||
Record []struct {
|
||||
DomainName string
|
||||
RecordID string
|
||||
Value string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AlidnsResp 修改/添加返回结果
|
||||
type AlidnsResp struct {
|
||||
RecordID string
|
||||
RequestID string
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (ali *Alidns) Init(conf *config.Config) {
|
||||
client, err := alidnssdk.NewClientWithAccessKey("cn-hangzhou", conf.DNS.ID, conf.DNS.Secret)
|
||||
if err != nil {
|
||||
log.Println("Alidns链接失败")
|
||||
}
|
||||
ali.client = client
|
||||
|
||||
ali.DNSConfig = conf.DNS
|
||||
ali.Domains.ParseDomain(conf)
|
||||
|
||||
}
|
||||
|
||||
// AddUpdateDomainRecords 添加或更新IPv4/IPv6记录
|
||||
@ -39,56 +58,100 @@ func (ali *Alidns) addUpdateDomainRecords(recordType string) {
|
||||
return
|
||||
}
|
||||
|
||||
existReq := alidnssdk.CreateDescribeSubDomainRecordsRequest()
|
||||
existReq.Type = recordType
|
||||
|
||||
for _, domain := range domains {
|
||||
existReq.SubDomain = domain.GetFullDomain()
|
||||
rep, err := ali.client.DescribeSubDomainRecords(existReq)
|
||||
var record AlidnsSubDomainRecords
|
||||
// 获取当前域名信息
|
||||
params := url.Values{}
|
||||
params.Set("Action", "DescribeSubDomainRecords")
|
||||
params.Set("SubDomain", domain.GetFullDomain())
|
||||
params.Set("Type", recordType)
|
||||
err := ali.request(params, &record)
|
||||
|
||||
if err != nil {
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if rep.TotalCount > 0 {
|
||||
// Update
|
||||
for _, record := range rep.DomainRecords.Record {
|
||||
if record.Value == ipAddr {
|
||||
log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
|
||||
continue
|
||||
}
|
||||
request := alidnssdk.CreateUpdateDomainRecordRequest()
|
||||
request.Scheme = "https"
|
||||
request.Value = ipAddr
|
||||
request.Type = recordType
|
||||
request.RR = domain.GetSubDomain()
|
||||
request.RecordId = record.RecordId
|
||||
|
||||
updateResp, err := ali.client.UpdateDomainRecord(request)
|
||||
if err == nil && updateResp.BaseResponse.IsSuccess() {
|
||||
log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr)
|
||||
domain.UpdateStatus = config.UpdatedSuccess
|
||||
} else {
|
||||
log.Printf("更新域名解析 %s 失败!IP: %s, Error: %s, Response: %s", domain, ipAddr, err, updateResp.GetHttpContentString())
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
}
|
||||
}
|
||||
if record.TotalCount > 0 {
|
||||
// 存在,更新
|
||||
ali.modify(record, domain, recordType, ipAddr)
|
||||
} else {
|
||||
// Add
|
||||
request := alidnssdk.CreateAddDomainRecordRequest()
|
||||
request.Scheme = "https"
|
||||
request.Value = ipAddr
|
||||
request.Type = recordType
|
||||
request.RR = domain.GetSubDomain()
|
||||
request.DomainName = domain.DomainName
|
||||
|
||||
createResp, err := ali.client.AddDomainRecord(request)
|
||||
if err == nil && createResp.BaseResponse.IsSuccess() {
|
||||
log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr)
|
||||
domain.UpdateStatus = config.UpdatedSuccess
|
||||
} else {
|
||||
log.Printf("新增域名解析 %s 失败!IP: %s, Error: %s, Response: %s", domain, ipAddr, err, createResp.GetHttpContentString())
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
}
|
||||
// 不存在,创建
|
||||
ali.create(domain, recordType, ipAddr)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 创建
|
||||
func (ali *Alidns) create(domain *config.Domain, recordType string, ipAddr string) {
|
||||
params := url.Values{}
|
||||
params.Set("Action", "AddDomainRecord")
|
||||
params.Set("DomainName", domain.DomainName)
|
||||
params.Set("RR", domain.GetSubDomain())
|
||||
params.Set("Type", recordType)
|
||||
params.Set("Value", ipAddr)
|
||||
|
||||
var result AlidnsResp
|
||||
err := ali.request(params, &result)
|
||||
|
||||
if err == nil && "" != result.RecordID {
|
||||
log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr)
|
||||
domain.UpdateStatus = config.UpdatedSuccess
|
||||
} else {
|
||||
log.Printf("新增域名解析 %s 失败!", domain)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
}
|
||||
}
|
||||
|
||||
// 修改
|
||||
func (ali *Alidns) modify(record AlidnsSubDomainRecords, domain *config.Domain, recordType string, ipAddr string) {
|
||||
|
||||
// 相同不修改
|
||||
if len(record.DomainRecords.Record) > 0 && record.DomainRecords.Record[0].Value == ipAddr {
|
||||
log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
|
||||
return
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("Action", "UpdateDomainRecord")
|
||||
params.Set("RR", domain.GetSubDomain())
|
||||
params.Set("RecordId", record.DomainRecords.Record[0].RecordID)
|
||||
params.Set("Type", recordType)
|
||||
params.Set("Value", ipAddr)
|
||||
|
||||
var result AlidnsResp
|
||||
err := ali.request(params, &result)
|
||||
|
||||
if err == nil && "" != result.RecordID {
|
||||
log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr)
|
||||
domain.UpdateStatus = config.UpdatedSuccess
|
||||
} else {
|
||||
log.Printf("更新域名解析 %s 失败!", domain)
|
||||
domain.UpdateStatus = config.UpdatedFailed
|
||||
}
|
||||
}
|
||||
|
||||
// request 统一请求接口
|
||||
func (ali *Alidns) request(params url.Values, result interface{}) (err error) {
|
||||
|
||||
util.AliyunSigner(ali.DNSConfig.ID, ali.DNSConfig.Secret, ¶ms)
|
||||
|
||||
req, err := http.NewRequest(
|
||||
"GET",
|
||||
alidnsEndpoint,
|
||||
bytes.NewBuffer(nil),
|
||||
)
|
||||
req.URL.RawQuery = params.Encode()
|
||||
|
||||
if err != nil {
|
||||
log.Println("http.NewRequest失败. Error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
clt := http.Client{}
|
||||
clt.Timeout = 30 * time.Second
|
||||
resp, err := clt.Do(req)
|
||||
err = util.GetHTTPResponse(resp, alidnsEndpoint, err, result)
|
||||
|
||||
return
|
||||
}
|
||||
|
9
go.mod
9
go.mod
@ -2,11 +2,4 @@ module ddns-go
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.439
|
||||
github.com/jmespath/go-jmespath v0.3.0 // indirect
|
||||
github.com/json-iterator/go v1.1.10 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
gopkg.in/ini.v1 v1.60.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
)
|
||||
require gopkg.in/yaml.v2 v2.3.0
|
||||
|
44
go.sum
44
go.sum
@ -1,46 +1,4 @@
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.439 h1:vRjH9EcYcCkVUwti0q6AKcO8vFqCqe3EQOQl1QK+1zw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.439/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-bindata/go-bindata v1.0.0 h1:DZ34txDXWn1DyWa+vQf7V9ANc2ILTtrEjtlsdJRF26M=
|
||||
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
|
||||
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/tools v0.0.0-20200826040757-bc8aaaa29e06 h1:o8QN2yZHpPfla7J9ZQpaypzCL6fyEGCsLYv1+FpWdJc=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.60.1 h1:P5y5shSkb0CFe44qEeMBgn8JLow09MP17jlJHanke5g=
|
||||
gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
97
util/aliyun_signer.go
Normal file
97
util/aliyun_signer.go
Normal file
@ -0,0 +1,97 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// https://github.com/rosbit/aliyun-sign/blob/master/aliyun-sign.go
|
||||
|
||||
var (
|
||||
signMethodMap = map[string]func() hash.Hash{
|
||||
"HMAC-SHA1": sha1.New,
|
||||
"HMAC-SHA256": sha256.New,
|
||||
"HMAC-MD5": md5.New,
|
||||
}
|
||||
)
|
||||
|
||||
func HmacSign(signMethod string, httpMethod string, appKeySecret string, vals url.Values) (signature []byte) {
|
||||
key := []byte(appKeySecret + "&")
|
||||
|
||||
var h hash.Hash
|
||||
if method, ok := signMethodMap[signMethod]; ok {
|
||||
h = hmac.New(method, key)
|
||||
} else {
|
||||
h = hmac.New(sha1.New, key)
|
||||
}
|
||||
makeDataToSign(h, httpMethod, vals)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func HmacSignToB64(signMethod string, httpMethod string, appKeySecret string, vals url.Values) (signature string) {
|
||||
return base64.StdEncoding.EncodeToString(HmacSign(signMethod, httpMethod, appKeySecret, vals))
|
||||
}
|
||||
|
||||
type strToEnc struct {
|
||||
s string
|
||||
e bool
|
||||
}
|
||||
|
||||
func makeDataToSign(w io.Writer, httpMethod string, vals url.Values) {
|
||||
in := make(chan *strToEnc)
|
||||
go func() {
|
||||
in <- &strToEnc{s: httpMethod}
|
||||
in <- &strToEnc{s: "&"}
|
||||
in <- &strToEnc{s: "/", e: true}
|
||||
in <- &strToEnc{s: "&"}
|
||||
in <- &strToEnc{s: vals.Encode(), e: true}
|
||||
close(in)
|
||||
}()
|
||||
|
||||
specialUrlEncode(in, w)
|
||||
}
|
||||
|
||||
var (
|
||||
encTilde = "%7E" // '~' -> "%7E"
|
||||
encBlank = []byte("%20") // ' ' -> "%20"
|
||||
tilde = []byte("~")
|
||||
)
|
||||
|
||||
func specialUrlEncode(in <-chan *strToEnc, w io.Writer) {
|
||||
for s := range in {
|
||||
if !s.e {
|
||||
io.WriteString(w, s.s)
|
||||
continue
|
||||
}
|
||||
|
||||
l := len(s.s)
|
||||
for i := 0; i < l; {
|
||||
ch := s.s[i]
|
||||
|
||||
switch ch {
|
||||
case '%':
|
||||
if encTilde == s.s[i:i+3] {
|
||||
w.Write(tilde)
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
case '*', '/', '&', '=':
|
||||
fmt.Fprintf(w, "%%%02X", ch)
|
||||
case '+':
|
||||
w.Write(encBlank)
|
||||
default:
|
||||
fmt.Fprintf(w, "%c", ch)
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
20
util/aliyun_signer_util.go
Normal file
20
util/aliyun_signer_util.go
Normal file
@ -0,0 +1,20 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AliyunSigner AliyunSigner
|
||||
func AliyunSigner(accessKeyID, accessSecret string, params *url.Values) {
|
||||
// 公共参数
|
||||
params.Set("SignatureMethod", "HMAC-SHA1")
|
||||
params.Set("SignatureNonce", strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
params.Set("AccessKeyId", accessKeyID)
|
||||
params.Set("SignatureVersion", "1.0")
|
||||
params.Set("Timestamp", time.Now().UTC().Format("2006-01-02T15:04:05Z"))
|
||||
params.Set("Format", "JSON")
|
||||
params.Set("Version", "2015-01-09")
|
||||
params.Set("Signature", HmacSignToB64("HMAC-SHA1", "GET", accessSecret, *params))
|
||||
}
|
Reference in New Issue
Block a user