|
@@ -1,6 +1,7 @@
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"log"
|
|
@@ -22,9 +23,10 @@ import (
|
|
|
|
|
|
// AccessKey from https://ak-console.aliyun.com/#/accesskey
|
|
|
type AccessKey struct {
|
|
|
- ID string
|
|
|
- Secret string
|
|
|
- client *dns.Client
|
|
|
+ ID string
|
|
|
+ Secret string
|
|
|
+ client *dns.Client
|
|
|
+ managedDomains []string
|
|
|
}
|
|
|
|
|
|
func (ak *AccessKey) getClient() *dns.Client {
|
|
@@ -42,6 +44,42 @@ func (ak AccessKey) String() string {
|
|
|
return fmt.Sprintf("Access Key: [ ID: %s ;\t Secret: %s ]", ak.ID, ak.Secret)
|
|
|
}
|
|
|
|
|
|
+func (ak *AccessKey) ListManagedDomains() (domains []string, err error) {
|
|
|
+ var resp []dns.DomainType
|
|
|
+ resp, err = ak.getClient().DescribeDomains(
|
|
|
+ &dns.DescribeDomainsArgs{
|
|
|
+ Pagination: common.Pagination{PageSize: 50},
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ domains = make([]string, len(resp))
|
|
|
+ for i, v := range resp {
|
|
|
+ domains[i] = v.DomainName
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (ak *AccessKey) AutocheckDomainRR(rr, domain string) (r, d string, err error) {
|
|
|
+ if contains(ak.managedDomains, domain) {
|
|
|
+ return rr, domain, nil
|
|
|
+ } else {
|
|
|
+ if !strings.Contains(rr, `.`) {
|
|
|
+ return "", "", fmt.Errorf("Domain [%s.%s] Not Managed", rr, domain)
|
|
|
+ } else {
|
|
|
+ rrs := strings.Split(rr, `.`)
|
|
|
+ for i := len(rrs) - 1; i > 0; i-- {
|
|
|
+ d = strings.Join(append(rrs[i:], domain), `.`)
|
|
|
+ if contains(ak.managedDomains, d) {
|
|
|
+ r = strings.Join(rrs[:i], `.`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "", "", fmt.Errorf("Domain [%s.%s] Not Managed", rr, domain)
|
|
|
+}
|
|
|
+
|
|
|
func (ak *AccessKey) ListRecord(domain string) (dnsRecords []dns.RecordTypeNew, err error) {
|
|
|
var resp *dns.DescribeDomainRecordsNewResponse
|
|
|
for idx := 1; idx <= 99; idx++ {
|
|
@@ -80,29 +118,31 @@ func (ak *AccessKey) DelRecord(rr, domain string) (err error) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func (ak *AccessKey) UpdateRecord(recordID, rr, dmType, value string) (err error) {
|
|
|
+func (ak *AccessKey) UpdateRecord(recordID, rr, dmType, value string, ttl int) (err error) {
|
|
|
_, err = ak.getClient().UpdateDomainRecord(
|
|
|
&dns.UpdateDomainRecordArgs{
|
|
|
RecordId: recordID,
|
|
|
RR: rr,
|
|
|
Value: value,
|
|
|
Type: dmType,
|
|
|
+ TTL: json.Number(fmt.Sprint(ttl)),
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func (ak *AccessKey) AddRecord(domain, rr, dmType, value string) (err error) {
|
|
|
+func (ak *AccessKey) AddRecord(domain, rr, dmType, value string, ttl int) (err error) {
|
|
|
_, err = ak.getClient().AddDomainRecord(
|
|
|
&dns.AddDomainRecordArgs{
|
|
|
DomainName: domain,
|
|
|
RR: rr,
|
|
|
Type: dmType,
|
|
|
Value: value,
|
|
|
+ TTL: json.Number(fmt.Sprint(ttl)),
|
|
|
})
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
-func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr, recordType string) (err error) {
|
|
|
+func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr, recordType string, ttl int) (err error) {
|
|
|
fulldomain := strings.Join([]string{rr, domain}, `.`)
|
|
|
if reslove(fulldomain) == ipaddr {
|
|
|
return // Skip
|
|
@@ -126,16 +166,16 @@ func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr, recordType string)
|
|
|
}
|
|
|
|
|
|
if target == nil {
|
|
|
- err = ak.AddRecord(domain, rr, recordType, ipaddr)
|
|
|
+ err = ak.AddRecord(domain, rr, recordType, ipaddr, ttl)
|
|
|
} else if target.Value != ipaddr {
|
|
|
if target.Type != recordType {
|
|
|
return fmt.Errorf("record type error! oldType=%s, targetType=%s", target.Type, recordType)
|
|
|
}
|
|
|
- err = ak.UpdateRecord(target.RecordId, target.RR, target.Type, ipaddr)
|
|
|
+ err = ak.UpdateRecord(target.RecordId, target.RR, target.Type, ipaddr, ttl)
|
|
|
}
|
|
|
if err != nil && strings.Contains(err.Error(), `DomainRecordDuplicate`) {
|
|
|
ak.DelRecord(rr, domain)
|
|
|
- return ak.CheckAndUpdateRecord(rr, domain, ipaddr, recordType)
|
|
|
+ return ak.CheckAndUpdateRecord(rr, domain, ipaddr, recordType, ttl)
|
|
|
}
|
|
|
return err
|
|
|
}
|
|
@@ -170,12 +210,15 @@ func main() {
|
|
|
return err
|
|
|
}
|
|
|
// fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"))
|
|
|
- _, domain := domain.SplitDomainToRR(c.String("domain"))
|
|
|
+ domain := c.String("domain")
|
|
|
+ if !contains(accessKey.managedDomains, domain) {
|
|
|
+ return fmt.Errorf("Domain [%s] Not Managed", domain)
|
|
|
+ }
|
|
|
if dnsRecords, err := accessKey.ListRecord(domain); err != nil {
|
|
|
fmt.Printf("%+v", err)
|
|
|
} else {
|
|
|
for _, v := range dnsRecords {
|
|
|
- fmt.Printf("%20s %-8s %s\n", v.RR+`.`+v.DomainName, v.Type, v.Value)
|
|
|
+ fmt.Printf("%20s %-16s %s\n", v.RR+`.`+v.DomainName, fmt.Sprintf("%s(TTL:%4s)", v.Type, v.TTL), v.Value)
|
|
|
}
|
|
|
}
|
|
|
return nil
|
|
@@ -196,7 +239,11 @@ func main() {
|
|
|
return err
|
|
|
}
|
|
|
// fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"))
|
|
|
- if err := accessKey.DelRecord(domain.SplitDomainToRR(c.String("domain"))); err != nil {
|
|
|
+ rr, domain, err := accessKey.AutocheckDomainRR(domain.SplitDomainToRR(c.String("domain")))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if err := accessKey.DelRecord(rr, domain); err != nil {
|
|
|
fmt.Printf("%+v", err)
|
|
|
} else {
|
|
|
fmt.Println(c.String("domain"), "Deleted")
|
|
@@ -217,18 +264,26 @@ func main() {
|
|
|
Name: "ipaddr, i",
|
|
|
Usage: "Specific `IP`. like 1.2.3.4",
|
|
|
},
|
|
|
+ cli.IntFlag{
|
|
|
+ Name: "ttl, t",
|
|
|
+ Value: 600,
|
|
|
+ Usage: "The resolution effective time (in `seconds`)",
|
|
|
+ },
|
|
|
},
|
|
|
Action: func(c *cli.Context) error {
|
|
|
if err := appInit(c, true); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"), c.String("ipaddr"))
|
|
|
- rr, domain := domain.SplitDomainToRR(c.String("domain"))
|
|
|
+ // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"), c.String("ipaddr"))
|
|
|
+ rr, domain, err := accessKey.AutocheckDomainRR(domain.SplitDomainToRR(c.String("domain")))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
recordType := "A"
|
|
|
if c.GlobalBool("ipv6") {
|
|
|
recordType = "AAAA"
|
|
|
}
|
|
|
- if err := accessKey.CheckAndUpdateRecord(rr, domain, c.String("ipaddr"), recordType); err != nil {
|
|
|
+ if err := accessKey.CheckAndUpdateRecord(rr, domain, c.String("ipaddr"), recordType, c.Int("ttl")); err != nil {
|
|
|
log.Printf("%+v", err)
|
|
|
} else {
|
|
|
log.Println(c.String("domain"), c.String("ipaddr"), ip2locCN(c.String("ipaddr")))
|
|
@@ -250,13 +305,21 @@ func main() {
|
|
|
Value: "",
|
|
|
Usage: "redo Auto-Update, every N `Seconds`; Disable if N less than 10; End with [Rr] enable random delay: [N, 2N]",
|
|
|
},
|
|
|
+ cli.IntFlag{
|
|
|
+ Name: "ttl, t",
|
|
|
+ Value: 600,
|
|
|
+ Usage: "The resolution effective time (in `seconds`)",
|
|
|
+ },
|
|
|
},
|
|
|
Action: func(c *cli.Context) error {
|
|
|
if err := appInit(c, true); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
// fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"), c.Int64("redo"))
|
|
|
- rr, domain := domain.SplitDomainToRR(c.String("domain"))
|
|
|
+ rr, domain, err := accessKey.AutocheckDomainRR(domain.SplitDomainToRR(c.String("domain")))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
recordType := "A"
|
|
|
if c.GlobalBool("ipv6") {
|
|
|
recordType = "AAAA"
|
|
@@ -281,7 +344,7 @@ func main() {
|
|
|
if len(autoip) == 0 {
|
|
|
log.Printf("# Err-CheckAndUpdateRecord: [%s]", "IP is empty, PLZ check network")
|
|
|
} else {
|
|
|
- if err := accessKey.CheckAndUpdateRecord(rr, domain, autoip, recordType); err != nil {
|
|
|
+ if err := accessKey.CheckAndUpdateRecord(rr, domain, autoip, recordType, c.Int("ttl")); err != nil {
|
|
|
log.Printf("# Err-CheckAndUpdateRecord: [%+v]", err)
|
|
|
} else {
|
|
|
log.Println(c.String("domain"), autoip, ip2locCN(autoip))
|
|
@@ -369,6 +432,13 @@ func appInit(c *cli.Context, checkAccessKey bool) error {
|
|
|
cli.ShowAppHelp(c)
|
|
|
return errors.New("access-key is empty")
|
|
|
}
|
|
|
+ if domains, err := accessKey.ListManagedDomains(); err == nil {
|
|
|
+ // log.Println(domains)
|
|
|
+ accessKey.managedDomains = domains
|
|
|
+ } else {
|
|
|
+ cli.ShowAppHelp(c)
|
|
|
+ return errors.New("No Managed Domains")
|
|
|
+ }
|
|
|
|
|
|
if c.GlobalBool("ipv6") {
|
|
|
funcs["myip"] = cip.MyIPv6
|