Bläddra i källkod

Create ddns-aliyun

Stille 4 år sedan
förälder
incheckning
f33d32c228
55 ändrade filer med 2291 tillägg och 0 borttagningar
  1. 42 0
      .github/workflows/ddns-aliyun.yml
  2. 83 0
      ddns-aliyun/.github/workflows/build.yaml
  3. 33 0
      ddns-aliyun/.gitignore
  4. 32 0
      ddns-aliyun/Dockerfile
  5. 21 0
      ddns-aliyun/LICENSE
  6. 43 0
      ddns-aliyun/Makefile
  7. 36 0
      ddns-aliyun/README.md
  8. 41 0
      ddns-aliyun/alidns/AddDomain.go
  9. 32 0
      ddns-aliyun/alidns/AddDomainGroup.go
  10. 25 0
      ddns-aliyun/alidns/AddDomainGroup_test.go
  11. 38 0
      ddns-aliyun/alidns/AddDomainRecord.go
  12. 27 0
      ddns-aliyun/alidns/AddDomainRecord_test.go
  13. 24 0
      ddns-aliyun/alidns/AddDomain_test.go
  14. 33 0
      ddns-aliyun/alidns/ChangeDomainGroup.go
  15. 36 0
      ddns-aliyun/alidns/ChangeDomainGroup_test.go
  16. 31 0
      ddns-aliyun/alidns/DeleteDomain.go
  17. 31 0
      ddns-aliyun/alidns/DeleteDomainGroup.go
  18. 23 0
      ddns-aliyun/alidns/DeleteDomainGroup_test.go
  19. 27 0
      ddns-aliyun/alidns/DeleteDomainRecord.go
  20. 29 0
      ddns-aliyun/alidns/DeleteDomainRecord_test.go
  21. 23 0
      ddns-aliyun/alidns/DeleteDomain_test.go
  22. 32 0
      ddns-aliyun/alidns/DeleteSubDomainRecords.go
  23. 30 0
      ddns-aliyun/alidns/DeleteSubDomainRecords_test.go
  24. 42 0
      ddns-aliyun/alidns/DescribeDomainGroups.go
  25. 15 0
      ddns-aliyun/alidns/DescribeDomainGroups_test.go
  26. 46 0
      ddns-aliyun/alidns/DescribeDomainInfo.go
  27. 17 0
      ddns-aliyun/alidns/DescribeDomainInfo_test.go
  28. 26 0
      ddns-aliyun/alidns/DescribeDomainRecordInfo.go
  29. 38 0
      ddns-aliyun/alidns/DescribeDomainRecordInfoNew.go
  30. 30 0
      ddns-aliyun/alidns/DescribeDomainRecordInfoNew_test.go
  31. 30 0
      ddns-aliyun/alidns/DescribeDomainRecordInfo_test.go
  32. 36 0
      ddns-aliyun/alidns/DescribeDomainRecords.go
  33. 36 0
      ddns-aliyun/alidns/DescribeDomainRecordsNew.go
  34. 21 0
      ddns-aliyun/alidns/DescribeDomainRecordsNew_test.go
  35. 21 0
      ddns-aliyun/alidns/DescribeDomainRecords_test.go
  36. 38 0
      ddns-aliyun/alidns/DescribeDomains.go
  37. 17 0
      ddns-aliyun/alidns/DescribeDomains_test.go
  38. 37 0
      ddns-aliyun/alidns/DescribeSubDomainRecords.go
  39. 20 0
      ddns-aliyun/alidns/DescribeSubDomainRecords_test.go
  40. 29 0
      ddns-aliyun/alidns/GetMainDomainName.go
  41. 20 0
      ddns-aliyun/alidns/GetMainDomainName_test.go
  42. 33 0
      ddns-aliyun/alidns/UpdateDomainGroup.go
  43. 23 0
      ddns-aliyun/alidns/UpdateDomainGroup_test.go
  44. 35 0
      ddns-aliyun/alidns/UpdateDomainRecord.go
  45. 43 0
      ddns-aliyun/alidns/UpdateDomainRecord_test.go
  46. 50 0
      ddns-aliyun/alidns/client.go
  47. 37 0
      ddns-aliyun/alidns/config_test.go
  48. 28 0
      ddns-aliyun/alidns/record.go
  49. 59 0
      ddns-aliyun/build-release.sh
  50. BIN
      ddns-aliyun/example/Synology_Docker.png
  51. 11 0
      ddns-aliyun/go.mod
  52. 102 0
      ddns-aliyun/go.sum
  53. 400 0
      ddns-aliyun/main.go
  54. 68 0
      ddns-aliyun/utils.go
  55. 111 0
      ddns-aliyun/utils_test.go

+ 42 - 0
.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}

+ 83 - 0
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

+ 33 - 0
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*

+ 32 - 0
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 <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 - 0
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.

+ 43 - 0
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

+ 36 - 0
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)
+

+ 41 - 0
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
+	}
+}

+ 32 - 0
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
+	}
+}

+ 25 - 0
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)
+	}
+}

+ 38 - 0
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
+	}
+}

+ 27 - 0
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)
+	}
+}

+ 24 - 0
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)
+	}
+}

+ 33 - 0
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
+	}
+}

+ 36 - 0
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)
+	}
+}

+ 31 - 0
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
+	}
+}

+ 31 - 0
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
+	}
+}

+ 23 - 0
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)
+	}
+}

+ 27 - 0
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
+	}
+}

+ 29 - 0
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)
+	}
+}

+ 23 - 0
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)
+	}
+}

+ 32 - 0
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
+	}
+}

+ 30 - 0
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)
+	}
+}

+ 42 - 0
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
+}

+ 15 - 0
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)
+	}
+}

+ 46 - 0
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
+}

+ 17 - 0
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)
+	}
+}

+ 26 - 0
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
+	}
+}

+ 38 - 0
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
+	}
+}

+ 30 - 0
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)
+	}
+}

+ 30 - 0
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)
+	}
+}

+ 36 - 0
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
+	}
+}

+ 36 - 0
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
+	}
+}

+ 21 - 0
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)
+	}
+}

+ 21 - 0
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)
+	}
+}

+ 38 - 0
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
+}

+ 17 - 0
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)
+	}
+}

+ 37 - 0
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
+	}
+}

+ 20 - 0
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)
+	}
+}

+ 29 - 0
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
+	}
+}

+ 20 - 0
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)
+	}
+}

+ 33 - 0
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
+	}
+}

+ 23 - 0
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)
+	}
+}

+ 35 - 0
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
+	}
+}

+ 43 - 0
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)
+	}
+
+}

+ 50 - 0
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
+}

+ 37 - 0
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
+}

+ 28 - 0
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
+}

+ 59 - 0
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

BIN
ddns-aliyun/example/Synology_Docker.png


+ 11 - 0
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
+)

+ 102 - 0
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=

+ 400 - 0
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
+}

+ 68 - 0
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
+}

+ 111 - 0
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")
+}