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))
|
||||
* DNSPod ([https://www.dnspod.cn/](https://www.dnspod.cn/))
|
||||
* 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
|
||||
* Linux
|
||||
@ -52,7 +53,7 @@ And the binary can run well on routers.
|
||||
|
||||
* 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
|
||||
|
||||
@ -89,7 +90,7 @@ Usage of ./godns:
|
||||
|
||||
* Get [config_sample.json](https://github.com/timothyye/godns/blob/master/config_sample.json) from Github.
|
||||
* 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.
|
||||
* 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": ""
|
||||
}
|
||||
```
|
||||
### 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
|
||||
|
||||
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 (
|
||||
"bytes"
|
@ -1,4 +1,4 @@
|
||||
package handler
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"encoding/json"
|
@ -1,4 +1,4 @@
|
||||
package handler
|
||||
package dnspod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/TimothyYe/godns"
|
||||
"github.com/bitly/go-simplejson"
|
||||
simplejson "github.com/bitly/go-simplejson"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
@ -68,7 +68,7 @@ func (handler *DNSPodHandler) DomainLoop(domain *godns.Domain, panicChan chan<-
|
||||
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") {
|
||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
||||
handler.UpdateIP(domainID, subDomainID, subDomain, currentIP)
|
@ -1,6 +1,12 @@
|
||||
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
|
||||
type IHandler interface {
|
||||
@ -14,11 +20,13 @@ func CreateHandler(provider string) IHandler {
|
||||
|
||||
switch provider {
|
||||
case godns.CLOUDFLARE:
|
||||
handler = IHandler(&CloudflareHandler{})
|
||||
handler = IHandler(&cloudflare.CloudflareHandler{})
|
||||
case godns.DNSPOD:
|
||||
handler = IHandler(&DNSPodHandler{})
|
||||
handler = IHandler(&dnspod.DNSPodHandler{})
|
||||
case godns.HE:
|
||||
handler = IHandler(&HEHandler{})
|
||||
handler = IHandler(&he.HEHandler{})
|
||||
case godns.ALIDNS:
|
||||
handler = IHandler(&alidns.AliDNSHandler{})
|
||||
}
|
||||
|
||||
return handler
|
||||
|
@ -1,4 +1,4 @@
|
||||
package handler
|
||||
package he
|
||||
|
||||
import (
|
||||
"fmt"
|
11
utils.go
11
utils.go
@ -11,7 +11,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
"gopkg.in/gomail.v2"
|
||||
gomail "gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -42,6 +42,8 @@ const (
|
||||
HE = "HE"
|
||||
// CLOUDFLARE for cloudflare.com
|
||||
CLOUDFLARE = "Cloudflare"
|
||||
// ALIDNS for AliDNS
|
||||
ALIDNS = "AliDNS"
|
||||
)
|
||||
|
||||
//GetIPFromInterface gets IP address from the specific interface
|
||||
@ -178,6 +180,13 @@ func CheckSettings(config *Settings) error {
|
||||
if config.Password == "" {
|
||||
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 {
|
||||
return errors.New("please provide supported DNS provider: DNSPod/HE")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user