diff --git a/net/ddns-scripts/Makefile b/net/ddns-scripts/Makefile index 83118750c21e32..63d41b24bb041b 100644 --- a/net/ddns-scripts/Makefile +++ b/net/ddns-scripts/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ddns-scripts PKG_VERSION:=2.8.2 -PKG_RELEASE:=52 +PKG_RELEASE:=53 PKG_LICENSE:=GPL-2.0 @@ -155,6 +155,17 @@ define Package/ddns-scripts-dnspod/description endef +define Package/ddns-scripts-dnspod-v3 + $(call Package/ddns-scripts/Default) + TITLE:=Extension for dnspod.tencentcloudapi.com (aka: dnspod.cn) API v3 + DEPENDS:=ddns-scripts +curl +openssl-util +endef + +define Package/ddns-scripts-dnspod-v3/description + Dynamic DNS Client scripts extension for dnspod.tencentcloudapi.com API v3 (require curl) +endef + + define Package/ddns-scripts-noip $(call Package/ddns-scripts/Default) TITLE:=Extension for no-ip.com @@ -385,6 +396,7 @@ define Package/ddns-scripts-services/install rm $(1)/usr/share/ddns/default/godaddy.com-v1.json rm $(1)/usr/share/ddns/default/digitalocean.com-v2.json rm $(1)/usr/share/ddns/default/dnspod.cn.json + rm $(1)/usr/share/ddns/default/dnspod.cn-v3.json rm $(1)/usr/share/ddns/default/no-ip.com.json rm $(1)/usr/share/ddns/default/bind-nsupdate.json rm $(1)/usr/share/ddns/default/route53-v1.json @@ -541,6 +553,25 @@ exit 0 endef +define Package/ddns-scripts-dnspod-v3/install + $(INSTALL_DIR) $(1)/usr/lib/ddns + $(INSTALL_BIN) ./files/usr/lib/ddns/update_dnspod_cn_v3.sh \ + $(1)/usr/lib/ddns + + $(INSTALL_DIR) $(1)/usr/share/ddns/default + $(INSTALL_DATA) ./files/usr/share/ddns/default/dnspod.cn-v3.json \ + $(1)/usr/share/ddns/default/ +endef + +define Package/ddns-scripts-dnspod-v3/prerm +#!/bin/sh +if [ -z "$${IPKG_INSTROOT}" ]; then + /etc/init.d/ddns stop +fi +exit 0 +endef + + define Package/ddns-scripts-noip/install $(INSTALL_DIR) $(1)/usr/lib/ddns $(INSTALL_BIN) ./files/usr/lib/ddns/update_no-ip_com.sh \ @@ -760,6 +791,7 @@ $(eval $(call BuildPackage,ddns-scripts-freedns)) $(eval $(call BuildPackage,ddns-scripts-godaddy)) $(eval $(call BuildPackage,ddns-scripts-digitalocean)) $(eval $(call BuildPackage,ddns-scripts-dnspod)) +$(eval $(call BuildPackage,ddns-scripts-dnspod-v3)) $(eval $(call BuildPackage,ddns-scripts-noip)) $(eval $(call BuildPackage,ddns-scripts-nsupdate)) $(eval $(call BuildPackage,ddns-scripts-route53)) diff --git a/net/ddns-scripts/files/usr/lib/ddns/update_dnspod_cn_v3.sh b/net/ddns-scripts/files/usr/lib/ddns/update_dnspod_cn_v3.sh new file mode 100644 index 00000000000000..c3c77ae8ae3501 --- /dev/null +++ b/net/ddns-scripts/files/usr/lib/ddns/update_dnspod_cn_v3.sh @@ -0,0 +1,345 @@ +#!/bin/sh +# +# Script for sending updates to cloud.tencent.com, modified based on +# "update_cloudflare_com_v4.sh" and "update_dnspod_cn.sh". +# +# You can found them from: +# - github.com/openwrt/packages for "update_cloudflare_com_v4.sh" +# - at: net/ddns-scripts/files/usr/lib/ddns/update_cloudflare_com_v4.sh +# - github.com/nixonli/ddns-scripts_dnspod for "update_dnspod_cn.sh" +# +# 2024 FriesI23 +# +# API documentation at https://cloud.tencent.com/document/api/1427/84627 +# API signature documentation at https://cloud.tencent.com/document/api/1427/56189 +# +# This script is parsed by dynamic_dns_functions.sh inside send_update() function +# +# using following options from /etc/config/ddns +# you can get your own secret values from console.cloud.tencent.com/cam/capi +# option username - api secretId +# option password - api secretKey +# option domain - "hostname@yourdomain.TLD" +# option record_id - record id for special record +# +# variable __IP already defined with the ip-address to use for update +# + +local OPENSSL=$(command -v openssl) + +# check parameters +[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing key as 'username'" +[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing secret as 'password'" +[ -z "$CURL_SSL" ] && write_log 13 "Dnspod communication require cURL with SSL support. Please install" +[ -z "$OPENSSL" ] && write_log 13 "Dnspod communication require openssl. Please install" +[ $use_https -eq 0 ] && use_https=1 + +# variables +local __HOST __DOMAIN __TYPE +local __RUNPROG +local __RECID __RECLINE __DATA __IPV6 +local __URLHOST="dnspod.tencentcloudapi.com" +local __URLBASE="https://$__URLHOST" +local __METHOD="POST" +local __CONTENT_TYPE="application/json" + +# split __HOST __DOMAIN from $domain +# given data: +# @example.com for "domain record" +# host.sub@example.com for a "host record" +__HOST=$(printf %s "$domain" | cut -d@ -f1) +__DOMAIN=$(printf %s "$domain" | cut -d@ -f2) + +# set record type +[ $use_ipv6 -eq 0 ] && __TYPE="A" || __TYPE="AAAA" + +tencentcloud_transfer() { + local __CNT=0 + local __ERR __CODE + + while :; do + write_log 7 "#> $__RUNPROG" + eval "$__RUNPROG" + __ERR=$? # save communication error + + if [ $__ERR -eq 0 ]; then + if grep -q '"Error"' "$DATFILE"; then + __CODE=$(grep -o '"Code":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1) + [[ $__CODE == "ResourceNotFound.NoDataOfRecord" ]] && break + write_log 3 "cURL Response Error: '$__CODE'" + write_log 7 "$(cat $DATFILE)" # report error + else + break + fi + else + write_log 3 "cURL Error: '$__ERR'" + write_log 7 "$(cat $ERRFILE)" # report error + fi + + [ $VERBOSE -gt 1 ] && { + # VERBOSE > 1 then NO retry + write_log 4 "Transfer failed - Verbose Mode: $VERBOSE - NO retry on error" + break + } + + __CNT=$(($__CNT + 1)) # increment error counter + # if error count > retry_count leave here + [ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && + write_log 14 "Transfer failed after $retry_count retries" + + write_log 4 "Transfer failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds" + sleep $RETRY_SECONDS & + PID_SLEEP=$! + wait $PID_SLEEP # enable trap-handler + PID_SLEEP=0 + done + + # check for error + if grep -q '"Error":' $DATFILE; then + __CODE=$(grep -o '"Code":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1) + [[ $__CODE == "ResourceNotFound.NoDataOfRecord" ]] && return 0 + write_log 4 "TecentCloud reported an error:" + write_log 7 "$(cat $DATFILE)" # report error + return 1 + fi + + return 0 +} + +# Build base command to use +__PRGBASE="$CURL -RsS -o $DATFILE --stderr $ERRFILE" + +# force network/interface-device to use for communication +if [ -n "$bind_network" ]; then + local __DEVICE + network_get_physdev __DEVICE $bind_network || + write_log 13 "Can not detect local device using 'network_get_physdev $bind_network' - Error: '$?'" + write_log 7 "Force communication via device '$__DEVICE'" + __PRGBASE="$__PRGBASE --interface $__DEVICE" +fi + +# force ip version to use +if [ $force_ipversion -eq 1 ]; then + [ $use_ipv6 -eq 0 ] && __PRGBASE="$__PRGBASE -4" || __PRGBASE="$__PRGBASE -6" # force IPv4/IPv6 +fi + +# set certificate parameters +if [ "$cacert" = "IGNORE" ]; then # idea from Ticket #15327 to ignore server cert + __PRGBASE="$__PRGBASE --insecure" # but not empty better to use "IGNORE" +elif [ -f "$cacert" ]; then + __PRGBASE="$__PRGBASE --cacert $cacert" +elif [ -d "$cacert" ]; then + __PRGBASE="$__PRGBASE --capath $cacert" +elif [ -n "$cacert" ]; then # it's not a file and not a directory but given + write_log 14 "No valid certificate(s) found at '$cacert' for HTTPS communication" +fi + +# disable proxy if not set (there might be .wgetrc or .curlrc or wrong environment set) +# or check if libcurl compiled with proxy support +if [ -z "$proxy" ]; then + __PRGBASE="$__PRGBASE --noproxy '*'" +elif [ -z "$CURL_PROXY" ]; then + # if libcurl has no proxy support and proxy should be used then force ERROR + write_log 13 "cURL: libcurl compiled without Proxy support" +fi + +# Signature Method v3 +# get more information from github.com/TencentCloud/signature-process-demo, +# at signature-v3/bash/signv3_no_xdd.sh +# usage: build_authorization +build_authorization() { + local __SECRET_ID=$username + local __SECRET_KEY=$password + + local __SERVICE=$(printf %s "$__URLHOST" | cut -d '.' -f1) + local __REGION="" + local __ACTION=$1 + local __VERSION=$2 + local __ALGORITHM="TC3-HMAC-SHA256" + local __TIMESTAMP=$3 + local __DATE=$(date -u -d @$__TIMESTAMP +"%Y-%m-%d") + local __PAYLOAD=$4 + + # Step 1: Concatenate request string + local __CANONICAL_URI="/" + local __CANONICAL_QUERYSTRING="" + local __CANONICAL_HEADERS=$( + cat < +build_header() { + local __ACTION=$1 + local __VERSION=$2 + local __TIMESTAMP=$(date +%s) + local __PAYLOAD=$3 + + local __AUTHORIZATION=$(build_authorization $__ACTION $__VERSION $__TIMESTAMP $__PAYLOAD) + + printf '%s' "--header 'HOST: $__URLHOST' " + printf '%s' "--header 'Content-Type: $__CONTENT_TYPE' " + printf '%s' "--header 'X-TC-Action: $__ACTION' " + printf '%s' "--header 'X-TC-Version: $__VERSION' " + printf '%s' "--header 'X-TC-Timestamp: $__TIMESTAMP' " + printf '%s' "--header 'Authorization: $__AUTHORIZATION' " +} + +# API: DescribeRecordList。 +# https://cloud.tencent.com/document/api/1427/56166 +build_describe_record_list_request_param() { + local __PAYLOAD="{\"Domain\":\"$__DOMAIN\"" + __PAYLOAD="$__PAYLOAD,\"Offset\":0" + __PAYLOAD="$__PAYLOAD,\"Limit\":1" + __PAYLOAD="$__PAYLOAD,\"RecordType\":\"$__TYPE\"" + if [[ -n "$__HOST" ]]; then + __PAYLOAD="$__PAYLOAD,\"Subdomain\":\"$__HOST\"" + fi + __PAYLOAD="$__PAYLOAD}" + + printf '%s' "--request POST " + printf '%s' "$__URLBASE " + printf '%s' "--data '$__PAYLOAD' " + build_header "DescribeRecordList" "2021-03-23" $__PAYLOAD +} + +# API: CreateRecord +# https://cloud.tencent.com/document/api/1427/56180 +build_create_record_request_param() { + local __VALUE=$1 + local __RECLINE=${2:-默认} + + local __PAYLOAD="{\"Domain\":\"$__DOMAIN\"" + __PAYLOAD="$__PAYLOAD,\"RecordType\":\"$__TYPE\"" + __PAYLOAD="$__PAYLOAD,\"RecordLine\":\"$__RECLINE\"" + __PAYLOAD="$__PAYLOAD,\"Value\":\"$__VALUE\"" + if [[ -n "$__HOST" ]]; then + __PAYLOAD="$__PAYLOAD,\"SubDomain\":\"$__HOST\"" + fi + __PAYLOAD="$__PAYLOAD}" + + printf '%s' "--request POST " + printf '%s' "$__URLBASE " + printf '%s' "--data '$__PAYLOAD' " + build_header "CreateRecord" "2021-03-23" $__PAYLOAD +} + +# API: ModifyRecord +# https://cloud.tencent.com/document/api/1427/56157 +build_modify_record_request_param() { + local __VALUE=$1 + local __RECLINE=${2:-默认} + local __RECID=$3 + + local __PAYLOAD="{\"Domain\":\"$__DOMAIN\"" + __PAYLOAD="$__PAYLOAD,\"RecordType\":\"$__TYPE\"" + __PAYLOAD="$__PAYLOAD,\"RecordLine\":\"$__RECLINE\"" + __PAYLOAD="$__PAYLOAD,\"RecordId\":$__RECID" + __PAYLOAD="$__PAYLOAD,\"Value\":\"$__VALUE\"" + if [[ -n "$__HOST" ]]; then + __PAYLOAD="$__PAYLOAD,\"SubDomain\":\"$__HOST\"" + fi + __PAYLOAD="$__PAYLOAD}" + + printf '%s' "--request POST " + printf '%s' "$__URLBASE " + printf '%s' "--data '$__PAYLOAD' " + build_header "ModifyRecord" "2021-03-23" $__PAYLOAD +} + +if [ -n "$record_id" ]; then + __RECID="$record_id" +else + # read record id for A or AAAA record of host.domain.TLD + __RUNPROG="$__PRGBASE $(build_describe_record_list_request_param)" + # extract zone id + tencentcloud_transfer || return 1 + __RECID=$(grep -o '"RecordId":[[:space:]]*[0-9]*' $DATFILE | grep -o '[0-9]*' | head -1) +fi + +[ $VERBOSE -gt 1 ] && write_log 7 "Got record id: $__RECID" + +# extract current stored IP +__RECLINE=$(grep -o '"Line":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1) +__DATA=$(grep -o '"Value":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1) + +# check data +[ $use_ipv6 -eq 0 ] && + __DATA=$(printf "%s" "$__DATA" | grep -m 1 -o "$IPV4_REGEX") || + __DATA=$(printf "%s" "$__DATA" | grep -m 1 -o "$IPV6_REGEX") + +# we got data so verify +[ -n "$__DATA" ] && { + # expand IPv6 for compare + if [ $use_ipv6 -eq 1 ]; then + expand_ipv6 $__IP __IPV6 + expand_ipv6 $__DATA __DATA + [ "$__DATA" = "$__IPV6" ] && { # IPv6 no update needed + write_log 7 "IPv6 at cloud.tencent.com already up to date" + return 0 + } + else + [ "$__DATA" = "$__IP" ] && { # IPv4 no update needed + write_log 7 "IPv4 at cloud.tencent.com already up to date" + return 0 + } + fi +} + +if [ -z "$__RECID" ]; then + # create new record if record id not found + __RUNPROG="$__PRGBASE $(build_create_record_request_param $__IP $__RECLINE)" + tencentcloud_transfer || return 1 + return 0 +fi + +__RUNPROG="$__PRGBASE $(build_modify_record_request_param $__IP $__RECLINE $__RECID)" +tencentcloud_transfer || return 1 +return diff --git a/net/ddns-scripts/files/usr/share/ddns/default/dnspod.cn-v3.json b/net/ddns-scripts/files/usr/share/ddns/default/dnspod.cn-v3.json new file mode 100644 index 00000000000000..aa8991432ea64c --- /dev/null +++ b/net/ddns-scripts/files/usr/share/ddns/default/dnspod.cn-v3.json @@ -0,0 +1,9 @@ +{ + "name": "dnspod.cn-v3", + "ipv4": { + "url": "update_dnspod_cn_v3.sh" + }, + "ipv6": { + "url": "update_dnspod_cn_v3.sh" + } +}