diff --git a/net/ddns-scripts/Makefile b/net/ddns-scripts/Makefile index 0561f99584..cd23443fa2 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:=48 +PKG_RELEASE:=49 PKG_LICENSE:=GPL-2.0 @@ -307,6 +307,15 @@ define Package/ddns-scripts-porkbun/description "option domain" to be the FQDN for which to configure DDNS endef +define Package/ddns-scripts-huaweicloud + $(call Package/ddns-scripts/Default) + TITLE:=Extension for huaweicloud.com API + DEPENDS:=ddns-scripts +curl +openssl-util +endef + +define Package/ddns-scripts-huaweicloud/description + Dynamic DNS Client scripts extension for huaweicloud.com API (require curl and openssl) +endef define Build/Configure endef @@ -386,6 +395,7 @@ define Package/ddns-scripts-services/install rm $(1)/usr/share/ddns/default/ns1.com.json rm $(1)/usr/share/ddns/default/one.com.json rm $(1)/usr/share/ddns/default/porkbun.com-v3.json + rm $(1)/usr/share/ddns/default/huaweicloud.com.json endef @@ -721,6 +731,25 @@ exit 0 endef +define Package/ddns-scripts-huaweicloud/install + $(INSTALL_DIR) $(1)/usr/lib/ddns + $(INSTALL_BIN) ./files/usr/lib/ddns/update_huaweicloud_com.sh \ + $(1)/usr/lib/ddns + + $(INSTALL_DIR) $(1)/usr/share/ddns/default + $(INSTALL_DATA) ./files/usr/share/ddns/default/huaweicloud.com.json \ + $(1)/usr/share/ddns/default/ +endef + +define Package/ddns-scripts-huaweicloud/prerm +#!/bin/sh +if [ -z "$${IPKG_INSTROOT}" ]; then + /etc/init.d/ddns stop +fi +exit 0 +endef + + $(eval $(call BuildPackage,ddns-scripts)) $(eval $(call BuildPackage,ddns-scripts-services)) $(eval $(call BuildPackage,ddns-scripts-utils)) @@ -741,3 +770,4 @@ $(eval $(call BuildPackage,ddns-scripts-transip)) $(eval $(call BuildPackage,ddns-scripts-ns1)) $(eval $(call BuildPackage,ddns-scripts-one)) $(eval $(call BuildPackage,ddns-scripts-porkbun)) +$(eval $(call BuildPackage,ddns-scripts-huaweicloud)) diff --git a/net/ddns-scripts/files/usr/lib/ddns/update_huaweicloud_com.sh b/net/ddns-scripts/files/usr/lib/ddns/update_huaweicloud_com.sh new file mode 100644 index 0000000000..1c21b1d2e7 --- /dev/null +++ b/net/ddns-scripts/files/usr/lib/ddns/update_huaweicloud_com.sh @@ -0,0 +1,152 @@ +#!/bin/sh +# +# script for sending updates to huaweicloud.com +# 2023-2024 sxlehua +# API documentation at https://support.huaweicloud.com/api-dns/dns_api_62003.html +# API signature documentation at https://support.huaweicloud.com/api-dns/dns_api_30003.html +# +# This script is parsed by dynamic_dns_functions.sh inside send_update() function +# +# useage: +# using following options from /etc/config/ddns +# option username - huaweicloud Access Key Id +# option password - huaweicloud Secret Access Key,AK、SK documentation from https://support.huaweicloud.com/devg-apisign/api-sign-provide-aksk.html +# option domain - "hostname@yourdomain.TLD" # syntax changed to remove split_FQDN() function and tld_names.dat.gz +# + +# Check inputs +[ -z "$username" ] && write_log 14 "Configuration error! [username] cannot be empty" +[ -z "$password" ] && write_log 14 "Configuration error! [password] cannot be empty" + +[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "huaweicloud API require cURL with SSL support. Please install" +command -v openssl >/dev/null 2>&1 || write_log 14 "huaweicloud API require openssl-util support. Please install" + +# public variable +local __HOST __DOMAIN __TYPE __ZONE_ID __RECORD_ID +local __ENDPOINT="dns.cn-north-1.myhuaweicloud.com" +local __TTL=120 +[ $use_ipv6 -eq 0 ] && __TYPE="A" || __TYPE="AAAA" + +# Get host and domain from $domain +[ "${domain:0:2}" == "@." ] && domain="${domain/./}" # host +[ "$domain" == "${domain/@/}" ] && domain="${domain/./@}" # host with no sperator +__HOST="${domain%%@*}" +__DOMAIN="${domain#*@}" +[ -z "$__HOST" -o "$__HOST" == "$__DOMAIN" ] && __HOST="@" + +hcloud_transfer() { + local method=$1 + local path=$2 + local query=$3 + local body=$4 + + local timestamp=$(date -u +'%Y%m%dT%H%M%SZ') + local contentType="" + if [ ! "$method" = "GET" ]; then + contentType="application/json" + fi + local _H_Content_Type="" + + local canonicalUri="${path}" + # add / if need + echo $canonicalUri | grep -qE "/$" || canonicalUri="$canonicalUri/" + local canonicalQuery="$query" # for extend + + local canonicalHeaders="host:$__ENDPOINT\nx-sdk-date:$timestamp\n" + local signedHeaders="host;x-sdk-date" + + if [ ! "$contentType" = "" ]; then + canonicalHeaders="content-type:$contentType\n${canonicalHeaders}" + signedHeaders="content-type;$signedHeaders" + _H_Content_Type="Content-Type: ${contentType}" + fi + + local hexencode=$(printf "%s" "$body" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/^.* //') + local canonicalRequest="$method\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$hexencode" + canonicalRequest="$(printf "$canonicalRequest%s")" + + local stringToSign="SDK-HMAC-SHA256\n$timestamp\n$(printf "%s" "$canonicalRequest" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/^.* //')" + stringToSign="$(printf "$stringToSign%s")" + + local signature=$(printf "%s" "$stringToSign" | openssl dgst -sha256 -hmac "$password" 2>/dev/null | sed 's/^.* //') + authorization="SDK-HMAC-SHA256 Access=$username, SignedHeaders=$signedHeaders, Signature=$signature" + + reqUrl="$__ENDPOINT$path" + if [ ! -z "$query" ]; then + reqUrl="$reqUrl""?$query" + fi + + curl -s -X "${method}" \ + -H "Host: $__ENDPOINT" \ + -H "$_H_Content_Type" \ + -H "Authorization: $authorization" \ + -H "X-Sdk-Date: $timestamp" \ + -d "${body}" \ + "https://$reqUrl" + + if [ $? -ne 0 ]; then + write_log 14 "rest api error" + fi +} + +get_zone() { + local resp=`hcloud_transfer GET /v2/zones "name=$__DOMAIN.&search_mode=equal" ""` + __ZONE_ID=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"'` + if [ "$__ZONE_ID" = "" ]; then + write_log 14 "error, no zone" + fi +} + +upd_record() { + local body="{\"name\":\"$__HOST.$__DOMAIN.\",\"type\":\"$__TYPE\",\"records\":[\"$__IP\"],\"ttl\":$__TTL}" + local resp=`hcloud_transfer PUT /v2/zones/"$__ZONE_ID"/recordsets/$__RECORD_ID "" "$body"` + local recordId=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"'` + if [ ! "$recordId" = "" ]; then + write_log 7 "upd [$recordId] success [$__TYPE] [$__IP]" + else + write_log 14 "upd ecord error [$resp]" + fi +} + +add_record() { + local body="{\"name\":\"$__HOST.$__DOMAIN.\",\"type\":\"$__TYPE\",\"records\":[\"$__IP\"],\"ttl\":$__TTL}" + local resp=`hcloud_transfer POST /v2/zones/"$__ZONE_ID"/recordsets "" "$body"` + local recordId=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"'` + if [ ! "$recordId" = "" ]; then + write_log 7 "add [$recordId] success [$__TYPE] [$__IP]" + else + write_log 14 "add record error [$resp]" + fi +} + +# Get DNS record +get_record() { + local ret=0 + local resp=`hcloud_transfer GET /v2/zones/$__ZONE_ID/recordsets "name=$__HOST.$__DOMAIN.&search_mode=equal" ""` + __RECORD_ID=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"' | head -1` + if [ "$__RECORD_ID" = "" ]; then + # Record needs to be add + ret=1 + else + local remoteIp=`printf "%s" $resp | grep -Eo '"records":\[[^]]+]' | cut -d ':' -f 2-10 | tr -d '[' | tr -d ']' | tr -d '"' | head -1` + if [ ! "$remoteIp" = "$__IP" ]; then + # Record needs to be updated + ret=2 + fi + fi + return $ret +} + +get_zone +get_record + +ret=$? +if [ $ret -eq 0 ]; then + write_log 7 "nochg [$__IP]" +fi +if [ $ret -eq 1 ]; then + add_record +fi +if [ $ret -eq 2 ]; then + upd_record +fi diff --git a/net/ddns-scripts/files/usr/share/ddns/default/huaweicloud.com.json b/net/ddns-scripts/files/usr/share/ddns/default/huaweicloud.com.json new file mode 100644 index 0000000000..be549deb64 --- /dev/null +++ b/net/ddns-scripts/files/usr/share/ddns/default/huaweicloud.com.json @@ -0,0 +1,9 @@ +{ + "name": "huaweicloud.com", + "ipv4": { + "url": "update_huaweicloud_com.sh" + }, + "ipv6": { + "url": "update_huaweicloud_com.sh" + } +}