1
1
package dns
2
2
3
3
import (
4
+ "crypto/hmac"
5
+ "crypto/sha1"
6
+ "encoding/base64"
7
+ "encoding/json"
4
8
"fmt"
9
+ "io"
10
+ "net/http"
5
11
"net/url"
12
+ "sort"
6
13
"strconv"
7
14
"strings"
15
+ "time"
8
16
9
17
"github.com/jeessy2/ddns-go/v6/config"
10
18
"github.com/jeessy2/ddns-go/v6/util"
11
19
)
12
20
13
- const (
14
- eranetRecordListAPI string = "http://api.eranet.com:2080/api/dns/describe-record-index.json"
15
- eranetRecordModifyURL string = "http://api.eranet.com:2080/api/dns/update-domain-record.json"
16
- eranetRecordCreateAPI string = "http://api.eranet.com:2080/api/dns/add-domain-record.json"
17
- )
18
-
19
- // https://partner.tnet.hk/adminCN/mode_Http_Api_detail.php
20
21
// Eranet DNS实现
21
22
type Eranet struct {
22
23
DNS config.DNS
@@ -36,11 +37,11 @@ type EranetRecord struct {
36
37
}
37
38
38
39
type EranetRecordListResp struct {
39
- EranetStatus
40
+ EranetBaseResult
40
41
Data []EranetRecord
41
42
}
42
43
43
- type EranetStatus struct {
44
+ type EranetBaseResult struct {
44
45
RequestId string `json:"RequestId"`
45
46
Id int `json:"Id"`
46
47
Error string `json:"error"`
@@ -104,22 +105,28 @@ func (eranet *Eranet) addUpdateDomainRecords(recordType string) {
104
105
105
106
// create 创建DNS记录
106
107
func (eranet * Eranet ) create (domain * config.Domain , recordType string , ipAddr string ) {
107
- param := map [string ]any {
108
+ param := map [string ]string {
108
109
"Domain" : domain .DomainName ,
109
110
"Host" : domain .GetSubDomain (),
110
111
"Type" : recordType ,
111
112
"Value" : ipAddr ,
112
113
"Ttl" : eranet .TTL ,
113
114
}
114
- res , err := eranet .request (eranetRecordCreateAPI , param )
115
+ res , err := eranet .request ("/api/Dns/AddDomainRecord" , param , "GET" )
115
116
if err != nil {
116
117
util .Log ("新增域名解析 %s 失败! 异常信息: %s" , domain , err .Error ())
117
118
domain .UpdateStatus = config .UpdatedFailed
118
- } else if res .Error != "" {
119
- util .Log ("新增域名解析 %s 失败! 异常信息: %s" , domain , res .Error )
119
+ }
120
+ var result NowcnBaseResult
121
+ err = json .Unmarshal (res , & result )
122
+ if err != nil {
123
+ util .Log ("新增域名解析 %s 失败! 异常信息: %s" , domain , err .Error ())
124
+ domain .UpdateStatus = config .UpdatedFailed
125
+ }
126
+ if result .Error != "" {
127
+ util .Log ("新增域名解析 %s 失败! 异常信息: %s" , domain , result .Error )
120
128
domain .UpdateStatus = config .UpdatedFailed
121
129
} else {
122
- util .Log ("新增域名解析 %s 成功! IP: %s" , domain , ipAddr )
123
130
domain .UpdateStatus = config .UpdatedSuccess
124
131
}
125
132
}
@@ -131,64 +138,43 @@ func (eranet *Eranet) modify(record EranetRecord, domain *config.Domain, recordT
131
138
util .Log ("你的IP %s 没有变化, 域名 %s" , ipAddr , domain )
132
139
return
133
140
}
134
- param := map [string ]any {
135
- "Id" : record .ID ,
141
+ param := map [string ]string {
142
+ "Id" : strconv . Itoa ( record .ID ) ,
136
143
"Domain" : domain .DomainName ,
137
144
"Host" : domain .GetSubDomain (),
138
145
"Type" : recordType ,
139
146
"Value" : ipAddr ,
140
147
"Ttl" : eranet .TTL ,
141
148
}
142
- res , err := eranet .request (eranetRecordModifyURL , param )
149
+ res , err := eranet .request ("/api/Dns/UpdateDomainRecord" , param , "GET" )
143
150
if err != nil {
144
151
util .Log ("更新域名解析 %s 失败! 异常信息: %s" , domain , err .Error ())
145
152
domain .UpdateStatus = config .UpdatedFailed
146
- } else if res .Error != "" {
147
- util .Log ("更新域名解析 %s 失败! 异常信息: %s" , domain , res .Error )
153
+ }
154
+ var result NowcnBaseResult
155
+ err = json .Unmarshal (res , & result )
156
+ if err != nil {
157
+ util .Log ("更新域名解析 %s 失败! 异常信息: %s" , domain , err .Error ())
158
+ domain .UpdateStatus = config .UpdatedFailed
159
+ }
160
+ if result .Error != "" {
161
+ util .Log ("更新域名解析 %s 失败! 异常信息: %s" , domain , result .Error )
148
162
domain .UpdateStatus = config .UpdatedFailed
149
163
} else {
150
164
util .Log ("更新域名解析 %s 成功! IP: %s" , domain , ipAddr )
151
165
domain .UpdateStatus = config .UpdatedSuccess
152
166
}
153
167
}
154
168
155
- // request 发送HTTP请求
156
- func (eranet * Eranet ) request (apiAddr string , param map [string ]any ) (status EranetStatus , err error ) {
157
- param ["auth-userid" ] = eranet .DNS .ID
158
- param ["api-key" ] = eranet .DNS .Secret
159
-
160
- fullURL := apiAddr + "?" + eranet .queryParams (param )
161
- client := util .CreateHTTPClient ()
162
- resp , err := client .Get (fullURL )
163
-
164
- // 处理响应
165
- err = util .GetHTTPResponse (resp , err , & status )
166
-
167
- return
168
- }
169
-
170
169
// getRecordList 获取域名记录列表
171
170
func (eranet * Eranet ) getRecordList (domain * config.Domain , typ string ) (result EranetRecordListResp , err error ) {
172
- param := map [string ]any {
173
- "Domain" : domain .DomainName ,
174
- "auth-userid" : eranet .DNS .ID ,
175
- "api-key" : eranet .DNS .Secret ,
176
- }
177
- fullURL := eranetRecordListAPI + "?" + eranet .queryParams (param )
178
- client := util .CreateHTTPClient ()
179
- resp , err := client .Get (fullURL )
180
- var response EranetRecordListResp
181
- result = EranetRecordListResp {
182
- Data : make ([]EranetRecord , 0 ),
183
- }
184
- err = util .GetHTTPResponse (resp , err , & response )
185
- for _ , v := range response .Data {
186
- if v .Host == domain .GetSubDomain () {
187
- result .Data = append (result .Data , v )
188
- break
189
- }
190
-
171
+ param := map [string ]string {
172
+ "Domain" : domain .DomainName ,
173
+ "Type" : typ ,
174
+ "Host" : domain .GetSubDomain (),
191
175
}
176
+ res , err := eranet .request ("/api/Dns/DescribeRecordIndex" , param , "GET" )
177
+ err = json .Unmarshal (res , & result )
192
178
return
193
179
}
194
180
@@ -205,3 +191,98 @@ func (eranet *Eranet) queryParams(param map[string]any) string {
205
191
}
206
192
return strings .Join (queryParams , "&" )
207
193
}
194
+
195
+ func (t * Eranet ) sign (params map [string ]string , method string ) (string , error ) {
196
+ // 添加公共参数
197
+ params ["AccessKeyID" ] = t .DNS .ID
198
+ params ["SignatureMethod" ] = "HMAC-SHA1"
199
+ params ["SignatureNonce" ] = fmt .Sprintf ("%d" , time .Now ().UnixNano ())
200
+ params ["Timestamp" ] = time .Now ().UTC ().Format ("2006-01-02T15:04:05Z" )
201
+
202
+ // 1. 排序参数(按首字母顺序)
203
+ var keys []string
204
+ for k := range params {
205
+ if k != "Signature" { // 排除Signature参数
206
+ keys = append (keys , k )
207
+ }
208
+ }
209
+ sort .Strings (keys )
210
+
211
+ // 2. 构造规范化请求字符串
212
+ var canonicalizedQuery []string
213
+ for _ , k := range keys {
214
+ // URL编码参数名和参数值
215
+ encodedKey := util .PercentEncode (k )
216
+ encodedValue := util .PercentEncode (params [k ])
217
+ canonicalizedQuery = append (canonicalizedQuery , encodedKey + "=" + encodedValue )
218
+ }
219
+ canonicalizedQueryString := strings .Join (canonicalizedQuery , "&" )
220
+
221
+ // 3. 构造待签名字符串
222
+ stringToSign := method + "&" + util .PercentEncode ("/" ) + "&" + util .PercentEncode (canonicalizedQueryString )
223
+
224
+ // 4. 计算HMAC-SHA1签名
225
+ key := t .DNS .Secret + "&"
226
+ h := hmac .New (sha1 .New , []byte (key ))
227
+ h .Write ([]byte (stringToSign ))
228
+ signature := base64 .StdEncoding .EncodeToString (h .Sum (nil ))
229
+
230
+ // 5. 添加签名到参数中
231
+ params ["Signature" ] = signature
232
+
233
+ // 6. 重新构造最终的查询字符串(包含签名)
234
+ keys = append (keys , "Signature" )
235
+ sort .Strings (keys )
236
+ var finalQuery []string
237
+ for _ , k := range keys {
238
+ encodedKey := util .PercentEncode (k )
239
+ encodedValue := util .PercentEncode (params [k ])
240
+ finalQuery = append (finalQuery , encodedKey + "=" + encodedValue )
241
+ }
242
+
243
+ return strings .Join (finalQuery , "&" ), nil
244
+ }
245
+
246
+ func (t * Eranet ) request (apiPath string , params map [string ]string , method string ) ([]byte , error ) {
247
+ // 生成签名
248
+ queryString , err := t .sign (params , method )
249
+ if err != nil {
250
+ return nil , fmt .Errorf ("生成签名失败: %v" , err )
251
+ }
252
+
253
+ // 构造完整URL
254
+ baseURL := "https://www.eranet.com"
255
+ fullURL := baseURL + apiPath + "?" + queryString
256
+
257
+ // 创建HTTP请求
258
+ req , err := http .NewRequest (method , fullURL , nil )
259
+ if err != nil {
260
+ return nil , fmt .Errorf ("创建请求失败: %v" , err )
261
+ }
262
+
263
+ // 设置请求头
264
+ req .Header .Set ("Accept" , "application/json" )
265
+
266
+ // 发送请求
267
+ client := & http.Client {
268
+ Timeout : 30 * time .Second ,
269
+ }
270
+ resp , err := client .Do (req )
271
+ if err != nil {
272
+ return nil , fmt .Errorf ("请求失败: %v" , err )
273
+ }
274
+ defer resp .Body .Close ()
275
+
276
+ // 读取响应
277
+ body , err := io .ReadAll (resp .Body )
278
+ if err != nil {
279
+ return nil , fmt .Errorf ("读取响应失败: %v" , err )
280
+ }
281
+
282
+ // 检查HTTP状态码
283
+ if resp .StatusCode != http .StatusOK {
284
+ return nil , fmt .Errorf ("API请求失败,状态码: %d, 响应: %s" , resp .StatusCode , string (body ))
285
+ }
286
+
287
+ return body , nil
288
+ }
0 commit comments