#!/bin/sh
#
#.Distributed under the terms of the GNU General Public License (GPL) version 2.0
# 2023 ren dong <lml256@foxmail.com>
#
# Aliyun DNS Documentation at https://help.aliyun.com/document_detail/29742.html
#
# This script is parsed by dynamic_dns_functions.sh inside send_update() function
#
# using following options from /etc/config/ddns
# option username - AccessKeyID generated by Aliyun
# option password - AccessKeySecret generated by Aliyun
# option domain   - "hostname@yourdomain.TLD" or "@yourdomain.TLD"
#
# variable __IP already defined with the ip-address to use for update
#

# set API URL base
__URLBASE="https://alidns.aliyuncs.com/?"

# check parameters
[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "Communication require cURL with SSL support. Please install"
[ -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'"

. /usr/share/libubox/jshn.sh

local __RR __HOST __DOMAIN __TYPE

# split __RR __DOMAIN from $domain
__RR=${domain%%@*}
__DOMAIN=${domain##*@}

if [ -z "$__RR" ]; then
	__RR="@" && __HOST="$__DOMAIN"
else 
	__HOST="$__RR.$__DOMAIN"
fi

# set record type
[ "$use_ipv6" -eq 0 ] && __TYPE="A" || __TYPE="AAAA"

# encode params using RFC3986 rule
encode_url_component() {
	local str1 str2 index
	str1=$(printf -- '%s' "$1" | $CURL -Gso /dev/null -w '%{url_effective}' --data-urlencode @- "aliyun.com" | cut -d "?" -f 2)
	str2=""
	index=0
	# convert the two hex numbers after '%' to uppercase
	# we need uppercase hex, and use the above code is enough on other linux platform. but
	# the curl of openwrt is a little bit different to other versions, i dont know why
	while [ "$index" -lt ${#str1} ]; do
		if [ "${str1:$index:1}" = "%" ]; then
			str2="$str2$(printf -- '%s' "${str1:$index:3}" | tr [a-z] [A-Z])" && index=$((index + 3))
		else
			str2="$str2${str1:$index:1}" && index=$((index + 1))
		fi
	done
	printf -- '%s' "$str2"
}

do_request() {
	local common_params canonicalized_query_string string_to_sign signature
	local program http_code err
	common_params="Format=JSON
				  Version=2015-01-09
				  AccessKeyId=$username
				  SignatureMethod=HMAC-SHA1
				  SignatureVersion=1.0
				  Timestamp=$(encode_url_component "$(date -u +"%Y-%m-%dT%H:%M:%SZ")")
				  SignatureNonce=$(head /dev/urandom | tr -dc '0123456789' | head -c16)"

	# build canonicalized query string, notice we use ascii order when sorting
	canonicalized_query_string="$(printf -- '%s' "$common_params $*" | sed 's/\s\+/\n/g' | LC_COLLATE=C sort | xargs | sed 's/\s/\&/g')"

	# calculate signature
	string_to_sign="GET&$(encode_url_component "/")&$(encode_url_component "$canonicalized_query_string")"
	signature="$(printf -- '%s' "$string_to_sign" | openssl sha1 -binary -hmac "$password&" | openssl base64)"
	signature="Signature=$(encode_url_component "$signature")"

	program="$CURL -sSL -o $DATFILE --stderr $ERRFILE -w '%{http_code}' \"$__URLBASE$canonicalized_query_string&$signature\""

	write_log 7 "Run command #> $program"
	http_code=$(eval "$program")
	err=$?
	[ "$err" -eq 0 ] && [ "$http_code" -eq 200 ] || {
		write_log 3 "Run command got error, curl err: $err, http_code: $http_code"
		write_log 7 "DATFILE: $(cat "$DATFILE") ERRFILE $(cat "$ERRFILE")"
		return 1
	}
}

do_request "Action=DescribeSubDomainRecords" \
	"DomainName=$(encode_url_component "$__DOMAIN")" \
	"Type=$__TYPE" \
	"SubDomain=$(encode_url_component "$__HOST")" || return 1

# load record id and record value from the response
json_load_file "$DATFILE"
json_get_var __RECORD_COUNT TotalCount

# if no record found, report error
[ "$__RECORD_COUNT" -eq 0 ] && {
	write_log 7 "DNS record of $__HOST is not exist."
	return 1
}

# if multiple records are found, only use the first one
[ "$__RECORD_COUNT" -gt 1 ] && {
	write_log 4 "WARNING: found multiple records of $__HOST, only use the first one"
}

# select the first DNS record
json_select DomainRecords
json_select Record
json_select 1
# get the record id of the first DNS record
json_get_var __RECORD_ID RecordId
json_get_var __RECORD_VALUE Value

# dont update if the ip has not changed
[ "$__RECORD_VALUE" = "$__IP" ] && {
	write_log 7 "DNS record is up to date"
	return 0
}

do_request "Action=UpdateDomainRecord" \
	"RR=$(encode_url_component "$__RR")" \
	"RecordId=$__RECORD_ID" \
	"Type=$__TYPE" \
	"Value=$(encode_url_component "$__IP")" || return 1

return 0
