diff --git a/Dockerfile b/Dockerfile index cafde4e..25ce5ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ ENV AKID=1234567890 \ CMD aliyun-ddns-cli \ --ipapi ${IPAPI} \ + ${IPV6:+-6} \ auto-update \ --domain ${DOMAIN} \ --redo ${REDO} diff --git a/README.md b/README.md index 2ddf244..07e71c5 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,14 @@ COMMANDS: auto-update Auto-Update AliYun's DNS DomainRecords Record, Get IP using its getip GET-IP: - getip Get IP Combine 12 different Web-API - resolve Get DNS-IPv4 Combine 5 DNS Upstream + getip Get IP Combine 10+ different Web-API + resolve Get DNS-IPv4 Combine 4+ DNS Upstream GLOBAL OPTIONS: --access-key-id value, --id value AliYun's Access Key ID --access-key-secret value, --secret value AliYun's Access Key Secret --ipapi value, --api value Web-API to Get IP, like: http://myip.ipip.net + --ipv6, -6 IPv6 --help, -h show help --version, -v print the version ``` diff --git a/go.mod b/go.mod index f9a50a4..704dcc7 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,9 @@ module github.com/honwen/aliyun-ddns-cli go 1.16 require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d - github.com/denverdino/aliyungo v0.0.0-20210318042315-546d0768f5c7 + github.com/denverdino/aliyungo v0.0.0-20210425065611-55bee4942cba + github.com/honwen/golibs v0.1.4 github.com/honwen/ip2loc v0.1.1 - github.com/honwen/tldextract v0.1.0 - github.com/miekg/dns v1.1.41 - github.com/mr-karan/doggo v0.3.9 github.com/stretchr/testify v1.7.0 github.com/urfave/cli v1.22.5 ) diff --git a/go.sum b/go.sum index 8702889..f8f0722 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,42 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= +github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= +github.com/ameshkov/dnscrypt v1.1.0 h1:2vAt5dD6ZmqlAxEAfzRcLBnkvdf8NI46Kn9InSwQbSI= +github.com/ameshkov/dnscrypt v1.1.0/go.mod h1:ikduAxNLCTEfd1AaCgpIA5TgroIVQ8JY3Vb095fiFJg= +github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= +github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= +github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denverdino/aliyungo v0.0.0-20210318042315-546d0768f5c7 h1:olbRoASibs9s5oTbeNrZnWK1ARyi0xGT1EGZnMopPIU= -github.com/denverdino/aliyungo v0.0.0-20210318042315-546d0768f5c7/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/denverdino/aliyungo v0.0.0-20210425065611-55bee4942cba h1:3g+YC/AyMKyPBDTKKMxTHekNFhBZPSxa/ckOPjOJPuU= +github.com/denverdino/aliyungo v0.0.0-20210425065611-55bee4942cba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-chi/chi v1.5.3/go.mod h1:Q8xfe6s3fjZyMr8ZTv5jL+vxhVaFyCq2s+RvSfzTD0E= -github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/honwen/golibs v0.1.4 h1:7B0gXElZex11CVfNs6p9jn7uj/DFroQgI4GIcrh9Wac= +github.com/honwen/golibs v0.1.4/go.mod h1:BqdA1MZSiyB9msXACcDNxUn17DSReiIvUWARfpzPI30= github.com/honwen/ip2loc v0.1.1 h1:sLdnE6U2cbiDxY+uKxtBORqZILnspTWp742CpFJjvTQ= github.com/honwen/ip2loc v0.1.1/go.mod h1:4loWbEvIxSNNr6pDp2YfPnAnJN/SKN7LiHfFr0rHB9o= -github.com/honwen/tldextract v0.1.0 h1:ebl2hs0pxhHZNKqTrWAwt+N9Hf+LI4NH5AUoATZ+v/I= -github.com/honwen/tldextract v0.1.0/go.mod h1:89LulaARVT8BtmLOXfwhIGkEICCKvlt5gUQ8HpSCAqo= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/knadh/koanf v0.14.0/go.mod h1:H5mEFsTeWizwFXHKtsITL5ipsLTuAMQoGuQpp+1JL9U= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mr-karan/doggo v0.3.9 h1:X5s+8tuQbsbFl6KIJFIA4HWBUvq8A34h1RuggWRkaX4= -github.com/mr-karan/doggo v0.3.9/go.mod h1:4Ndm6Y0wp8z0fW6O/NUEx2ZNQQx0sleONl322KP6EB8= +github.com/mr-karan/doggo v0.4.0 h1:d5z93qsmEnWLZ2iNn5uBlN70R71wk9RraD02x0kTt/g= +github.com/mr-karan/doggo v0.4.0/go.mod h1:Fr8UVbK1pBT1p3hXjF0dWDKQ+K096siHYnXT0Em0btA= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -54,12 +61,16 @@ github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210502030024-e5908800b52b h1:jCRjgm6WJHzM8VQrm/es2wXYqqbq0NZ1yXFHHgzkiVQ= +golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -70,13 +81,16 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 4f508f4..bb1954e 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,8 @@ import ( "github.com/denverdino/aliyungo/common" dns "github.com/honwen/aliyun-ddns-cli/alidns" + "github.com/honwen/golibs/cip" + "github.com/honwen/golibs/domain" "github.com/urfave/cli" ) @@ -100,15 +102,11 @@ func (ak *AccessKey) AddRecord(domain, rr, dmType, value string) (err error) { return err } -func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr string, ipv6 bool) (err error) { +func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr, recordType string) (err error) { fulldomain := strings.Join([]string{rr, domain}, `.`) - if getDNS(fulldomain, ipv6) == ipaddr { + if reslove(fulldomain) == ipaddr { return // Skip } - recordType := "A" - if ipv6 { - recordType = "AAAA" - } targetCnt := 0 var target *dns.RecordTypeNew if dnsRecords, err := ak.ListRecord(domain); err == nil { @@ -137,7 +135,7 @@ func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr string, ipv6 bool) } if err != nil && strings.Contains(err.Error(), `DomainRecordDuplicate`) { ak.DelRecord(rr, domain) - return ak.CheckAndUpdateRecord(rr, domain, ipaddr, ipv6) + return ak.CheckAndUpdateRecord(rr, domain, ipaddr, recordType) } return err } @@ -168,11 +166,11 @@ func main() { }, }, Action: func(c *cli.Context) error { - if err := appInit(c); err != nil { + if err := appInit(c, true); err != nil { return err } // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain")) - _, domain := splitDomain(c.String("domain")) + _, domain := domain.SplitDomainToRR(c.String("domain")) if dnsRecords, err := accessKey.ListRecord(domain); err != nil { fmt.Printf("%+v", err) } else { @@ -194,11 +192,11 @@ func main() { }, }, Action: func(c *cli.Context) error { - if err := appInit(c); err != nil { + if err := appInit(c, true); err != nil { return err } // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain")) - if err := accessKey.DelRecord(splitDomain(c.String("domain"))); err != nil { + if err := accessKey.DelRecord(domain.SplitDomainToRR(c.String("domain"))); err != nil { fmt.Printf("%+v", err) } else { fmt.Println(c.String("domain"), "Deleted") @@ -219,18 +217,18 @@ func main() { Name: "ipaddr, i", Usage: "Specific `IP`. like 1.2.3.4", }, - cli.BoolFlag{ - Name: "ipv6, 6", - Usage: "update IPv6 address", - }, }, Action: func(c *cli.Context) error { - if err := appInit(c); err != nil { + if err := appInit(c, true); err != nil { return err } fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"), c.String("ipaddr")) - rr, domain := splitDomain(c.String("domain")) - if err := accessKey.CheckAndUpdateRecord(rr, domain, c.String("ipaddr"), c.Bool("ipv6")); err != nil { + rr, domain := domain.SplitDomainToRR(c.String("domain")) + recordType := "A" + if c.GlobalBool("ipv6") { + recordType = "AAAA" + } + if err := accessKey.CheckAndUpdateRecord(rr, domain, c.String("ipaddr"), recordType); err != nil { log.Printf("%+v", err) } else { log.Println(c.String("domain"), c.String("ipaddr"), ip2locCN(c.String("ipaddr"))) @@ -252,17 +250,17 @@ 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.BoolFlag{ - Name: "ipv6, 6", - Usage: "update IPv6 address", - }, }, Action: func(c *cli.Context) error { - if err := appInit(c); err != nil { + if err := appInit(c, true); err != nil { return err } // fmt.Println(c.Command.Name, "task: ", accessKey, c.String("domain"), c.Int64("redo")) - rr, domain := splitDomain(c.String("domain")) + rr, domain := domain.SplitDomainToRR(c.String("domain")) + recordType := "A" + if c.GlobalBool("ipv6") { + recordType = "AAAA" + } redoDurtionStr := c.String("redo") if len(redoDurtionStr) > 0 && !regexp.MustCompile(`\d+[Rr]?$`).MatchString(redoDurtionStr) { return errors.New(`redo format: [0-9]+[Rr]?$`) @@ -270,23 +268,20 @@ func main() { randomDelay := regexp.MustCompile(`\d+[Rr]$`).MatchString(redoDurtionStr) redoDurtion := 0 if randomDelay { - // Print Version if exist - if !strings.HasPrefix(VersionString, "MISSING") { - fmt.Fprintf(os.Stderr, "%s %s\n", strings.ToUpper(c.App.Name), c.App.Version) - } redoDurtion, _ = strconv.Atoi(redoDurtionStr[:len(redoDurtionStr)-1]) } else { redoDurtion, _ = strconv.Atoi(redoDurtionStr) } + // Print Version if exist + if redoDurtion > 0 && !strings.HasPrefix(VersionString, "MISSING") { + fmt.Fprintf(os.Stderr, "%s %s\n", strings.ToUpper(c.App.Name), c.App.Version) + } for { - autoip := getIP() - if c.Bool("ipv6") { - autoip = getIP6() - } + autoip := myip() if len(autoip) == 0 { log.Printf("# Err-CheckAndUpdateRecord: [%s]", "IP is empty, PLZ check network") } else { - if err := accessKey.CheckAndUpdateRecord(rr, domain, autoip, c.Bool("ipv6")); err != nil { + if err := accessKey.CheckAndUpdateRecord(rr, domain, autoip, recordType); err != nil { log.Printf("# Err-CheckAndUpdateRecord: [%+v]", err) } else { log.Println(c.String("domain"), autoip, ip2locCN(autoip)) @@ -307,50 +302,34 @@ func main() { { Name: "getip", Category: "GET-IP", - Usage: fmt.Sprintf(" Get IP Combine %d different Web-API", len(ipAPI)), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "ipv6, 6", - Usage: "IPv6", - }, - }, + Usage: fmt.Sprintf(" Get IP Combine 10+ different Web-API"), Action: func(c *cli.Context) error { - // fmt.Println(c.Command.Name, "task: ", c.Command.Usage) - if c.Bool("ipv6") { - ip := getIP6() - fmt.Println(ip) - } else { - ip := getIP() - fmt.Println(ip, ip2locCN(ip)) + if err := appInit(c, false); err != nil { + return err } + // fmt.Println(c.Command.Name, "task: ", c.Command.Usage) + ip := myip() + fmt.Println(ip, ip2locCN(ip)) return nil }, }, { Name: "resolve", Category: "GET-IP", - Usage: fmt.Sprintf(" Get DNS-IPv4 Combine %d DNS Upstream", len(dnsUpStream)), + Usage: fmt.Sprintf(" Get DNS-IPv4 Combine 4+ DNS Upstream"), Flags: []cli.Flag{ cli.StringFlag{ Name: "domain, d", Usage: "Specific `DomainName`. like ddns.aliyun.com", }, - cli.BoolFlag{ - Name: "ipv6, 6", - Usage: "IPv6", - }, }, Action: func(c *cli.Context) error { - // fmt.Println(c.Command.Name, "task: ", c.Command.Usage) - ip := getDNS(c.String("domain"), c.Bool("ipv6")) - if len(ip) < 1 { - return nil - } - if c.Bool("ipv6") { - fmt.Println(ip) - } else { - fmt.Println(ip, ip2locCN(ip)) + if err := appInit(c, false); err != nil { + return err } + // fmt.Println(c.Command.Name, "task: ", c.Command.Usage) + ip := reslove(c.String("domain")) + fmt.Println(ip, ip2locCN(ip)) return nil }, }, @@ -368,36 +347,53 @@ func main() { Name: "ipapi, api", Usage: "Web-API to Get IP, like: http://myip.ipip.net", }, + cli.BoolFlag{ + Name: "ipv6, 6", + Usage: "IPv6", + }, } app.Action = func(c *cli.Context) error { - return appInit(c) + return appInit(c, true) } app.Run(os.Args) } -func appInit(c *cli.Context) error { +func appInit(c *cli.Context, checkAccessKey bool) error { akids := []string{c.GlobalString("access-key-id"), os.Getenv("AKID"), os.Getenv("AccessKeyID")} akscts := []string{c.GlobalString("access-key-secret"), os.Getenv("AKSCT"), os.Getenv("AccessKeySecret")} sort.Sort(sort.Reverse(sort.StringSlice(akids))) sort.Sort(sort.Reverse(sort.StringSlice(akscts))) accessKey.ID = akids[0] accessKey.Secret = akscts[0] - if accessKey.getClient() == nil { + if checkAccessKey && accessKey.getClient() == nil { cli.ShowAppHelp(c) return errors.New("access-key is empty") } - newIPAPI := make([]string, 0) + if c.GlobalBool("ipv6") { + funcs["myip"] = cip.MyIPv6 + funcs["reslove"] = cip.ResloveIPv6 + } + + ipapi := []string{} for _, api := range c.GlobalStringSlice("ipapi") { if !regexp.MustCompile(`^https?://.*`).MatchString(api) { api = "http://" + api } if regexp.MustCompile(`(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`).MatchString(api) { - newIPAPI = append(newIPAPI, api) + ipapi = append(ipapi, api) } } - if len(newIPAPI) > 0 { - ipAPI = newIPAPI + if len(ipapi) > 0 { + regx := regexp.MustCompile(cip.RegxIPv4) + if c.GlobalBoolT("ipv6") { + regx = regexp.MustCompile(cip.RegxIPv6) + } + funcs["myip"] = func() string { + return cip.FastWGetWithVailder(ipapi, func(s string) string { + return regx.FindString((s)) + }) + } } return nil diff --git a/utils.go b/utils.go index 3add42e..f2b2eb9 100644 --- a/utils.go +++ b/utils.go @@ -1,182 +1,25 @@ package main import ( + "errors" "fmt" - "io/ioutil" "log" - "math/rand" - "net/http" - "regexp" + "reflect" "strings" - "time" - "github.com/asaskevich/govalidator" + "github.com/honwen/golibs/cip" "github.com/honwen/ip2loc" - "github.com/honwen/tldextract" - "github.com/miekg/dns" - "github.com/mr-karan/doggo/pkg/resolvers" - "github.com/mr-karan/doggo/pkg/utils" ) -const minTimeout = 2000 * time.Millisecond -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)` -const regxIP6 = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{1,4}$|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})` - -var ipAPI = []string{ - "http://www.net.cn/static/customercare/yourip.asp", "http://ddns.oray.com/checkip", "http://haoip.cn", - "http://members.3322.org/dyndns/getip", "http://ns1.dnspod.net:6666", "http://v4.myip.la", - "http://pv.sohu.com/cityjson?ie=utf-8", "http://whois.pconline.com.cn/ipJson.jsp", - "http://api-ipv4.ip.sb/ip", "http://ip-api.com/", "http://whatismyip.akamai.com/", -} - -var ip6API = []string{ - "http://speed.neu6.edu.cn/getIP.php", "http://v6.myip.la", "http://api-ipv6.ip.sb/ip", "http://ip6only.me/api/", "http://v6.ipv6-test.com/api/myip.php", "https://v6.ident.me", -} - -var curlVer = []string{ - "7.75.0", "7.74.0", "7.73.0", "7.72.0", "7.71.1", "7.71.0", "7.70.0", "7.69.1", "7.69.0", "7.68.0", "7.67.0", "7.66.0", - "7.65.3", "7.65.2", "7.65.1", "7.65.0", "7.64.1", "7.64.0", "7.63.0", "7.62.0", "7.61.1", "7.61.0", "7.60.0", "7.59.0", - "7.58.0", "7.57.0", "7.56.1", "7.56.0", "7.55.1", "7.55.0", "7.54.1", "7.54.0", "7.53.1", "7.53.0", "7.52.1", "7.52.0", - "7.51.0", "7.50.3", "7.50.2", "7.50.1", "7.50.0", "7.49.1", "7.49.0", "7.48.0", "7.47.1", "7.47.0", "7.46.0", "7.45.0", - "7.44.0", "7.43.0", "7.42.1", "7.42.0", "7.41.0", "7.40.0", "7.39.0", "7.38.0", "7.37.1", "7.37.0", "7.36.0", -} - -var dnsUpStream = []resolvers.Resolver{} - -func init() { - var upStreams = []string{ - "tls://223.5.5.5:853", "tls://223.6.6.6:853", "https://223.5.5.5/dns-query", "https://223.6.6.6/dns-query", - } - var opts = resolvers.Options{ - Timeout: 2000 * time.Millisecond, - Logger: utils.InitLogger(), - } - var dotOpts = resolvers.ClassicResolverOpts{ - UseTLS: true, - UseTCP: true, - } - - for _, upstream := range upStreams { - var ns resolvers.Resolver - switch { - case strings.HasPrefix(upstream, "https://"): - ns, _ = resolvers.NewDOHResolver(upstream, opts) - case strings.HasPrefix(upstream, "tls://"): - ns, _ = resolvers.NewClassicResolver(upstream[6:], dotOpts, opts) - default: - continue - } - dnsUpStream = append(dnsUpStream, ns) - } -} - -func getIP() (ip string) { - return apiGetIP(ipAPI, regxIP) -} - -func getIP6() (ip string) { - return apiGetIP(ip6API, regxIP6) +var funcs = map[string]interface{}{ + "myip": cip.MyIPv4, + "reslove": cip.ResloveIPv4, } -func apiGetIP(ipAPI []string, regxIP string) (ip string) { - var ( - length = len(ipAPI) - ipMap = make(map[string]int, length/5) - cchan = make(chan string, length/2) - regx = regexp.MustCompile(regxIP) - maxCount = -1 - ) - for _, url := range ipAPI { - go func(url string) { - cchan <- regx.FindString(wGet(url, minTimeout)) - }(url) - } - for i := 0; i < length; i++ { - v := <-cchan - if 0 == len(v) { - continue - } - if ipMap[v]++; ipMap[v] >= length/2 { - return v - } - } - for k, v := range ipMap { - if v > maxCount { - maxCount = v - ip = k - } - } - - // Use First ipAPI as failsafe - if 0 == len(ip) { - ip = regexp.MustCompile(regxIP).FindString(wGet(ipAPI[0], 5*minTimeout)) - } - return -} - -func wGet(url string, timeout time.Duration) (str string) { - request, err := http.NewRequest("GET", url, nil) - request.Header.Set("User-Agent", "curl/"+curlVer[rand.Intn(len(curlVer))]) - if err != nil { - return - } - client := &http.Client{ - Timeout: timeout, - } - resp, err := client.Do(request) - if err != nil { +func ip2locCN(ip string) (str string) { + if strings.Count(ip, `.`) < 3 { return } - body, err := ioutil.ReadAll(resp.Body) - resp.Body.Close() - str = string(body) - // fmt.Println(url, regexp.MustCompile(regxIP).FindString(str)) - return -} - -func getDNS(domain string, ipv6 bool) (ip string) { - var ( - dnsMap = make(map[string]int, len(dnsUpStream)) - cchan = make(chan string, len(dnsUpStream)) - maxCount = -1 - ) - - for i := range dnsUpStream { - go func(resolver *resolvers.Resolver) { - qtype := dns.TypeA - if ipv6 { - qtype = dns.TypeAAAA - } - resp, err := (*resolver).Lookup(dns.Question{Name: domain, Qtype: qtype}) - if err == nil && len(resp.Answers) > 0 { - cchan <- resp.Answers[0].Address - } else { - cchan <- "" // SOA - } - }(&dnsUpStream[i]) - } - - for i := 0; i < len(dnsUpStream); i++ { - v := <-cchan - if len(v) == 0 { - continue - } - if dnsMap[v] >= len(dnsUpStream)/2 { - return v - } - dnsMap[v]++ - } - - for k, v := range dnsMap { - if v > maxCount { - maxCount = v - ip = k - } - } - return -} - -func ip2locCN(ip string) (str string) { if loc, err := ip2loc.IP2loc(ip); err != nil { log.Printf("%+v", err) } else { @@ -191,37 +34,35 @@ func ip2locCN(ip string) (str string) { return } -func splitDomain(fulldomain string) (rr, domain string) { - wildCard := false - if strings.HasPrefix(fulldomain, `*.`) { - wildCard = true - fulldomain = fulldomain[2:] - } - - for len(fulldomain) > 0 && strings.HasSuffix(fulldomain, `.`) { - fulldomain = fulldomain[:len(fulldomain)-1] +func Call(m map[string]interface{}, name string, params ...interface{}) (result []reflect.Value, err error) { + f := reflect.ValueOf(m[name]) + if len(params) != f.Type().NumIn() { + err = errors.New("The number of params is not adapted.") + return } - domainInfo := tldextract.New().Extract(fulldomain) - if !govalidator.IsDNSName(fulldomain) || len(domainInfo.Tld) == 0 || len(domainInfo.Root) == 0 { - log.Fatal("Not a Vaild Domain") - return + in := make([]reflect.Value, len(params)) + for k, param := range params { + in[k] = reflect.ValueOf(param) } + result = f.Call(in) + return +} - domain = domainInfo.Root + `.` + domainInfo.Tld - rr = domainInfo.Sub - if wildCard { - if len(rr) == 0 { - rr = `*` - } else { - rr = `*.` + rr +func myip() (ip string) { + if result, err := Call(funcs, "myip"); err == nil { + for _, r := range result { + return r.String() } } + return +} - if len(rr) == 0 { - rr = `@` +func reslove(domain string) (ip string) { + if result, err := Call(funcs, "reslove", domain); err == nil { + for _, r := range result { + return r.String() + } } - - // fmt.Println(rr, domain) return } diff --git a/utils_test.go b/utils_test.go index b1cb4c5..1e863be 100644 --- a/utils_test.go +++ b/utils_test.go @@ -4,6 +4,8 @@ import ( "regexp" "testing" + "github.com/honwen/golibs/cip" + "github.com/honwen/golibs/domain" "github.com/stretchr/testify/assert" ) @@ -30,82 +32,79 @@ func TestIp2locCN(t *testing.T) { } func TestGetIPv4(t *testing.T) { - ip4 := getIP() - assert.True(t, regexp.MustCompile(regxIP).MatchString(ip4) || len(ip4) == 0) + funcs["myip"] = cip.MyIPv4 + ip4 := myip() + assert.True(t, regexp.MustCompile(cip.RegxIPv4).MatchString(ip4) || len(ip4) == 0) } func TestGetIPv6(t *testing.T) { - ip6 := getIP6() - assert.True(t, regexp.MustCompile(regxIP6).MatchString(ip6) || len(ip6) == 0) + funcs["myip"] = cip.MyIPv6 + ip6 := myip() + assert.True(t, regexp.MustCompile(cip.RegxIPv6).MatchString(ip6) || len(ip6) == 0) } -func TestGetDNSv4(t *testing.T) { - assert.Contains(t, []string{"8.8.8.8", "8.8.4.4"}, getDNS("dns.google", false)) - assert.Contains(t, []string{"223.6.6.6", "223.5.5.5"}, getDNS("dns.alidns.com", false)) +func TestResloveIPv4(t *testing.T) { + funcs["reslove"] = cip.ResloveIPv4 + assert.Contains(t, []string{"8.8.8.8", "8.8.4.4"}, reslove("dns.google")) + assert.Contains(t, []string{"223.6.6.6", "223.5.5.5"}, reslove("dns.alidns.com")) } -func TestGetDNSv6(t *testing.T) { - assert.Contains(t, []string{"2001:4860:4860::8844", "2001:4860:4860::8888"}, getDNS("dns.google", true)) - assert.Contains(t, []string{"2400:3200::1", "2400:3200:baba::1"}, getDNS("dns.alidns.com", true)) +func TestResloveIPv6(t *testing.T) { + funcs["reslove"] = cip.ResloveIPv6 + assert.Contains(t, []string{"2001:4860:4860::8844", "2001:4860:4860::8888"}, reslove("dns.google")) + assert.Contains(t, []string{"2400:3200::1", "2400:3200:baba::1"}, reslove("dns.alidns.com")) } -// func TestSplitDomainInVaild(t *testing.T) { -// rr, domain := splitDomain("a.example.com.invaild") - -// assert.Equal(t, rr, "") -// assert.Equal(t, domain, "") -// } - func TestSplitDomain001(t *testing.T) { - rr, domain := splitDomain("a.example.com") + rr, domain := domain.SplitDomainToRR("a.example.com") assert.Equal(t, rr, "a") assert.Equal(t, domain, "example.com") } func TestSplitDomain002(t *testing.T) { - rr, domain := splitDomain("example.com") + rr, domain := domain.SplitDomainToRR("example.com") assert.Equal(t, rr, "@") assert.Equal(t, domain, "example.com") } func TestSplitDomain003(t *testing.T) { - rr, domain := splitDomain("*.example.com") + rr, domain := domain.SplitDomainToRR("*.example.com") assert.Equal(t, rr, "*") assert.Equal(t, domain, "example.com") } func TestSplitDomain004(t *testing.T) { - rr, domain := splitDomain("*.a.example.com") + rr, domain := domain.SplitDomainToRR("*.a.example.com") assert.Equal(t, rr, "*.a") assert.Equal(t, domain, "example.com") } func TestSplitDomain005(t *testing.T) { - rr, domain := splitDomain("*.b.a.example.com") + rr, domain := domain.SplitDomainToRR("*.b.a.example.com") assert.Equal(t, rr, "*.b.a") assert.Equal(t, domain, "example.com") } func TestSplitDomain006(t *testing.T) { - rr, domain := splitDomain("a.example.co.kr") + rr, domain := domain.SplitDomainToRR("a.example.co.kr") assert.Equal(t, rr, "a") assert.Equal(t, domain, "example.co.kr") } func TestSplitDomain007(t *testing.T) { - rr, domain := splitDomain("*.a.example.co.kr") + rr, domain := domain.SplitDomainToRR("*.a.example.co.kr") assert.Equal(t, rr, "*.a") assert.Equal(t, domain, "example.co.kr") } func TestSplitDomain008(t *testing.T) { - rr, domain := splitDomain("example.co.kr") + rr, domain := domain.SplitDomainToRR("example.co.kr") assert.Equal(t, rr, "@") assert.Equal(t, domain, "example.co.kr")