From c5e0c33b79daf5d6d09d3123427f985f7098c9d9 Mon Sep 17 00:00:00 2001 From: "honwen.chan" Date: Fri, 16 Dec 2016 22:14:17 +0800 Subject: [PATCH] first commit --- .gitignore | 7 ++ README.md | 35 ++++++- build-release.sh | 52 +++++++++++ main.go | 235 +++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 29 ++++++ 5 files changed, 357 insertions(+), 1 deletion(-) create mode 100755 build-release.sh create mode 100644 main.go create mode 100644 utils.go diff --git a/.gitignore b/.gitignore index daf913b..825b13c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ # Folders _obj _test +.vscode/ +release/ # Architecture specific extensions/prefixes *.[568vq] @@ -22,3 +24,8 @@ _testmain.go *.exe *.test *.prof +*.upx + +debug +aliyun-ddns-cli +aliddns* diff --git a/README.md b/README.md index 6379a5d..29e7ab5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,35 @@ # aliyun-ddns-cli -aliyun-ddns-cli golang +``` +NAME: + aliddns - aliyun-ddns-cli + +USAGE: + aliyun-ddns-cli [global options] command [command options] [arguments...] + +VERSION: + XXXXXX + +COMMANDS: + help, h Shows a list of commands or help for one command + DDNS: + list List AliYun's DNS DomainRecords Record + update Update AliYun's DNS DomainRecords Record + auto-update Auto-Update AliYun's DNS DomainRecords Record, Get IP using http://ip.cn + GET-IP: + getip Get IP using http://ip.cn + getip-intl Get IP using http://ipinfo.io + +GLOBAL OPTIONS: + --access-key-id value, --id value AliYun's Access Key ID + --access-key-secret value, --secret value AliYun's Access Key Secret + --help, -h show help + --version, -v print the version + + +EXAMPLE: + +aliddns --id ${AccessKeyID} --secret ${AccessKeySecret} auto-update --domain ddns.example.win + +aliddns --id ${AccessKeyID} --secret ${AccessKeySecret} update --domain ddns.example.win --ipaddr $(ifconfig pppoe-wan | sed -n '2{s/[^0-9]*://;s/[^0-9.].*//p}') + +``` diff --git a/build-release.sh b/build-release.sh new file mode 100755 index 0000000..e751e42 --- /dev/null +++ b/build-release.sh @@ -0,0 +1,52 @@ +#!/bin/bash +MD5='md5sum' +unamestr=`uname` +if [[ "$unamestr" == 'Darwin' ]]; then + MD5='md5' +fi + +UPX=false +if hash upx 2>/dev/null; then + UPX=true +fi + +VERSION=`date -u +%Y%m%d` +LDFLAGS="-X main.version=$VERSION -s -w -linkmode external -extldflags -static" +GCFLAGS="" + +OSES=(windows linux darwin freebsd) +ARCHS=(amd64 386) +rm -rf ./release +mkdir -p ./release +for os in ${OSES[@]}; do + for arch in ${ARCHS[@]}; do + suffix="" + if [ "$os" == "windows" ]; then + suffix=".exe" + LDFLAGS="-X main.version=$VERSION -s -w" + fi + env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o ./release/aliddns_${os}_${arch}${suffix} github.com/chenhw2/aliyun-ddns-cli + if $UPX; then upx -9 ./release/aliddns_${os}_${arch}${suffix};fi + tar -zcf ./release/aliddns_${os}-${arch}-$VERSION.tar.gz ./release/aliddns_${os}_${arch}${suffix} + $MD5 ./release/aliddns_${os}-${arch}-$VERSION.tar.gz + done +done + +# ARM +ARMS=(5 6 7) +for v in ${ARMS[@]}; do + env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o ./release/aliddns_arm$v github.com/chenhw2/aliyun-ddns-cli +done +if $UPX; then upx -9 ./release/aliddns_arm*;fi +tar -zcf ./release/aliddns_arm-$VERSION.tar.gz ./release/aliddns_arm* +$MD5 ./release/aliddns_arm-$VERSION.tar.gz + +#MIPS32LE +LDFLAGS="-X main.version=$VERSION -s -w" +env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o ./release/aliddns_mipsle github.com/chenhw2/aliyun-ddns-cli +env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o ./release/aliddns_mips github.com/chenhw2/aliyun-ddns-cli + +if $UPX; then upx -9 ./release/aliddns_mips**;fi +tar -zcf ./release/aliddns_mipsle-$VERSION.tar.gz ./release/aliddns_mipsle +tar -zcf ./release/aliddns_mips-$VERSION.tar.gz ./release/aliddns_mips +$MD5 ./release/aliddns_mipsle-$VERSION.tar.gz diff --git a/main.go b/main.go new file mode 100644 index 0000000..f762149 --- /dev/null +++ b/main.go @@ -0,0 +1,235 @@ +package main + +import ( + "errors" + "fmt" + "os" + + "regexp" + + "github.com/denverdino/aliyungo/dns" + "github.com/urfave/cli" +) + +// AccessKey from https://ak-console.aliyun.com/#/accesskey +type AccessKey struct { + ID string + Secret string +} + +func (ak *AccessKey) isFiled() bool { + return len(ak.ID) > 0 && len(ak.Secret) > 0 +} + +func (ak AccessKey) String() string { + return fmt.Sprintf("Access Key: [ ID: %s ;\t Secret: %s ]", ak.ID, ak.Secret) +} + +func (ak *AccessKey) list(domain string) (dnsRecords []dns.RecordType, err error) { + client := dns.NewClient(ak.ID, ak.Secret) + res, err := client.DescribeDomainRecords( + &dns.DescribeDomainRecordsArgs{ + DomainName: domain, + }) + if err != nil { + return + } + dnsRecords = res.DomainRecords.Record + return +} + +func (ak *AccessKey) update(recordID, rr, value string) (err error) { + client := dns.NewClient(ak.ID, ak.Secret) + _, err = client.UpdateDomainRecord( + &dns.UpdateDomainRecordArgs{ + RecordId: recordID, + RR: rr, + Value: value, + Type: dns.ARecord, + }) + return +} + +var ( + accessKey AccessKey + version = "MISSING build version [git hash]" +) + +func main() { + app := cli.NewApp() + app.Name = "aliddns" + app.Usage = "aliyun-ddns-cli" + app.Version = version + app.Commands = []cli.Command{ + { + Name: "list", + Category: "DDNS", + Usage: "List AliYun's DNS DomainRecords Record", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "domain, d", + Usage: "specify `DomainName`. like aliyun.com", + }, + }, + Action: func(c *cli.Context) error { + if err := appInit(c); err != nil { + return err + } + // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain")) + dnsRecords, err := accessKey.list(c.String("domain")) + if err != nil { + fmt.Printf("%+v", err) + } else { + for _, v := range dnsRecords { + fmt.Println(v.RR+`.`+v.DomainName, v.Value) + } + } + return nil + }, + }, + { + Name: "update", + Category: "DDNS", + Usage: "Update AliYun's DNS DomainRecords Record", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "domain, d", + Usage: "specify `DomainName`. like ddns.aliyun.com", + }, + cli.StringFlag{ + Name: "ipaddr, i", + Usage: "specify `IP`. like 1.2.3.4", + }, + }, + Action: func(c *cli.Context) error { + if err := appInit(c); err != nil { + return err + } + // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"), c.String("ipaddr")) + rr := regexp.MustCompile(`\.[^\.]*`).ReplaceAllString(c.String("domain"), "") + domain := regexp.MustCompile(`^[^\.]*\.`).ReplaceAllString(c.String("domain"), "") + // fmt.Println(rr, domain) + dnsRecords, err := accessKey.list(domain) + var target *dns.RecordType + if err != nil { + fmt.Printf("%+v", err) + } else { + for i := range dnsRecords { + if dnsRecords[i].RR == rr { + target = &dnsRecords[i] + break + } + } + } + if target != nil { + ipaddr := getIPipcn() + if ipaddr == target.Value { + fmt.Println(target.RR+`.`+target.DomainName, ipaddr) + return nil + } + err = accessKey.update(target.RecordId, target.RR, c.String("ipaddr")) + if err != nil { + fmt.Printf("%+v", err) + } else { + fmt.Println(target.RR+`.`+target.DomainName, c.String("ipaddr")) + } + } else { + fmt.Println("Can't Find target") + } + return nil + }, + }, + { + Name: "auto-update", + Category: "DDNS", + Usage: "Auto-Update AliYun's DNS DomainRecords Record, Get IP using http://ip.cn", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "domain, d", + Usage: "specify `DomainName`. like ddns.aliyun.com", + }, + }, + Action: func(c *cli.Context) error { + if err := appInit(c); err != nil { + return err + } + // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain")) + rr := regexp.MustCompile(`\.[^\.]*`).ReplaceAllString(c.String("domain"), "") + domain := regexp.MustCompile(`^[^\.]*\.`).ReplaceAllString(c.String("domain"), "") + // fmt.Println(rr, domain) + dnsRecords, err := accessKey.list(domain) + var target *dns.RecordType + if err != nil { + fmt.Printf("%+v", err) + } else { + for i := range dnsRecords { + if dnsRecords[i].RR == rr { + target = &dnsRecords[i] + break + } + } + } + if target != nil { + ipaddr := getIPipcn() + if ipaddr == target.Value { + fmt.Println(target.RR+`.`+target.DomainName, ipaddr) + return nil + } + err = accessKey.update(target.RecordId, target.RR, ipaddr) + if err != nil { + fmt.Printf("%+v", err) + } else { + fmt.Println(target.RR+`.`+target.DomainName, ipaddr) + } + } else { + fmt.Println("Can't Find target") + } + return nil + }, + }, + { + Name: "getip", + Category: "GET-IP", + Usage: "Get IP using http://ip.cn", + Action: func(c *cli.Context) error { + // fmt.Println(c.Command.Name, "task: ", c.Command.Usage) + fmt.Println(getIPipcn()) + return nil + }, + }, + { + Name: "getip-intl", + Category: "GET-IP", + Usage: "Get IP using http://ipinfo.io", + Action: func(c *cli.Context) error { + // fmt.Println(c.Command.Name, "task: ", c.Command.Usage) + fmt.Println(getIPipio()) + return nil + }, + }, + } + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "access-key-id, id", + Usage: "AliYun's Access Key ID", + }, + cli.StringFlag{ + Name: "access-key-secret, secret", + Usage: "AliYun's Access Key Secret", + }, + } + app.Action = func(c *cli.Context) error { + return appInit(c) + } + app.Run(os.Args) +} + +func appInit(c *cli.Context) error { + accessKey.ID = c.GlobalString("access-key-id") + accessKey.Secret = c.GlobalString("access-key-secret") + if !accessKey.isFiled() { + cli.ShowAppHelp(c) + return errors.New("access-key is empty") + } + return nil +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..0f645c4 --- /dev/null +++ b/utils.go @@ -0,0 +1,29 @@ +package main + +import ( + "io/ioutil" + "net/http" + "regexp" +) + +const regxIP = `(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)` + +func getIPipcn() (ip string) { + res, err := http.Get("http://ip.cn") + if err == nil { + defer res.Body.Close() + body, _ := ioutil.ReadAll(res.Body) + ip = regexp.MustCompile(regxIP).FindString(string(body)) + } + return +} + +func getIPipio() (ip string) { + res, err := http.Get("http://ipinfo.io") + if err == nil { + defer res.Body.Close() + body, _ := ioutil.ReadAll(res.Body) + ip = regexp.MustCompile(regxIP).FindString(string(body)) + } + return +}