From 5140d299dd278fe8bbd9ca85c91a18f3e7108dd6 Mon Sep 17 00:00:00 2001 From: jeessy2 <6205259+jeessy2@users.noreply.github.com> Date: Thu, 25 Feb 2021 17:01:28 +0800 Subject: [PATCH] refact: alidns (#43) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构alidns。包大小更小 --- dns/alidns.go | 177 ++++++++++++++++++--------- go.mod | 9 +- go.sum | 44 +------ util/aliyun_signer.go | 97 +++++++++++++++ util/aliyun_signer_util.go | 20 +++ util/{signer.go => huawei_signer.go} | 0 6 files changed, 239 insertions(+), 108 deletions(-) create mode 100644 util/aliyun_signer.go create mode 100644 util/aliyun_signer_util.go rename util/{signer.go => huawei_signer.go} (100%) diff --git a/dns/alidns.go b/dns/alidns.go index ac7c7d9..80ba8a9 100644 --- a/dns/alidns.go +++ b/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 +} diff --git a/go.mod b/go.mod index de63037..64aface 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 0554dc8..168980d 100644 --- a/go.sum +++ b/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= diff --git a/util/aliyun_signer.go b/util/aliyun_signer.go new file mode 100644 index 0000000..11766d4 --- /dev/null +++ b/util/aliyun_signer.go @@ -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 + } + } +} diff --git a/util/aliyun_signer_util.go b/util/aliyun_signer_util.go new file mode 100644 index 0000000..2551940 --- /dev/null +++ b/util/aliyun_signer_util.go @@ -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)) +} diff --git a/util/signer.go b/util/huawei_signer.go similarity index 100% rename from util/signer.go rename to util/huawei_signer.go