diff --git a/.github/workflows/ddns-aliyun.yml b/.github/workflows/ddns-aliyun.yml new file mode 100644 index 0000000..301121c --- /dev/null +++ b/.github/workflows/ddns-aliyun.yml @@ -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} diff --git a/ddns-aliyun/.github/workflows/build.yaml b/ddns-aliyun/.github/workflows/build.yaml new file mode 100644 index 0000000..3737a67 --- /dev/null +++ b/ddns-aliyun/.github/workflows/build.yaml @@ -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 diff --git a/ddns-aliyun/.gitignore b/ddns-aliyun/.gitignore new file mode 100644 index 0000000..2e5afd5 --- /dev/null +++ b/ddns-aliyun/.gitignore @@ -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* diff --git a/ddns-aliyun/Dockerfile b/ddns-aliyun/Dockerfile new file mode 100644 index 0000000..d92946b --- /dev/null +++ b/ddns-aliyun/Dockerfile @@ -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 + +# /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} diff --git a/ddns-aliyun/LICENSE b/ddns-aliyun/LICENSE new file mode 100644 index 0000000..346ea2f --- /dev/null +++ b/ddns-aliyun/LICENSE @@ -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. diff --git a/ddns-aliyun/Makefile b/ddns-aliyun/Makefile new file mode 100644 index 0000000..0269e51 --- /dev/null +++ b/ddns-aliyun/Makefile @@ -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 diff --git a/ddns-aliyun/README.md b/ddns-aliyun/README.md new file mode 100644 index 0000000..aa2d9cb --- /dev/null +++ b/ddns-aliyun/README.md @@ -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) + diff --git a/ddns-aliyun/alidns/AddDomain.go b/ddns-aliyun/alidns/AddDomain.go new file mode 100644 index 0000000..2383865 --- /dev/null +++ b/ddns-aliyun/alidns/AddDomain.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/AddDomainGroup.go b/ddns-aliyun/alidns/AddDomainGroup.go new file mode 100644 index 0000000..4537350 --- /dev/null +++ b/ddns-aliyun/alidns/AddDomainGroup.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/AddDomainGroup_test.go b/ddns-aliyun/alidns/AddDomainGroup_test.go new file mode 100644 index 0000000..b6ee131 --- /dev/null +++ b/ddns-aliyun/alidns/AddDomainGroup_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/AddDomainRecord.go b/ddns-aliyun/alidns/AddDomainRecord.go new file mode 100644 index 0000000..caed84b --- /dev/null +++ b/ddns-aliyun/alidns/AddDomainRecord.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/AddDomainRecord_test.go b/ddns-aliyun/alidns/AddDomainRecord_test.go new file mode 100644 index 0000000..e8e6c5b --- /dev/null +++ b/ddns-aliyun/alidns/AddDomainRecord_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/AddDomain_test.go b/ddns-aliyun/alidns/AddDomain_test.go new file mode 100644 index 0000000..70b2a0f --- /dev/null +++ b/ddns-aliyun/alidns/AddDomain_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/ChangeDomainGroup.go b/ddns-aliyun/alidns/ChangeDomainGroup.go new file mode 100644 index 0000000..a7aea26 --- /dev/null +++ b/ddns-aliyun/alidns/ChangeDomainGroup.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/ChangeDomainGroup_test.go b/ddns-aliyun/alidns/ChangeDomainGroup_test.go new file mode 100644 index 0000000..05d316f --- /dev/null +++ b/ddns-aliyun/alidns/ChangeDomainGroup_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DeleteDomain.go b/ddns-aliyun/alidns/DeleteDomain.go new file mode 100644 index 0000000..03d9d58 --- /dev/null +++ b/ddns-aliyun/alidns/DeleteDomain.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DeleteDomainGroup.go b/ddns-aliyun/alidns/DeleteDomainGroup.go new file mode 100644 index 0000000..be593e1 --- /dev/null +++ b/ddns-aliyun/alidns/DeleteDomainGroup.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DeleteDomainGroup_test.go b/ddns-aliyun/alidns/DeleteDomainGroup_test.go new file mode 100644 index 0000000..f8ecb63 --- /dev/null +++ b/ddns-aliyun/alidns/DeleteDomainGroup_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DeleteDomainRecord.go b/ddns-aliyun/alidns/DeleteDomainRecord.go new file mode 100644 index 0000000..a80acdb --- /dev/null +++ b/ddns-aliyun/alidns/DeleteDomainRecord.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DeleteDomainRecord_test.go b/ddns-aliyun/alidns/DeleteDomainRecord_test.go new file mode 100644 index 0000000..e6d2e3f --- /dev/null +++ b/ddns-aliyun/alidns/DeleteDomainRecord_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DeleteDomain_test.go b/ddns-aliyun/alidns/DeleteDomain_test.go new file mode 100644 index 0000000..5c99f6d --- /dev/null +++ b/ddns-aliyun/alidns/DeleteDomain_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DeleteSubDomainRecords.go b/ddns-aliyun/alidns/DeleteSubDomainRecords.go new file mode 100644 index 0000000..4ff4c05 --- /dev/null +++ b/ddns-aliyun/alidns/DeleteSubDomainRecords.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DeleteSubDomainRecords_test.go b/ddns-aliyun/alidns/DeleteSubDomainRecords_test.go new file mode 100644 index 0000000..d21eacf --- /dev/null +++ b/ddns-aliyun/alidns/DeleteSubDomainRecords_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainGroups.go b/ddns-aliyun/alidns/DescribeDomainGroups.go new file mode 100644 index 0000000..797b354 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainGroups.go @@ -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 +} diff --git a/ddns-aliyun/alidns/DescribeDomainGroups_test.go b/ddns-aliyun/alidns/DescribeDomainGroups_test.go new file mode 100644 index 0000000..3e06a62 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainGroups_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainInfo.go b/ddns-aliyun/alidns/DescribeDomainInfo.go new file mode 100644 index 0000000..b88cd91 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainInfo.go @@ -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 +} diff --git a/ddns-aliyun/alidns/DescribeDomainInfo_test.go b/ddns-aliyun/alidns/DescribeDomainInfo_test.go new file mode 100644 index 0000000..86fe175 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainInfo_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecordInfo.go b/ddns-aliyun/alidns/DescribeDomainRecordInfo.go new file mode 100644 index 0000000..76c042e --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecordInfo.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecordInfoNew.go b/ddns-aliyun/alidns/DescribeDomainRecordInfoNew.go new file mode 100644 index 0000000..8eed446 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecordInfoNew.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecordInfoNew_test.go b/ddns-aliyun/alidns/DescribeDomainRecordInfoNew_test.go new file mode 100644 index 0000000..3d26ea2 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecordInfoNew_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecordInfo_test.go b/ddns-aliyun/alidns/DescribeDomainRecordInfo_test.go new file mode 100644 index 0000000..4741fee --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecordInfo_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecords.go b/ddns-aliyun/alidns/DescribeDomainRecords.go new file mode 100644 index 0000000..81c97db --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecords.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecordsNew.go b/ddns-aliyun/alidns/DescribeDomainRecordsNew.go new file mode 100644 index 0000000..f2f04d3 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecordsNew.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecordsNew_test.go b/ddns-aliyun/alidns/DescribeDomainRecordsNew_test.go new file mode 100644 index 0000000..96818a0 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecordsNew_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomainRecords_test.go b/ddns-aliyun/alidns/DescribeDomainRecords_test.go new file mode 100644 index 0000000..45e5a34 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomainRecords_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeDomains.go b/ddns-aliyun/alidns/DescribeDomains.go new file mode 100644 index 0000000..41e1232 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomains.go @@ -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 +} diff --git a/ddns-aliyun/alidns/DescribeDomains_test.go b/ddns-aliyun/alidns/DescribeDomains_test.go new file mode 100644 index 0000000..7a8a373 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeDomains_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/DescribeSubDomainRecords.go b/ddns-aliyun/alidns/DescribeSubDomainRecords.go new file mode 100644 index 0000000..86a8e4d --- /dev/null +++ b/ddns-aliyun/alidns/DescribeSubDomainRecords.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/DescribeSubDomainRecords_test.go b/ddns-aliyun/alidns/DescribeSubDomainRecords_test.go new file mode 100644 index 0000000..c4cbf27 --- /dev/null +++ b/ddns-aliyun/alidns/DescribeSubDomainRecords_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/GetMainDomainName.go b/ddns-aliyun/alidns/GetMainDomainName.go new file mode 100644 index 0000000..5733c84 --- /dev/null +++ b/ddns-aliyun/alidns/GetMainDomainName.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/GetMainDomainName_test.go b/ddns-aliyun/alidns/GetMainDomainName_test.go new file mode 100644 index 0000000..f35ba70 --- /dev/null +++ b/ddns-aliyun/alidns/GetMainDomainName_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/UpdateDomainGroup.go b/ddns-aliyun/alidns/UpdateDomainGroup.go new file mode 100644 index 0000000..2f76ebe --- /dev/null +++ b/ddns-aliyun/alidns/UpdateDomainGroup.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/UpdateDomainGroup_test.go b/ddns-aliyun/alidns/UpdateDomainGroup_test.go new file mode 100644 index 0000000..85ded4c --- /dev/null +++ b/ddns-aliyun/alidns/UpdateDomainGroup_test.go @@ -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) + } +} diff --git a/ddns-aliyun/alidns/UpdateDomainRecord.go b/ddns-aliyun/alidns/UpdateDomainRecord.go new file mode 100644 index 0000000..ba32173 --- /dev/null +++ b/ddns-aliyun/alidns/UpdateDomainRecord.go @@ -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 + } +} diff --git a/ddns-aliyun/alidns/UpdateDomainRecord_test.go b/ddns-aliyun/alidns/UpdateDomainRecord_test.go new file mode 100644 index 0000000..59f4ff9 --- /dev/null +++ b/ddns-aliyun/alidns/UpdateDomainRecord_test.go @@ -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) + } + +} diff --git a/ddns-aliyun/alidns/client.go b/ddns-aliyun/alidns/client.go new file mode 100644 index 0000000..4d655d4 --- /dev/null +++ b/ddns-aliyun/alidns/client.go @@ -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 +} diff --git a/ddns-aliyun/alidns/config_test.go b/ddns-aliyun/alidns/config_test.go new file mode 100644 index 0000000..117c255 --- /dev/null +++ b/ddns-aliyun/alidns/config_test.go @@ -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 +} diff --git a/ddns-aliyun/alidns/record.go b/ddns-aliyun/alidns/record.go new file mode 100644 index 0000000..5e306f7 --- /dev/null +++ b/ddns-aliyun/alidns/record.go @@ -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 +} diff --git a/ddns-aliyun/build-release.sh b/ddns-aliyun/build-release.sh new file mode 100755 index 0000000..ba46633 --- /dev/null +++ b/ddns-aliyun/build-release.sh @@ -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 diff --git a/ddns-aliyun/example/Synology_Docker.png b/ddns-aliyun/example/Synology_Docker.png new file mode 100644 index 0000000..72b849d Binary files /dev/null and b/ddns-aliyun/example/Synology_Docker.png differ diff --git a/ddns-aliyun/go.mod b/ddns-aliyun/go.mod new file mode 100644 index 0000000..a6c4e6a --- /dev/null +++ b/ddns-aliyun/go.mod @@ -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 +) diff --git a/ddns-aliyun/go.sum b/ddns-aliyun/go.sum new file mode 100644 index 0000000..a175d6b --- /dev/null +++ b/ddns-aliyun/go.sum @@ -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= diff --git a/ddns-aliyun/main.go b/ddns-aliyun/main.go new file mode 100644 index 0000000..bb1954e --- /dev/null +++ b/ddns-aliyun/main.go @@ -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 +} diff --git a/ddns-aliyun/utils.go b/ddns-aliyun/utils.go new file mode 100644 index 0000000..f2b2eb9 --- /dev/null +++ b/ddns-aliyun/utils.go @@ -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 +} diff --git a/ddns-aliyun/utils_test.go b/ddns-aliyun/utils_test.go new file mode 100644 index 0000000..1e863be --- /dev/null +++ b/ddns-aliyun/utils_test.go @@ -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") +}