Create ddns-aliyun

This commit is contained in:
Stille 2021-06-11 14:30:59 +08:00
parent 6b8bda2d07
commit f33d32c228
55 changed files with 2291 additions and 0 deletions

42
.github/workflows/ddns-aliyun.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: "ddns-aliyun docker build"
env:
PROJECT: ddns-aliyun
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set tag
id: tag
run: |
TAG=$(cat ${{ env.PROJECT }}/Dockerfile | awk 'NR==4 {print $3}')
echo "::set-env name=TAG::$TAG"
- name: Docker Hub login
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${DOCKER_PASSWORD}" | docker login --username ${DOCKER_USERNAME} --password-stdin
- name: Set up Docker Buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
buildx-version: latest
- name: Build Dockerfile
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: |
docker buildx build \
--platform=linux/amd64,linux/arm64 \
--output "type=image,push=true" \
--file ${{ env.PROJECT }}/Dockerfile ./${{ env.PROJECT }} \
--tag $(echo "${DOCKER_USERNAME}" | tr '[:upper:]' '[:lower:]')/${{ env.PROJECT }}:latest \
--tag $(echo "${DOCKER_USERNAME}" | tr '[:upper:]' '[:lower:]')/${{ env.PROJECT }}:${TAG}

View File

@ -0,0 +1,83 @@
name: Build
"on":
"push":
"tags":
- "v*"
"branches":
- "*"
"pull_request":
jobs:
build:
runs-on: ubuntu-latest
env:
GO111MODULE: "on"
steps:
- uses: actions/checkout@master
- uses: actions/setup-go@v2
with:
go-version: 1.16.4
- name: Prepare environment
run: |-
RELEASE_VERSION="${GITHUB_REF##*/}"
if [[ "${RELEASE_VERSION}" != v* ]]; then RELEASE_VERSION='dev'; fi
echo "RELEASE_VERSION=\"${RELEASE_VERSION}@${GITHUB_SHA:0:10}\"" | tee -a $GITHUB_ENV
go mod vendor
# Test
- name: Unit Testing
run: go test .
# Win
- run: GOOS=windows GOARCH=386 VERSION=${RELEASE_VERSION} make release
- run: GOOS=windows GOARCH=amd64 VERSION=${RELEASE_VERSION} make release
# MacOS
- run: GOOS=darwin GOARCH=amd64 VERSION=${RELEASE_VERSION} make release
# Linux X86/AMD64
- run: GOOS=linux GOARCH=386 VERSION=${RELEASE_VERSION} make release
- run: GOOS=linux GOARCH=amd64 VERSION=${RELEASE_VERSION} make release
# Linux ARM
- run: GOOS=linux GOARCH=arm GOARM=6 VERSION=${RELEASE_VERSION} make release
- run: GOOS=linux GOARCH=arm64 VERSION=${RELEASE_VERSION} make release
# Linux MIPS/MIPSLE
- run: GOOS=linux GOARCH=mips GOMIPS=softfloat VERSION=${RELEASE_VERSION} make release
- run: GOOS=linux GOARCH=mipsle GOMIPS=softfloat VERSION=${RELEASE_VERSION} make release
# FreeBSD X86
- run: GOOS=freebsd GOARCH=386 VERSION=${RELEASE_VERSION} make release
- run: GOOS=freebsd GOARCH=amd64 VERSION=${RELEASE_VERSION} make release
# FreeBSD ARM/ARM64
- run: GOOS=freebsd GOARCH=arm GOARM=6 VERSION=${RELEASE_VERSION} make release
- run: GOOS=freebsd GOARCH=arm64 VERSION=${RELEASE_VERSION} make release
- run: ls -l build/aliddns-*
- name: Create release
if: startsWith(github.ref, 'refs/tags/v')
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload
if: startsWith(github.ref, 'refs/tags/v')
uses: xresloader/upload-to-github-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
file: "build/aliddns-*.tar.gz;build/aliddns-*.zip"
tags: true
draft: false

33
ddns-aliyun/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
.vscode/
release/
vendor/
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
*.upx
build/
debug
aliyun-ddns-cli
aliddns*

32
ddns-aliyun/Dockerfile Normal file
View File

@ -0,0 +1,32 @@
FROM golang:alpine as builder
ENV CGO_ENABLED=0 \
GO111MODULE=on
ENV VERSION 2021-06-11
RUN apk add --update git curl
ADD . $GOPATH/src/github.com/honwen/aliyun-ddns-cli
RUN set -ex \
&& cd $GOPATH/src/github.com/honwen/aliyun-ddns-cli \
&& go build -ldflags "-X main.VersionString=$(curl -sSL https://api.github.com/repos/honwen/aliyun-ddns-cli/commits/master | \
sed -n '{/sha/p; /date/p;}' | sed 's/.* \"//g' | cut -c1-10 | tr '[:lower:]' '[:upper:]' | sed 'N;s/\n/@/g' | head -1)" . \
&& mv aliyun-ddns-cli $GOPATH/bin/
FROM alpine
LABEL MAINTAINER honwen <https://github.com/honwen>
# /usr/bin/aliyun-ddns-cli
COPY --from=builder /go/bin /usr/bin
ENV AKID=1234567890 \
AKSCT=abcdefghijklmn \
DOMAIN=ddns.example.win \
IPAPI=[IPAPI-GROUP] \
REDO=0
CMD aliyun-ddns-cli \
--ipapi ${IPAPI} \
${IPV6:+-6} \
auto-update \
--domain ${DOMAIN} \
--redo ${REDO}

21
ddns-aliyun/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2020 honwen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

43
ddns-aliyun/Makefile Normal file
View File

@ -0,0 +1,43 @@
NAME=aliddns
BASE_BUILDDIR=build
BUILDNAME=$(GOOS)-$(GOARCH)$(GOARM)
BUILDDIR=$(BASE_BUILDDIR)/$(BUILDNAME)
VERSION?=dev
ifeq ($(GOOS),windows)
ext=.exe
archiveCmd=zip -9 -r $(NAME)-$(BUILDNAME)-$(VERSION).zip $(BUILDNAME)
else
ext=
archiveCmd=tar czpvf $(NAME)-$(BUILDNAME)-$(VERSION).tar.gz $(BUILDNAME)
endif
.PHONY: default
default: build
build: clean test
go build -mod=vendor
release: check-env-release
mkdir -p $(BUILDDIR)
cp LICENSE $(BUILDDIR)/
cp README.md $(BUILDDIR)/
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -mod=vendor -ldflags "-s -w -X main.VersionString=$(VERSION)" -o $(BUILDDIR)/$(NAME)$(ext)
cd $(BASE_BUILDDIR) ; $(archiveCmd)
test:
go test -race -v -bench=. ./...
clean:
go clean
rm -rf $(BASE_BUILDDIR)
check-env-release:
@ if [ "$(GOOS)" = "" ]; then \
echo "Environment variable GOOS not set"; \
exit 1; \
fi
@ if [ "$(GOARCH)" = "" ]; then \
echo "Environment variable GOOS not set"; \
exit 1; \
fi

36
ddns-aliyun/README.md Normal file
View File

@ -0,0 +1,36 @@
# ddns-aliyun
GitHub [stilleshan/dockerfiles](https://github.com/stilleshan/dockerfiles)
Docker [stilleshan/ddns-aliyun](https://hub.docker.com/r/stilleshan/ddns-aliyun)
> *docker image support for X86 and ARM*
## 使用
### docker 启动
```shell
docker run -d \
-e "AKID=[ALIYUN's AccessKey-ID]" \
-e "AKSCT=[ALIYUN's AccessKey-Secret]" \
-e "DOMAIN=ddns.yourdomain.com" \
-e "REDO=600" \
stilleshan/ddns-aliyun
```
- AKID: 填写`AccessKeyID`
- AKSCT: 填写`AccessKeySecret`
- DOMAIN: 填写`ddns 域名`
### 示例
> 使用二级域名来作为 ddns 域名,例如以下示例使用 ddns.ioiox.com ,需要使用的正式域名添加 CNAME 指向 ddns.ioiox.com 即可.
```shell
docker run -d \
-e "AKID=kzazFUTW0uRIWWtk" \
-e "AKSCT=kcbGxOVmc9PUpJWTBWNFNWWnNSbFJW" \
-e "DOMAIN=ddns.ioiox.com" \
-e "REDO=600" \
stilleshan/ddns-aliyun
```
## 参考
[群晖NAS网络服务 - docker 部署配置阿里云 DDNS 动态域名解析](https://www.ioiox.com/archives/29.html)
更多请参考原始仓库 [honwen/aliyun-ddns-cli](https://github.com/honwen/aliyun-ddns-cli)

View File

@ -0,0 +1,41 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type AddDomainArgs struct {
DomainName string
//optional
GroupId string
}
type AddDomainResponse struct {
common.Response
DomainId string
DomainName string
GroupId string
GroupName string
PunyCode string
DnsServers struct {
DnsServer []string
}
}
// AddDomain
//
// You can read doc at https://help.aliyun.com/document_detail/29749.html?spm=5176.doc29805.6.592.6LMqlG
func (client *Client) AddDomain(args *AddDomainArgs) (response *AddDomainResponse, err error) {
action := "AddDomain"
response = &AddDomainResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,32 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type AddDomainGroupArgs struct {
GroupName string
}
type AddDomainGroupResponse struct {
common.Response
GroupId string
GroupName string
}
// AddDomainGroup
//
// You can read doc at https://help.aliyun.com/document_detail/29762.html?spm=5176.doc29749.6.604.PJtwG1
func (client *Client) AddDomainGroup(args *AddDomainGroupArgs) (response *AddDomainGroupResponse, err error) {
action := "AddDomainGroup"
response = &AddDomainGroupResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,25 @@
package dns
import (
"testing"
)
func TestAddDomainGroup(t *testing.T) {
client := NewTestClientNew()
args := AddDomainGroupArgs{
GroupName: TestDomainGroupName,
}
response, err := client.AddDomainGroup(&args)
if err == nil {
t.Logf("AddDomainGroup %s success, %v", TestDomainGroupName, response)
deleteDomainGroupArgs := DeleteDomainGroupArgs{
GroupId: response.GroupId,
}
client.DeleteDomainGroup(&deleteDomainGroupArgs)
} else {
t.Errorf("Failed to AddDomainGroup, %v", err)
}
}

View File

@ -0,0 +1,38 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type AddDomainRecordArgs struct {
DomainName string
RR string
Type string
Value string
//optional
Line string
}
type AddDomainRecordResponse struct {
common.Response
InstanceId string
RecordId string
}
// AddDomainRecord
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&AddDomainRecord
func (client *Client) AddDomainRecord(args *AddDomainRecordArgs) (response *AddDomainRecordResponse, err error) {
action := "AddDomainRecord"
response = &AddDomainRecordResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,27 @@
package dns
import (
"testing"
)
func TestAddDomainRecord(t *testing.T) {
client := NewTestClient()
addDomainRecordArgs := AddDomainRecordArgs{
DomainName: TestDomainName,
RR: "testaddrecord",
Type: ARecord,
Value: "8.8.8.8",
}
response, err := client.AddDomainRecord(&addDomainRecordArgs)
if err == nil {
t.Logf("AddDomainRecord: testaddr for domain: %s Success, %v",
TestDomainName, response)
deleteDomainRecordArgs := DeleteDomainRecordArgs{
RecordId: response.RecordId,
}
client.DeleteDomainRecord(&deleteDomainRecordArgs)
} else {
t.Errorf("Failed to AddDomainRecord: testaddr for domain: %s", TestDomainName)
}
}

View File

@ -0,0 +1,24 @@
package dns
import (
"testing"
)
func TestAddDomain(t *testing.T) {
client := NewTestClientNew()
args := AddDomainArgs{
DomainName: TestDomainName,
}
if res, err := client.AddDomain(&args); err == nil {
t.Logf("AddDomain %s success, %v", TestDomainName, res)
deleteDomainArgs := DeleteDomainArgs{
DomainName: TestDomainName,
}
client.DeleteDomain(&deleteDomainArgs)
} else {
t.Errorf("Failed to AddDomain, %v", err)
}
}

View File

@ -0,0 +1,33 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type ChangeDomainGroupArgs struct {
DomainName string
GroupId string
}
type ChangeDomainGroupResponse struct {
common.Response
GroupId string
GroupName string
}
// ChangeDomainGroup
//
// You can read doc at https://help.aliyun.com/document_detail/29765.html?spm=5176.doc29764.6.607.WUJQgE
func (client *Client) ChangeDomainGroup(args *ChangeDomainGroupArgs) (response *ChangeDomainGroupResponse, err error) {
action := "ChangeDomainGroup"
response = &ChangeDomainGroupResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,36 @@
package dns
import "testing"
func TestChangeDomainGroup(t *testing.T) {
client := NewTestClientNew()
// create origin group
addGroupArgs := AddDomainGroupArgs{
GroupName: TestDomainGroupName,
}
addGroupRes, _ := client.AddDomainGroup(&addGroupArgs)
// add domain to origin group
addDomainArgs := AddDomainArgs{
DomainName: TestDomainName,
GroupId: addGroupRes.GroupId,
}
addDomainRes, _ := client.AddDomain(&addDomainArgs)
// create new group
addGroupArgs.GroupName = TestChanegGroupName
addGroupRes, _ = client.AddDomainGroup(&addGroupArgs)
// change to new group
changeArgs := ChangeDomainGroupArgs{
DomainName: addDomainRes.DomainName,
GroupId: addGroupRes.GroupId,
}
_, err := client.ChangeDomainGroup(&changeArgs)
if err == nil {
t.Logf("ChangeDomainGroup success")
} else {
t.Errorf("Failed to ChangeDomainGroup, %v", err)
}
}

View File

@ -0,0 +1,31 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type DeleteDomainArgs struct {
DomainName string
}
type DeleteDomainResponse struct {
common.Response
DomainName string
}
// DeleteDomain
//
// You can read doc at https://help.aliyun.com/document_detail/29750.html?spm=5176.doc29766.6.593.eELaZ7
func (client *Client) DeleteDomain(args *DeleteDomainArgs) (response *DeleteDomainResponse, err error) {
action := "DeleteDomain"
response = &DeleteDomainResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,31 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type DeleteDomainGroupArgs struct {
GroupId string
}
type DeleteDomainGroupResponse struct {
common.Response
GroupName string
}
// DeleteDomainGroup
//
// You can read doc at https://help.aliyun.com/document_detail/29764.html?spm=5176.doc29763.6.606.Vm3FyC
func (client *Client) DeleteDomainGroup(args *DeleteDomainGroupArgs) (response *DeleteDomainGroupResponse, err error) {
action := "DeleteDomainGroup"
response = &DeleteDomainGroupResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,23 @@
package dns
import (
"testing"
)
func TestDeleteDomainGroup(t *testing.T) {
client := NewTestClientNew()
args := AddDomainGroupArgs{
GroupName: TestDomainGroupName,
}
res, _ := client.AddDomainGroup(&args)
deleteDomainGroupArgs := DeleteDomainGroupArgs{
GroupId: res.GroupId,
}
response, err := client.DeleteDomainGroup(&deleteDomainGroupArgs)
if err == nil {
t.Logf("DeleteDomainGroup %s success, %v", TestDomainGroupName, response)
} else {
t.Errorf("Failed to DeleteDomainGroup, %v", err)
}
}

View File

@ -0,0 +1,27 @@
package dns
import "github.com/denverdino/aliyungo/common"
type DeleteDomainRecordArgs struct {
RecordId string
}
type DeleteDomainRecordResponse struct {
common.Response
InstanceId string
RecordId string
}
// DeleteDomainRecord
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DeleteDomainRecord
func (client *Client) DeleteDomainRecord(args *DeleteDomainRecordArgs) (response *DeleteDomainRecordResponse, err error) {
action := "DeleteDomainRecord"
response = &DeleteDomainRecordResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,29 @@
package dns
import (
"testing"
)
func TestDeleteDomainRecord(t *testing.T) {
//prepare
client := NewTestClient()
addDomainRecordArgs := AddDomainRecordArgs{
DomainName: TestDomainName,
RR: "testdeleterecordid",
Type: ARecord,
Value: "8.8.8.8",
}
addResponse, err := client.AddDomainRecord(&addDomainRecordArgs)
//test delete record
deleteDomainRecordArgs := DeleteDomainRecordArgs{
RecordId: addResponse.RecordId,
}
deleteResponse, err := client.DeleteDomainRecord(&deleteDomainRecordArgs)
if err == nil {
t.Logf("DeleteDomainRecord: %v", deleteResponse)
} else {
t.Errorf("Failed to DeleteDomainRecord: %s", deleteDomainRecordArgs.RecordId)
}
}

View File

@ -0,0 +1,23 @@
package dns
import (
"testing"
)
func TestDeleteDomain(t *testing.T) {
client := NewTestClientNew()
args := AddDomainArgs{
DomainName: TestDomainName,
}
client.AddDomain(&args)
deleteDomainArgs := DeleteDomainArgs{
DomainName: TestDomainName,
}
response, err := client.DeleteDomain(&deleteDomainArgs)
if err == nil {
t.Logf("DeleteDomain %s success, %v", TestDomainName, response)
} else {
t.Errorf("Failed to DeleteDomain, %v", err)
}
}

View File

@ -0,0 +1,32 @@
package dns
import "github.com/denverdino/aliyungo/common"
type DeleteSubDomainRecordsArgs struct {
DomainName string
RR string
//optional
Type string
}
type DeleteSubDomainRecordsResponse struct {
common.Response
InstanceId string
RR string
// TotalCount int32
}
// DeleteSubDomainRecords
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DeleteSubDomainRecords
func (client *Client) DeleteSubDomainRecords(args *DeleteSubDomainRecordsArgs) (response *DeleteSubDomainRecordsResponse, err error) {
action := "DeleteSubDomainRecords"
response = &DeleteSubDomainRecordsResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,30 @@
package dns
import (
"testing"
)
func TestDeleteSubDomainRecords(t *testing.T) {
//prepare
client := NewTestClient()
addDomainRecordArgs := AddDomainRecordArgs{
DomainName: TestDomainName,
RR: "testdeletesubdomainrecords",
Type: ARecord,
Value: "8.8.8.8",
}
client.AddDomainRecord(&addDomainRecordArgs)
//test delete subdomain
deleteDomainRecordArgs := DeleteSubDomainRecordsArgs{
DomainName: TestDomainName,
RR: "testdeletesubdomainrecords",
}
deleteResponse, err := client.DeleteSubDomainRecords(&deleteDomainRecordArgs)
if err == nil {
t.Logf("DeleteDomainRecord: %v", deleteResponse)
} else {
t.Errorf("Failed to DeleteSubDomainRecords: %s", deleteDomainRecordArgs.RR)
}
}

View File

@ -0,0 +1,42 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type DomainGroupType struct {
GroupId string
GroupName string
}
type DescribeDomainGroupsArgs struct {
//optional
common.Pagination
KeyWord string
}
type DescribeDomainGroupsResponse struct {
response common.Response
common.PaginationResult
DomainGroups struct {
DomainGroup []DomainGroupType
}
}
// DescribeDomainGroups
//
// You can read doc at https://help.aliyun.com/document_detail/29766.html?spm=5176.doc29765.6.608.qcQr2R
func (client *Client) DescribeDomainGroups(args *DescribeDomainGroupsArgs) (groups []DomainGroupType, err error) {
action := "DescribeDomainGroups"
response := &DescribeDomainGroupsResponse{}
err = client.Invoke(action, args, response)
if err != nil {
log.Printf("%s error, %v", action, err)
return nil, err
}
return response.DomainGroups.DomainGroup, nil
}

View File

@ -0,0 +1,15 @@
package dns
import "testing"
func TestDescribeDomainGroups(t *testing.T) {
client := NewTestClientNew()
describeArgs := DescribeDomainGroupsArgs{}
_, err := client.DescribeDomainGroups(&describeArgs)
if err == nil {
t.Logf("DescribeDomainGroups success")
} else {
t.Errorf("Failed to DescribeDomainGroups: %v", err)
}
}

View File

@ -0,0 +1,46 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type DomainType struct {
DomainId string
DomainName string
AliDomain bool
GroupId string
GroupName string
InstanceId string
VersionCode string
PunyCode string
DnsServers struct {
DnsServer []string
}
}
type DescribeDomainInfoArgs struct {
DomainName string
}
type DescribeDomainInfoResponse struct {
response common.Response
DomainType
}
// DescribeDomainInfo
//
// You can read doc at https://help.aliyun.com/document_detail/29752.html?spm=5176.doc29751.6.595.VJM3Gy
func (client *Client) DescribeDomainInfo(args *DescribeDomainInfoArgs) (domain DomainType, err error) {
action := "DescribeDomainInfo"
response := &DescribeDomainInfoResponse{}
err = client.Invoke(action, args, response)
if err != nil {
log.Printf("%s error, %v", action, err)
return DomainType{}, err
}
return response.DomainType, nil
}

View File

@ -0,0 +1,17 @@
package dns
import "testing"
func TestDescribeDomainInfo(t *testing.T) {
client := NewTestClientNew()
describeArgs := DescribeDomainInfoArgs{
DomainName: TestDomainName,
}
_, err := client.DescribeDomainInfo(&describeArgs)
if err == nil {
t.Logf("DescribeDomainInfo success")
} else {
t.Errorf("Failed to DescribeDomainInfo: %v", err)
}
}

View File

@ -0,0 +1,26 @@
package dns
import "github.com/denverdino/aliyungo/common"
type DescribeDomainRecordInfoArgs struct {
RecordId string
}
type DescribeDomainRecordInfoResponse struct {
common.Response
RecordType
}
// DescribeDomainRecordInfo
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DescribeDomainRecordInfo
func (client *Client) DescribeDomainRecordInfo(args *DescribeDomainRecordInfoArgs) (response *DescribeDomainRecordInfoResponse, err error) {
action := "DescribeDomainRecordInfo"
response = &DescribeDomainRecordInfoResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,38 @@
package dns
import "github.com/denverdino/aliyungo/common"
// endpoint change to 'http://alidns.aliyuncs.com' then record ttl and priority change to string
type RecordTypeNew struct {
DomainName string
RecordId string
RR string
Type string
Value string
Line string
Status string
Locked bool
}
type DescribeDomainRecordInfoNewArgs struct {
RecordId string
}
type DescribeDomainRecordInfoNewResponse struct {
common.Response
RecordTypeNew
}
// DescribeDomainRecordInformation
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DescribeDomainRecordInfo
func (client *Client) DescribeDomainRecordInfoNew(args *DescribeDomainRecordInfoNewArgs) (response *DescribeDomainRecordInfoNewResponse, err error) {
action := "DescribeDomainRecordInfo"
response = &DescribeDomainRecordInfoNewResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,30 @@
package dns
import (
"testing"
)
func TestDescribeDomainRecordInfoNew(t *testing.T) {
//prepare
client := NewTestClientNew()
describeArgs := DescribeDomainRecordsNewArgs{
DomainName: TestDomainName,
}
describeArgs.PageSize = 100
describeResponse, err := client.DescribeDomainRecordsNew(&describeArgs)
if err == nil {
record := describeResponse.DomainRecords.Record[0]
arg := DescribeDomainRecordInfoNewArgs{
RecordId: record.RecordId,
}
response, err := client.DescribeDomainRecordInfoNew(&arg)
if err == nil {
t.Logf("DescribeDomainRecordInfo success: %v", response)
} else {
t.Errorf("Failed to DescribeDomainRecordInfo: %s", describeArgs.DomainName)
}
} else {
t.Errorf("Failed to DescribeDomainRecords: %s", describeArgs.DomainName)
}
}

View File

@ -0,0 +1,30 @@
package dns
import (
"testing"
)
func TestDescribeDomainRecordInfo(t *testing.T) {
//prepare
client := NewTestClient()
describeArgs := DescribeDomainRecordsArgs{
DomainName: TestDomainName,
}
describeArgs.PageSize = 100
describeResponse, err := client.DescribeDomainRecords(&describeArgs)
if err == nil {
record := describeResponse.DomainRecords.Record[0]
arg := DescribeDomainRecordInfoArgs{
RecordId: record.RecordId,
}
response, err := client.DescribeDomainRecordInfo(&arg)
if err == nil {
t.Logf("DescribeDomainRecordInfo success: %v", response)
} else {
t.Errorf("Failed to DescribeDomainRecordInfo: %s", describeArgs.DomainName)
}
} else {
t.Errorf("Failed to DescribeDomainRecords: %s", describeArgs.DomainName)
}
}

View File

@ -0,0 +1,36 @@
package dns
import "github.com/denverdino/aliyungo/common"
type DescribeDomainRecordsArgs struct {
DomainName string
//optional
common.Pagination
RRKeyWord string
TypeKeyWord string
ValueKeyWord string
}
type DescribeDomainRecordsResponse struct {
common.Response
common.PaginationResult
InstanceId string
DomainRecords struct {
Record []RecordType
}
}
// DescribeDomainRecords
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DescribeDomainRecords
func (client *Client) DescribeDomainRecords(args *DescribeDomainRecordsArgs) (response *DescribeDomainRecordsResponse, err error) {
action := "DescribeDomainRecords"
response = &DescribeDomainRecordsResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,36 @@
package dns
import "github.com/denverdino/aliyungo/common"
type DescribeDomainRecordsNewArgs struct {
DomainName string
//optional
common.Pagination
RRKeyWord string
TypeKeyWord string
ValueKeyWord string
}
type DescribeDomainRecordsNewResponse struct {
common.Response
common.PaginationResult
InstanceId string
DomainRecords struct {
Record []RecordTypeNew
}
}
// DescribeDomainRecordsNew
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DescribeDomainRecords
func (client *Client) DescribeDomainRecordsNew(args *DescribeDomainRecordsNewArgs) (response *DescribeDomainRecordsNewResponse, err error) {
action := "DescribeDomainRecords"
response = &DescribeDomainRecordsNewResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,21 @@
package dns
import (
"testing"
)
func TestDescribeDomainRecordsNew(t *testing.T) {
//prepare
client := NewTestClientNew()
describeArgs := DescribeDomainRecordsNewArgs{
DomainName: TestDomainName,
}
describeArgs.PageSize = 100
describeResponse, err := client.DescribeDomainRecordsNew(&describeArgs)
if err == nil {
t.Logf("DescribeDomainRecords success: TotalCount:%d ", describeResponse.TotalCount)
} else {
t.Errorf("Failed to DescribeDomainRecords: %s", describeArgs.DomainName)
}
}

View File

@ -0,0 +1,21 @@
package dns
import (
"testing"
)
func TestDescribeDomainRecords(t *testing.T) {
//prepare
client := NewTestClient()
describeArgs := DescribeDomainRecordsArgs{
DomainName: TestDomainName,
}
describeArgs.PageSize = 100
describeResponse, err := client.DescribeDomainRecords(&describeArgs)
if err == nil {
t.Logf("DescribeDomainRecords success: TotalCount:%d ", describeResponse.TotalCount)
} else {
t.Errorf("Failed to DescribeDomainRecords: %s", describeArgs.DomainName)
}
}

View File

@ -0,0 +1,38 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type DescribeDomainsArgs struct {
// optional
common.Pagination
KeyWord string
GroupId string
}
type DescribeDomainsResponse struct {
response common.Response
common.PaginationResult
Domains struct {
Domain []DomainType
}
}
// DescribeDomains
//
// You can read doc at https://help.aliyun.com/document_detail/29751.html?spm=5176.doc29750.6.594.dvyRJV
func (client *Client) DescribeDomains(args *DescribeDomainsArgs) (domains []DomainType, err error) {
action := "DescribeDomains"
response := &DescribeDomainsResponse{}
err = client.Invoke(action, args, response)
if err != nil {
log.Printf("%s error, %v", action, err)
return nil, err
}
return response.Domains.Domain, err
}

View File

@ -0,0 +1,17 @@
package dns
import "testing"
func TestDescribeDomains(t *testing.T) {
client := NewTestClientNew()
describeArgs := DescribeDomainInfoArgs{
DomainName: TestDomainName,
}
_, err := client.DescribeDomainInfo(&describeArgs)
if err == nil {
t.Logf("DescribeDomains success")
} else {
t.Errorf("Failed to DescribeDomains: %v", err)
}
}

View File

@ -0,0 +1,37 @@
package dns
import "github.com/denverdino/aliyungo/common"
type DescribeSubDomainRecordsArgs struct {
SubDomain string
//optional
PageNumber int32
PageSize int32
Type string
}
type DescribeSubDomainRecordsResponse struct {
common.Response
InstanceId string
TotalCount int32
PageNumber int32
PageSize int32
DomainRecords struct {
Record []RecordType
}
}
// DescribeSubDomainRecords
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&DescribeSubDomainRecords
func (client *Client) DescribeSubDomainRecords(args *DescribeSubDomainRecordsArgs) (response *DescribeSubDomainRecordsResponse, err error) {
action := "DescribeSubDomainRecords"
response = &DescribeSubDomainRecordsResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,20 @@
package dns
import (
"testing"
)
func TestDescribeSubDomainRecords(t *testing.T) {
//prepare
client := NewTestClient()
describeArgs := DescribeSubDomainRecordsArgs{
SubDomain: "go." + TestDomainName,
}
describeResponse, err := client.DescribeSubDomainRecords(&describeArgs)
if err == nil {
t.Logf("DescribeSubDomainRecords success: %v ", describeResponse)
} else {
t.Errorf("Failed to DescribeSubDomainRecords: %s", describeArgs.SubDomain)
}
}

View File

@ -0,0 +1,29 @@
package dns
import "github.com/denverdino/aliyungo/common"
type GetMainDomainNameArgs struct {
InputString string
}
type GetMainDomainNameResponse struct {
common.Response
InstanceId string
DomainName string
RR string
DomainLevel int32
}
// GetMainDomainName
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/domain-related&GetMainDomainName
func (client *Client) GetMainDomainName(args *GetMainDomainNameArgs) (response *GetMainDomainNameResponse, err error) {
action := "GetMainDomainName"
response = &GetMainDomainNameResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,20 @@
package dns
import (
"testing"
)
func TestGetMainDomainName(t *testing.T) {
//prepare
client := NewTestClient()
args := GetMainDomainNameArgs{
InputString: "go." + TestDomainName,
}
resp, err := client.GetMainDomainName(&args)
if err == nil {
t.Logf("GetMainDomainName success: %v ", resp)
} else {
t.Errorf("Failed to GetMainDomainName: %s", args.InputString)
}
}

View File

@ -0,0 +1,33 @@
package dns
import (
"log"
"github.com/denverdino/aliyungo/common"
)
type UpdateDomainGroupArgs struct {
GroupId string
GroupName string
}
type UpdateDomainGroupResponse struct {
common.Response
GroupId string
GroupName string
}
// UpdateDomainGroup
//
// You can read doc at https://help.aliyun.com/document_detail/29763.html?spm=5176.doc29762.6.605.iFRKjn
func (client *Client) UpdateDomainGroup(args *UpdateDomainGroupArgs) (response *UpdateDomainGroupResponse, err error) {
action := "UpdateDomainGroup"
response = &UpdateDomainGroupResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
log.Printf("%s error, %v", action, err)
return response, err
}
}

View File

@ -0,0 +1,23 @@
package dns
import "testing"
func TestUpdateDomainGroup(t *testing.T) {
client := NewTestClientNew()
addGroupArgs := AddDomainGroupArgs{
GroupName: TestDomainGroupName,
}
addGroupRes, _ := client.AddDomainGroup(&addGroupArgs)
updateArgs := UpdateDomainGroupArgs{
GroupId: addGroupRes.GroupId,
GroupName: TestChanegGroupName,
}
_, err := client.UpdateDomainGroup(&updateArgs)
if err == nil {
t.Logf("UpdateDomainGroup success")
} else {
t.Errorf("Failed to UpdateDomainGroup, %v", err)
}
}

View File

@ -0,0 +1,35 @@
package dns
import "github.com/denverdino/aliyungo/common"
type UpdateDomainRecordArgs struct {
RecordId string
RR string
Type string
Value string
//optional
TTL int32
Priority int32
Line string
}
type UpdateDomainRecordResponse struct {
common.Response
InstanceId string
RecordId string
}
// UpdateDomainRecord
//
// You can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/record-related&UpdateDomainRecord
func (client *Client) UpdateDomainRecord(args *UpdateDomainRecordArgs) (response *UpdateDomainRecordResponse, err error) {
action := "UpdateDomainRecord"
response = &UpdateDomainRecordResponse{}
err = client.Invoke(action, args, response)
if err == nil {
return response, nil
} else {
return nil, err
}
}

View File

@ -0,0 +1,43 @@
package dns
import (
"testing"
)
func TestUpdateDomainRecord(t *testing.T) {
//prepare
client := NewTestClient()
addDomainRecordArgs := AddDomainRecordArgs{
DomainName: TestDomainName,
RR: "testupdaterecordid",
Value: "8.8.8.8",
Type: ARecord,
}
addResponse, err := client.AddDomainRecord(&addDomainRecordArgs)
// test update record
updateArgs := UpdateDomainRecordArgs{
RecordId: addResponse.RecordId,
RR: addDomainRecordArgs.RR,
Value: "4.4.4.4",
Type: ARecord,
}
_, err = client.UpdateDomainRecord(&updateArgs)
if err == nil {
t.Logf("UpdateDomainRecord success: RR:%s Value:%s", updateArgs.RR, updateArgs.Value)
} else {
t.Errorf("Failed to UpdateDomainRecord: %s", updateArgs.RecordId)
}
//clearup
deleteDomainRecordArgs := DeleteDomainRecordArgs{
RecordId: addResponse.RecordId,
}
_, err = client.DeleteDomainRecord(&deleteDomainRecordArgs)
if err != nil {
t.Errorf("Failed to DeleteDomainRecord: %s", deleteDomainRecordArgs.RecordId)
}
}

View File

@ -0,0 +1,50 @@
package dns
import (
"os"
"github.com/denverdino/aliyungo/common"
)
type Client struct {
common.Client
}
const (
// DNSDefaultEndpoint is the default API endpoint of DNS services
DNSDefaultEndpoint = "http://dns.aliyuncs.com"
DNSAPIVersion = "2015-01-09"
DNSDefaultEndpointNew = "http://alidns.aliyuncs.com"
)
// NewClient creates a new instance of DNS client
func NewClient(accessKeyId, accessKeySecret string) *Client {
endpoint := os.Getenv("DNS_ENDPOINT")
if endpoint == "" {
endpoint = DNSDefaultEndpoint
}
return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
}
// NewClientNew creates a new instance of DNS client, with http://alidns.aliyuncs.com as default endpoint
func NewClientNew(accessKeyId, accessKeySecret string) *Client {
endpoint := os.Getenv("DNS_ENDPOINT")
if endpoint == "" {
endpoint = DNSDefaultEndpointNew
}
return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
}
// NewCustomClient creates a new instance of ECS client with customized API endpoint
func NewCustomClient(accessKeyId, accessKeySecret string, endpoint string) *Client {
client := &Client{}
client.Init(endpoint, DNSAPIVersion, accessKeyId, accessKeySecret)
return client
}
func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client {
client := &Client{}
client.Init(endpoint, DNSAPIVersion, accessKeyId, accessKeySecret)
return client
}

View File

@ -0,0 +1,37 @@
package dns
//Modify with your Access Key Id and Access Key Secret
const (
TestAccessKeyId = "MY_ACCESS_KEY_ID"
TestAccessKeySecret = "MY_ACCESS_KEY_SECRET"
TestDomainName = "aisafe.win"
TestDomainGroupName = "testgroup"
TestChanegGroupName = "testchangegroup"
)
var testClient *Client
func NewTestClient() *Client {
if testClient == nil {
testClient = NewClient(TestAccessKeyId, TestAccessKeySecret)
}
return testClient
}
// change DNSDefaultEndpoint to "http://alidns.aliyuncs.com"
func NewTestClientNew() *Client {
if testClient == nil {
testClient = NewClientNew(TestAccessKeyId, TestAccessKeySecret)
}
return testClient
}
var testDebugClient *Client
func NewTestClientForDebug() *Client {
if testDebugClient == nil {
testDebugClient = NewClient(TestAccessKeyId, TestAccessKeySecret)
testDebugClient.SetDebug(true)
}
return testDebugClient
}

View File

@ -0,0 +1,28 @@
package dns
//
//you can read doc at https://docs.aliyun.com/#/pub/dns/api-reference/enum-type&record-format
const (
ARecord = "A"
NSRecord = "NS"
MXRecord = "MX"
TXTRecord = "TXT"
CNAMERecord = "CNAME"
SRVRecord = "SRV"
AAAARecord = "AAAA"
RedirectURLRecord = "REDIRECT_URL"
ForwordURLRecord = "FORWORD_URL"
)
type RecordType struct {
DomainName string
RecordId string
RR string
Type string
Value string
TTL int32
Priority int32
Line string
Status string
Locked bool
}

59
ddns-aliyun/build-release.sh Executable file
View File

@ -0,0 +1,59 @@
#!/bin/bash
name='aliddns'
MD5='md5sum'
if [[ "$(uname)" == 'Darwin' ]]; then
MD5='md5'
fi
# UPX=false
# if hash upx 2>/dev/null; then
# UPX=true
# fi
VERSION=$(curl -sSL https://api.github.com/repos/honwen/aliyun-ddns-cli/commits/master | sed -n '{/sha/p; /date/p;}'| sed 's/.* \"//g' | cut -c1-10 | tr '[:lower:]' '[:upper:]' | sed 'N;s/\n/@/g' | head -1)
LDFLAGS="-X main.version=$VERSION -s -w"
# X86
OSES=(windows linux darwin freebsd)
# ARCHS=(amd64 386)
ARCHS=(amd64)
rm -rf ./release
mkdir -p ./release
for os in ${OSES[@]}; do
for arch in ${ARCHS[@]}; do
suffix=""
if [ "$os" == "windows" ]; then
suffix=".exe"
fi
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o ./release/${name}_${os}_${arch}${suffix} .
# if $UPX; then upx -9 ./release/${name}_${os}_${arch}${suffix} -o ./release/${name}_${os}_${arch}${suffix}_upx; fi
tar -C ./release -zcf ./release/${name}_${os}-${arch}-$VERSION.tar.gz ./${name}_${os}_${arch}${suffix}
$MD5 ./release/${name}_${os}-${arch}-$VERSION.tar.gz
done
done
# ARM64
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -o ./release/${name}_arm64 .
# ARM
ARMS=(5 6 7)
for v in ${ARMS[@]}; do
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o ./release/${name}_arm$v .
done
# if $UPX; then upx -9 ./release/${name}_arm*; fi
tar -C ./release -zcf ./release/${name}_arm-$VERSION.tar.gz $(for v in ${ARMS[@]}; do echo -n "./${name}_arm$v ";done)
$MD5 ./release/${name}_arm-$VERSION.tar.gz
# MIPS/hardfloat
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$LDFLAGS" -o ./release/${name}_mipsle .
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$LDFLAGS" -o ./release/${name}_mips .
# MIPS/softfloat
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -o ./release/${name}_mipsle_sf .
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$LDFLAGS" -o ./release/${name}_mips_sf .
# if $UPX; then upx -9 ./release/${name}_mips**; fi
tar -C ./release -zcf ./release/${name}_mipsle-$VERSION.tar.gz ./${name}_mipsle
tar -C ./release -zcf ./release/${name}_mips-$VERSION.tar.gz ./${name}_mips
tar -C ./release -zcf ./release/${name}_mipsle-sf-$VERSION.tar.gz ./${name}_mipsle_sf
tar -C ./release -zcf ./release/${name}_mips-sf-$VERSION.tar.gz ./${name}_mips_sf
$MD5 ./release/${name}_mipsle-$VERSION.tar.gz

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

11
ddns-aliyun/go.mod Normal file
View File

@ -0,0 +1,11 @@
module github.com/honwen/aliyun-ddns-cli
go 1.16
require (
github.com/denverdino/aliyungo v0.0.0-20210518071019-eb3bbb144d8a
github.com/honwen/golibs v0.1.4
github.com/honwen/ip2loc v0.1.2
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5
)

102
ddns-aliyun/go.sum Normal file
View File

@ -0,0 +1,102 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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-20210518071019-eb3bbb144d8a h1:J9+NI0ywi1btTOYseLdmr/H3XxutbC4m2bU48kKpwVs=
github.com/denverdino/aliyungo v0.0.0-20210518071019-eb3bbb144d8a/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/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.2 h1:YgIK6A6iBynOlh2tzYRKl8mGmFoHcPPwws5Q0Ar4lwM=
github.com/honwen/ip2loc v0.1.2/go.mod h1:4loWbEvIxSNNr6pDp2YfPnAnJN/SKN7LiHfFr0rHB9o=
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.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=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
github.com/rhnvrm/simples3 v0.5.0/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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-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=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

400
ddns-aliyun/main.go Normal file
View File

@ -0,0 +1,400 @@
package main
import (
"errors"
"fmt"
"log"
"math/rand"
"os"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"time"
"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"
)
// AccessKey from https://ak-console.aliyun.com/#/accesskey
type AccessKey struct {
ID string
Secret string
client *dns.Client
}
func (ak *AccessKey) getClient() *dns.Client {
if len(ak.ID) <= 0 && len(ak.Secret) <= 0 {
return nil
}
if ak.client == nil {
ak.client = dns.NewClient(ak.ID, ak.Secret)
ak.client.SetEndpoint(dns.DNSDefaultEndpointNew)
}
return ak.client
}
func (ak AccessKey) String() string {
return fmt.Sprintf("Access Key: [ ID: %s ;\t Secret: %s ]", ak.ID, ak.Secret)
}
func (ak *AccessKey) ListRecord(domain string) (dnsRecords []dns.RecordTypeNew, err error) {
var resp *dns.DescribeDomainRecordsNewResponse
for idx := 1; idx <= 99; idx++ {
resp, err = ak.getClient().DescribeDomainRecordsNew(
&dns.DescribeDomainRecordsNewArgs{
DomainName: domain,
Pagination: common.Pagination{PageNumber: idx, PageSize: 50},
})
if err != nil {
return
}
dnsRecords = append(dnsRecords, resp.DomainRecords.Record...)
if len(dnsRecords) >= resp.PaginationResult.TotalCount {
return
}
}
return
}
func (ak *AccessKey) DelRecord(rr, domain string) (err error) {
var target *dns.RecordTypeNew
if dnsRecords, err := ak.ListRecord(domain); err == nil {
for i := range dnsRecords {
if dnsRecords[i].RR == rr {
target = &dnsRecords[i]
_, err = ak.getClient().DeleteDomainRecord(
&dns.DeleteDomainRecordArgs{
RecordId: target.RecordId,
},
)
}
}
} else {
return err
}
return
}
func (ak *AccessKey) UpdateRecord(recordID, rr, dmType, value string) (err error) {
_, err = ak.getClient().UpdateDomainRecord(
&dns.UpdateDomainRecordArgs{
RecordId: recordID,
RR: rr,
Value: value,
Type: dmType,
})
return
}
func (ak *AccessKey) AddRecord(domain, rr, dmType, value string) (err error) {
_, err = ak.getClient().AddDomainRecord(
&dns.AddDomainRecordArgs{
DomainName: domain,
RR: rr,
Type: dmType,
Value: value,
})
return err
}
func (ak *AccessKey) CheckAndUpdateRecord(rr, domain, ipaddr, recordType string) (err error) {
fulldomain := strings.Join([]string{rr, domain}, `.`)
if reslove(fulldomain) == ipaddr {
return // Skip
}
targetCnt := 0
var target *dns.RecordTypeNew
if dnsRecords, err := ak.ListRecord(domain); err == nil {
for i := range dnsRecords {
if dnsRecords[i].RR == rr && dnsRecords[i].Type == recordType {
target = &dnsRecords[i]
targetCnt++
}
}
} else {
return err
}
if targetCnt > 1 {
ak.DelRecord(rr, domain)
target = nil
}
if target == nil {
err = ak.AddRecord(domain, rr, recordType, ipaddr)
} 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)
}
if err != nil && strings.Contains(err.Error(), `DomainRecordDuplicate`) {
ak.DelRecord(rr, domain)
return ak.CheckAndUpdateRecord(rr, domain, ipaddr, recordType)
}
return err
}
var (
accessKey AccessKey
VersionString = "MISSING build version [git hash]"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func main() {
app := cli.NewApp()
app.Name = "aliddns"
app.Usage = "aliyun-ddns-cli"
app.Version = fmt.Sprintf("Git:[%s] (%s)", strings.ToUpper(VersionString), runtime.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: "Specific `DomainName`. like aliyun.com",
},
},
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"))
_, domain := domain.SplitDomainToRR(c.String("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)
}
}
return nil
},
},
{
Name: "delete",
Category: "DDNS",
Usage: "Delete AliYun's DNS DomainRecords Record",
Flags: []cli.Flag{
cli.StringFlag{
Name: "domain, d",
Usage: "Specific `FullDomainName`. like ddns.aliyun.com",
},
},
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"))
if err := accessKey.DelRecord(domain.SplitDomainToRR(c.String("domain"))); err != nil {
fmt.Printf("%+v", err)
} else {
fmt.Println(c.String("domain"), "Deleted")
}
return nil
},
},
{
Name: "update",
Category: "DDNS",
Usage: "Update AliYun's DNS DomainRecords Record, Create Record if not exist",
Flags: []cli.Flag{
cli.StringFlag{
Name: "domain, d",
Usage: "Specific `DomainName`. like ddns.aliyun.com",
},
cli.StringFlag{
Name: "ipaddr, i",
Usage: "Specific `IP`. like 1.2.3.4",
},
},
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"))
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")))
}
return nil
},
},
{
Name: "auto-update",
Category: "DDNS",
Usage: "Auto-Update AliYun's DNS DomainRecords Record, Get IP using its getip",
Flags: []cli.Flag{
cli.StringFlag{
Name: "domain, d",
Usage: "Specific `DomainName`. like ddns.aliyun.com",
},
cli.StringFlag{
Name: "redo, r",
Value: "",
Usage: "redo Auto-Update, every N `Seconds`; Disable if N less than 10; End with [Rr] enable random delay: [N, 2N]",
},
},
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"))
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]?$`)
}
randomDelay := regexp.MustCompile(`\d+[Rr]$`).MatchString(redoDurtionStr)
redoDurtion := 0
if randomDelay {
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 := myip()
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 {
log.Printf("# Err-CheckAndUpdateRecord: [%+v]", err)
} else {
log.Println(c.String("domain"), autoip, ip2locCN(autoip))
}
}
if redoDurtion < 10 {
break // Disable if N less than 10
}
if randomDelay {
time.Sleep(time.Duration(redoDurtion+rand.Intn(redoDurtion)) * time.Second)
} else {
time.Sleep(time.Duration(redoDurtion) * time.Second)
}
}
return nil
},
},
{
Name: "getip",
Category: "GET-IP",
Usage: fmt.Sprintf(" Get IP Combine 10+ different Web-API"),
Action: func(c *cli.Context) error {
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 4+ DNS Upstream"),
Flags: []cli.Flag{
cli.StringFlag{
Name: "domain, d",
Usage: "Specific `DomainName`. like ddns.aliyun.com",
},
},
Action: func(c *cli.Context) error {
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
},
},
}
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",
},
cli.StringSliceFlag{
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, true)
}
app.Run(os.Args)
}
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 checkAccessKey && accessKey.getClient() == nil {
cli.ShowAppHelp(c)
return errors.New("access-key is empty")
}
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) {
ipapi = append(ipapi, api)
}
}
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
}

68
ddns-aliyun/utils.go Normal file
View File

@ -0,0 +1,68 @@
package main
import (
"errors"
"fmt"
"log"
"reflect"
"strings"
"github.com/honwen/golibs/cip"
"github.com/honwen/ip2loc"
)
var funcs = map[string]interface{}{
"myip": cip.MyIPv4,
"reslove": cip.ResloveIPv4,
}
func ip2locCN(ip string) (str string) {
if strings.Count(ip, `.`) < 3 {
return
}
if loc, err := ip2loc.IP2loc(ip); err != nil {
log.Printf("%+v", err)
} else {
str = fmt.Sprintf("[%s %s %s %s]", loc.CountryName, loc.RegionName, loc.CityName, loc.IspDomain)
for strings.Contains(str, " ]") {
str = strings.ReplaceAll(str, " ]", "]")
}
for strings.Contains(str, " ") {
str = strings.ReplaceAll(str, " ", " ")
}
}
return
}
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
}
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
result = f.Call(in)
return
}
func myip() (ip string) {
if result, err := Call(funcs, "myip"); err == nil {
for _, r := range result {
return r.String()
}
}
return
}
func reslove(domain string) (ip string) {
if result, err := Call(funcs, "reslove", domain); err == nil {
for _, r := range result {
return r.String()
}
}
return
}

111
ddns-aliyun/utils_test.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"regexp"
"testing"
"github.com/honwen/golibs/cip"
"github.com/honwen/golibs/domain"
"github.com/stretchr/testify/assert"
)
func TestIp2locCN(t *testing.T) {
assert.Equal(t, ip2locCN("202.96.128.86"), "[中国 广东 广州 电信]")
assert.Equal(t, ip2locCN("202.96.209.133"), "[中国 上海 上海 电信]")
assert.Equal(t, ip2locCN("219.141.136.10"), "[中国 北京 北京 电信]")
assert.Equal(t, ip2locCN("210.22.70.3"), "[中国 上海 上海 联通]")
assert.Equal(t, ip2locCN("123.123.123.123"), "[中国 北京 北京 联通]")
assert.Equal(t, ip2locCN("223.87.238.22"), "[中国 四川 成都 移动]")
assert.Equal(t, ip2locCN("101.6.6.6"), "[中国 北京 北京 教育网]")
assert.Equal(t, ip2locCN("168.95.1.1"), "[中国 台湾 中华电信]")
assert.Equal(t, ip2locCN("202.67.240.222"), "[中国 香港]")
assert.Equal(t, ip2locCN("203.189.136.148"), "[柬埔寨 柬埔寨]")
assert.Equal(t, ip2locCN("203.112.2.4"), "[日本 日本]")
assert.Equal(t, ip2locCN("80.80.80.80"), "[荷兰 荷兰]")
assert.Equal(t, ip2locCN("74.82.42.42"), "[美国 加利福尼亚州]")
}
func TestGetIPv4(t *testing.T) {
funcs["myip"] = cip.MyIPv4
ip4 := myip()
assert.True(t, regexp.MustCompile(cip.RegxIPv4).MatchString(ip4) || len(ip4) == 0)
}
func TestGetIPv6(t *testing.T) {
funcs["myip"] = cip.MyIPv6
ip6 := myip()
assert.True(t, regexp.MustCompile(cip.RegxIPv6).MatchString(ip6) || len(ip6) == 0)
}
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 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 TestSplitDomain001(t *testing.T) {
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 := domain.SplitDomainToRR("example.com")
assert.Equal(t, rr, "@")
assert.Equal(t, domain, "example.com")
}
func TestSplitDomain003(t *testing.T) {
rr, domain := domain.SplitDomainToRR("*.example.com")
assert.Equal(t, rr, "*")
assert.Equal(t, domain, "example.com")
}
func TestSplitDomain004(t *testing.T) {
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 := 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 := 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 := 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 := domain.SplitDomainToRR("example.co.kr")
assert.Equal(t, rr, "@")
assert.Equal(t, domain, "example.co.kr")
}