mirror of
https://github.com/taigrr/godns
synced 2025-01-18 04:03:25 -08:00
add AliDNS support
This commit is contained in:
parent
66187e0269
commit
57b3c4c79d
27
README.md
27
README.md
@ -30,6 +30,7 @@ Now I rewrite [DynDNS](https://github.com/TimothyYe/DynDNS) by Golang and call i
|
|||||||
* Cloudflare ([https://cloudflare.com](https://cloudflare.com))
|
* Cloudflare ([https://cloudflare.com](https://cloudflare.com))
|
||||||
* DNSPod ([https://www.dnspod.cn/](https://www.dnspod.cn/))
|
* DNSPod ([https://www.dnspod.cn/](https://www.dnspod.cn/))
|
||||||
* HE.net (Hurricane Electric) ([https://dns.he.net/](https://dns.he.net/))
|
* HE.net (Hurricane Electric) ([https://dns.he.net/](https://dns.he.net/))
|
||||||
|
* AliDNS ([https://help.aliyun.com/product/29697.html](https://help.aliyun.com/product/29697.html))
|
||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
* Linux
|
* Linux
|
||||||
@ -52,7 +53,7 @@ And the binary can run well on routers.
|
|||||||
|
|
||||||
* Register and own a domain.
|
* Register and own a domain.
|
||||||
|
|
||||||
* Domain's nameserver points to [DNSPod](https://www.dnspod.cn/) or [HE.net](https://dns.he.net/) or [Cloudflare](https://www.cloudflare.com/).
|
* Domain's nameserver points to [DNSPod](https://www.dnspod.cn/) or [HE.net](https://dns.he.net/) or [Cloudflare](https://www.cloudflare.com/) or [AliDNS](https://dc.console.aliyun.com).
|
||||||
|
|
||||||
## Get it
|
## Get it
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ Usage of ./godns:
|
|||||||
|
|
||||||
* Get [config_sample.json](https://github.com/timothyye/godns/blob/master/config_sample.json) from Github.
|
* Get [config_sample.json](https://github.com/timothyye/godns/blob/master/config_sample.json) from Github.
|
||||||
* Rename it to **config.json**.
|
* Rename it to **config.json**.
|
||||||
* Configure your provider, domain/sub-domain info, username and password, etc.
|
* Configure your provider, domain/subdomain info, username and password, etc.
|
||||||
* Configure the SMTP options if you want, a mail notification will sent to your mailbox once the IP is changed.
|
* Configure the SMTP options if you want, a mail notification will sent to your mailbox once the IP is changed.
|
||||||
* Save it in the same directory of GoDNS, or use -c=your_conf_path command.
|
* Save it in the same directory of GoDNS, or use -c=your_conf_path command.
|
||||||
|
|
||||||
@ -136,6 +137,28 @@ For DNSPod, you need to provide email & password, and config all the domains &
|
|||||||
"socks5_proxy": ""
|
"socks5_proxy": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### Config example for AliDNS
|
||||||
|
|
||||||
|
For AliDNS, you need to provide `AccessKeyID` & `AccessKeySecret` as `email` & `password`, and config all the domains & subdomains.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"provider": "AliDNS",
|
||||||
|
"email": "AccessKeyID",
|
||||||
|
"password": "AccessKeySecret",
|
||||||
|
"login_token": "",
|
||||||
|
"domains": [{
|
||||||
|
"domain_name": "example.com",
|
||||||
|
"sub_domains": ["www","test"]
|
||||||
|
},{
|
||||||
|
"domain_name": "example2.com",
|
||||||
|
"sub_domains": ["www","test"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ip_url": "http://members.3322.org/dyndns/getip",
|
||||||
|
"socks5_proxy": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
### Config example for HE.net
|
### Config example for HE.net
|
||||||
|
|
||||||
For HE, email is not needed, just fill DDNS key to password, and config all the domains & subdomains.
|
For HE, email is not needed, just fill DDNS key to password, and config all the domains & subdomains.
|
||||||
|
157
handler/alidns/alidns.go
Normal file
157
handler/alidns/alidns.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package alidns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AliDNS token
|
||||||
|
type AliDNS struct {
|
||||||
|
AccessKeyID string
|
||||||
|
AccessKeySecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
publicParm = map[string]string{
|
||||||
|
"AccessKeyId": "",
|
||||||
|
"Format": "JSON",
|
||||||
|
"Version": "2015-01-09",
|
||||||
|
"SignatureMethod": "HMAC-SHA1",
|
||||||
|
"Timestamp": "",
|
||||||
|
"SignatureVersion": "1.0",
|
||||||
|
"SignatureNonce": "",
|
||||||
|
}
|
||||||
|
baseURL = "http://alidns.aliyuncs.com/"
|
||||||
|
instance *AliDNS
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
type domainRecordsResp struct {
|
||||||
|
RequestID string `json:"RequestId"`
|
||||||
|
TotalCount int
|
||||||
|
PageNumber int
|
||||||
|
PageSize int
|
||||||
|
DomainRecords domainRecords
|
||||||
|
}
|
||||||
|
|
||||||
|
type domainRecords struct {
|
||||||
|
Record []DomainRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainRecord struct
|
||||||
|
type DomainRecord struct {
|
||||||
|
DomainName string
|
||||||
|
RecordID string `json:"RecordId"`
|
||||||
|
RR string
|
||||||
|
Type string
|
||||||
|
Value string
|
||||||
|
Line string
|
||||||
|
Priority int
|
||||||
|
TTL int
|
||||||
|
Status string
|
||||||
|
Locked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHTTPBody(url string) ([]byte, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return body, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Status %d, Error:%s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAliDNS(key, secret string) *AliDNS {
|
||||||
|
once.Do(func() {
|
||||||
|
instance = &AliDNS{
|
||||||
|
AccessKeyID: key,
|
||||||
|
AccessKeySecret: secret,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainRecords gets all the doamin records according to input subdomain key
|
||||||
|
func (d *AliDNS) GetDomainRecords(domain, rr string) []DomainRecord {
|
||||||
|
resp := &domainRecordsResp{}
|
||||||
|
parms := map[string]string{
|
||||||
|
"Action": "DescribeDomainRecords",
|
||||||
|
"DomainName": domain,
|
||||||
|
"RRKeyWord": rr,
|
||||||
|
}
|
||||||
|
urlPath := d.genRequestURL(parms)
|
||||||
|
body, err := getHTTPBody(urlPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("GetDomainRecords error.%+v\n", err)
|
||||||
|
} else {
|
||||||
|
json.Unmarshal(body, resp)
|
||||||
|
return resp.DomainRecords.Record
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDomainRecord updates domain record
|
||||||
|
func (d *AliDNS) UpdateDomainRecord(r DomainRecord) error {
|
||||||
|
parms := map[string]string{
|
||||||
|
"Action": "UpdateDomainRecord",
|
||||||
|
"RecordId": r.RecordID,
|
||||||
|
"RR": r.RR,
|
||||||
|
"Type": r.Type,
|
||||||
|
"Value": r.Value,
|
||||||
|
"TTL": strconv.Itoa(r.TTL),
|
||||||
|
"Line": r.Line,
|
||||||
|
}
|
||||||
|
|
||||||
|
urlPath := d.genRequestURL(parms)
|
||||||
|
_, err := getHTTPBody(urlPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("UpdateDomainRecord error.%+v\n", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliDNS) genRequestURL(parms map[string]string) string {
|
||||||
|
pArr := []string{}
|
||||||
|
ps := map[string]string{}
|
||||||
|
for k, v := range publicParm {
|
||||||
|
ps[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range parms {
|
||||||
|
ps[k] = v
|
||||||
|
}
|
||||||
|
now := time.Now().UTC()
|
||||||
|
ps["AccessKeyId"] = d.AccessKeyID
|
||||||
|
ps["SignatureNonce"] = strconv.Itoa(int(now.UnixNano()) + rand.Intn(99999))
|
||||||
|
ps["Timestamp"] = now.Format("2006-01-02T15:04:05Z")
|
||||||
|
|
||||||
|
for k, v := range ps {
|
||||||
|
pArr = append(pArr, fmt.Sprintf("%s=%s", k, v))
|
||||||
|
}
|
||||||
|
sort.Strings(pArr)
|
||||||
|
path := strings.Join(pArr, "&")
|
||||||
|
|
||||||
|
s := "GET&%2F&" + url.QueryEscape(path)
|
||||||
|
s = strings.Replace(s, "%3A", "%253A", -1)
|
||||||
|
s = strings.Replace(s, "%40", "%2540", -1)
|
||||||
|
s = strings.Replace(s, "%2A", "%252A", -1)
|
||||||
|
mac := hmac.New(sha1.New, []byte(d.AccessKeySecret+"&"))
|
||||||
|
|
||||||
|
mac.Write([]byte(s))
|
||||||
|
sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||||
|
return fmt.Sprintf("%s?%s&Signature=%s", baseURL, path, url.QueryEscape(sign))
|
||||||
|
}
|
77
handler/alidns/alidns_handler.go
Normal file
77
handler/alidns/alidns_handler.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package alidns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/TimothyYe/godns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AliDNSHandler struct
|
||||||
|
type AliDNSHandler struct {
|
||||||
|
Configuration *godns.Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfiguration pass dns settings and store it to handler instance
|
||||||
|
func (handler *AliDNSHandler) SetConfiguration(conf *godns.Settings) {
|
||||||
|
handler.Configuration = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainLoop the main logic loop
|
||||||
|
func (handler *AliDNSHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
||||||
|
panicChan <- *domain
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var lastIP string
|
||||||
|
aliDNS := NewAliDNS(handler.Configuration.Email, handler.Configuration.Password)
|
||||||
|
|
||||||
|
for {
|
||||||
|
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get current IP:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("currentIP is:", currentIP)
|
||||||
|
|
||||||
|
//check against locally cached IP, if no change, skip update
|
||||||
|
if currentIP == lastIP {
|
||||||
|
log.Printf("IP is the same as cached one. Skip update.\n")
|
||||||
|
} else {
|
||||||
|
lastIP = currentIP
|
||||||
|
|
||||||
|
for _, subDomain := range domain.SubDomains {
|
||||||
|
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
||||||
|
records := aliDNS.GetDomainRecords(domain.DomainName, subDomain)
|
||||||
|
if records == nil || len(records) == 0 {
|
||||||
|
log.Printf("Cannot get subdomain %s from AliDNS.\r\n", subDomain)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
records[0].Value = currentIP
|
||||||
|
if err := aliDNS.UpdateDomainRecord(records[0]); err != nil {
|
||||||
|
log.Printf("Failed to update IP for subdomain:%s\r\n", subDomain)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
log.Printf("IP updated for subdomain:%s\r\n", subDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send mail notification if notify is enabled
|
||||||
|
if handler.Configuration.Notify.Enabled {
|
||||||
|
log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
|
||||||
|
godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Interval is 5 minutes
|
||||||
|
log.Printf("Going to sleep, will start next checking in %d minutes...\r\n", godns.INTERVAL)
|
||||||
|
time.Sleep(time.Minute * godns.INTERVAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package handler
|
package cloudflare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
@ -1,4 +1,4 @@
|
|||||||
package handler
|
package cloudflare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
@ -1,4 +1,4 @@
|
|||||||
package handler
|
package dnspod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
"github.com/TimothyYe/godns"
|
||||||
"github.com/bitly/go-simplejson"
|
simplejson "github.com/bitly/go-simplejson"
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ func (handler *DNSPodHandler) DomainLoop(domain *godns.Domain, panicChan chan<-
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue to check the IP of sub-domain
|
// Continue to check the IP of subdomain
|
||||||
if len(ip) > 0 && strings.TrimRight(currentIP, "\n") != strings.TrimRight(ip, "\n") {
|
if len(ip) > 0 && strings.TrimRight(currentIP, "\n") != strings.TrimRight(ip, "\n") {
|
||||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
||||||
handler.UpdateIP(domainID, subDomainID, subDomain, currentIP)
|
handler.UpdateIP(domainID, subDomainID, subDomain, currentIP)
|
@ -1,6 +1,12 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import "github.com/TimothyYe/godns"
|
import (
|
||||||
|
"github.com/TimothyYe/godns"
|
||||||
|
"github.com/TimothyYe/godns/handler/alidns"
|
||||||
|
"github.com/TimothyYe/godns/handler/cloudflare"
|
||||||
|
"github.com/TimothyYe/godns/handler/dnspod"
|
||||||
|
"github.com/TimothyYe/godns/handler/he"
|
||||||
|
)
|
||||||
|
|
||||||
// IHandler is the interface for all DNS handlers
|
// IHandler is the interface for all DNS handlers
|
||||||
type IHandler interface {
|
type IHandler interface {
|
||||||
@ -14,11 +20,13 @@ func CreateHandler(provider string) IHandler {
|
|||||||
|
|
||||||
switch provider {
|
switch provider {
|
||||||
case godns.CLOUDFLARE:
|
case godns.CLOUDFLARE:
|
||||||
handler = IHandler(&CloudflareHandler{})
|
handler = IHandler(&cloudflare.CloudflareHandler{})
|
||||||
case godns.DNSPOD:
|
case godns.DNSPOD:
|
||||||
handler = IHandler(&DNSPodHandler{})
|
handler = IHandler(&dnspod.DNSPodHandler{})
|
||||||
case godns.HE:
|
case godns.HE:
|
||||||
handler = IHandler(&HEHandler{})
|
handler = IHandler(&he.HEHandler{})
|
||||||
|
case godns.ALIDNS:
|
||||||
|
handler = IHandler(&alidns.AliDNSHandler{})
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package handler
|
package he
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
11
utils.go
11
utils.go
@ -11,7 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
"gopkg.in/gomail.v2"
|
gomail "gopkg.in/gomail.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -42,6 +42,8 @@ const (
|
|||||||
HE = "HE"
|
HE = "HE"
|
||||||
// CLOUDFLARE for cloudflare.com
|
// CLOUDFLARE for cloudflare.com
|
||||||
CLOUDFLARE = "Cloudflare"
|
CLOUDFLARE = "Cloudflare"
|
||||||
|
// ALIDNS for AliDNS
|
||||||
|
ALIDNS = "AliDNS"
|
||||||
)
|
)
|
||||||
|
|
||||||
//GetIPFromInterface gets IP address from the specific interface
|
//GetIPFromInterface gets IP address from the specific interface
|
||||||
@ -178,6 +180,13 @@ func CheckSettings(config *Settings) error {
|
|||||||
if config.Password == "" {
|
if config.Password == "" {
|
||||||
return errors.New("password cannot be empty")
|
return errors.New("password cannot be empty")
|
||||||
}
|
}
|
||||||
|
} else if config.Provider == ALIDNS {
|
||||||
|
if config.Email == "" {
|
||||||
|
return errors.New("email cannot be empty")
|
||||||
|
}
|
||||||
|
if config.Password == "" {
|
||||||
|
return errors.New("password cannot be empty")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("please provide supported DNS provider: DNSPod/HE")
|
return errors.New("please provide supported DNS provider: DNSPod/HE")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user