-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6202 from ARNik/dns_beget
Add Beget.com DNS API support
- Loading branch information
Showing
1 changed file
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
#!/usr/bin/env sh | ||
# shellcheck disable=SC2034 | ||
dns_beget_info='Beget.com | ||
Site: Beget.com | ||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_beget | ||
Options: | ||
BEGET_User API user | ||
BEGET_Password API password | ||
Issues: github.com/acmesh-official/acme.sh/issues/6200 | ||
Author: ARNik [email protected] | ||
' | ||
|
||
Beget_Api="https://api.beget.com/api" | ||
|
||
#################### Public functions #################### | ||
|
||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||
# Used to add txt record | ||
dns_beget_add() { | ||
fulldomain=$1 | ||
txtvalue=$2 | ||
_debug "dns_beget_add() $fulldomain $txtvalue" | ||
fulldomain=$(echo "$fulldomain" | _lower_case) | ||
|
||
Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}" | ||
Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}" | ||
|
||
if [ -z "$Beget_Username" ] || [ -z "$Beget_Password" ]; then | ||
Beget_Username="" | ||
Beget_Password="" | ||
_err "You must export variables: Beget_Username, and Beget_Password" | ||
return 1 | ||
fi | ||
|
||
#save the credentials to the account conf file. | ||
_saveaccountconf_mutable Beget_Username "$Beget_Username" | ||
_saveaccountconf_mutable Beget_Password "$Beget_Password" | ||
|
||
_info "Prepare subdomain." | ||
if ! _prepare_subdomain "$fulldomain"; then | ||
_err "Can't prepare subdomain." | ||
return 1 | ||
fi | ||
|
||
_info "Get domain records" | ||
data="{\"fqdn\":\"$fulldomain\"}" | ||
res=$(_api_call "$Beget_Api/dns/getData" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't get domain records." | ||
return 1 | ||
fi | ||
|
||
_info "Add new TXT record" | ||
data="{\"fqdn\":\"$fulldomain\",\"records\":{" | ||
data=${data}$(_parce_records "$res" "A") | ||
data=${data}$(_parce_records "$res" "AAAA") | ||
data=${data}$(_parce_records "$res" "CAA") | ||
data=${data}$(_parce_records "$res" "MX") | ||
data=${data}$(_parce_records "$res" "SRV") | ||
data=${data}$(_parce_records "$res" "TXT") | ||
data=$(echo "$data" | sed 's/,$//') | ||
data=${data}'}}' | ||
|
||
str=$(_txt_to_dns_json "$txtvalue") | ||
data=$(_add_record "$data" "TXT" "$str") | ||
|
||
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't change domain records." | ||
return 1 | ||
fi | ||
|
||
return 0 | ||
} | ||
|
||
# Usage: fulldomain txtvalue | ||
# Used to remove the txt record after validation | ||
dns_beget_rm() { | ||
fulldomain=$1 | ||
txtvalue=$2 | ||
_debug "dns_beget_rm() $fulldomain $txtvalue" | ||
fulldomain=$(echo "$fulldomain" | _lower_case) | ||
|
||
Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}" | ||
Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}" | ||
|
||
_info "Get current domain records" | ||
data="{\"fqdn\":\"$fulldomain\"}" | ||
res=$(_api_call "$Beget_Api/dns/getData" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't get domain records." | ||
return 1 | ||
fi | ||
|
||
_info "Remove TXT record" | ||
data="{\"fqdn\":\"$fulldomain\",\"records\":{" | ||
data=${data}$(_parce_records "$res" "A") | ||
data=${data}$(_parce_records "$res" "AAAA") | ||
data=${data}$(_parce_records "$res" "CAA") | ||
data=${data}$(_parce_records "$res" "MX") | ||
data=${data}$(_parce_records "$res" "SRV") | ||
data=${data}$(_parce_records "$res" "TXT") | ||
data=$(echo "$data" | sed 's/,$//') | ||
data=${data}'}}' | ||
|
||
str=$(_txt_to_dns_json "$txtvalue") | ||
data=$(_rm_record "$data" "$str") | ||
|
||
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't change domain records." | ||
return 1 | ||
fi | ||
|
||
return 0 | ||
} | ||
|
||
#################### Private functions below #################### | ||
|
||
# Create subdomain if needed | ||
# Usage: _prepare_subdomain [fulldomain] | ||
_prepare_subdomain() { | ||
fulldomain=$1 | ||
|
||
_info "Detect the root zone" | ||
if ! _get_root "$fulldomain"; then | ||
_err "invalid domain" | ||
return 1 | ||
fi | ||
_debug _domain_id "$_domain_id" | ||
_debug _sub_domain "$_sub_domain" | ||
_debug _domain "$_domain" | ||
|
||
if [ -z "$_sub_domain" ]; then | ||
_debug "$fulldomain is a root domain." | ||
return 0 | ||
fi | ||
|
||
_info "Get subdomain list" | ||
res=$(_api_call "$Beget_Api/domain/getSubdomainList") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't get subdomain list." | ||
return 1 | ||
fi | ||
|
||
if _contains "$res" "\"fqdn\":\"$fulldomain\""; then | ||
_debug "Subdomain $fulldomain already exist." | ||
return 0 | ||
fi | ||
|
||
_info "Subdomain $fulldomain does not exist. Let's create one." | ||
data="{\"subdomain\":\"$_sub_domain\",\"domain_id\":$_domain_id}" | ||
res=$(_api_call "$Beget_Api/domain/addSubdomainVirtual" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't create subdomain." | ||
return 1 | ||
fi | ||
|
||
_debug "Cleanup subdomen records" | ||
data="{\"fqdn\":\"$fulldomain\",\"records\":{}}" | ||
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_debug "Can't cleanup $fulldomain records." | ||
fi | ||
|
||
data="{\"fqdn\":\"www.$fulldomain\",\"records\":{}}" | ||
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||
if ! _is_api_reply_ok "$res"; then | ||
_debug "Can't cleanup www.$fulldomain records." | ||
fi | ||
|
||
return 0 | ||
} | ||
|
||
# Usage: _get_root _acme-challenge.www.domain.com | ||
#returns | ||
# _sub_domain=_acme-challenge.www | ||
# _domain=domain.com | ||
# _domain_id=32436365 | ||
_get_root() { | ||
fulldomain=$1 | ||
i=1 | ||
p=1 | ||
|
||
_debug "Get domain list" | ||
res=$(_api_call "$Beget_Api/domain/getList") | ||
if ! _is_api_reply_ok "$res"; then | ||
_err "Can't get domain list." | ||
return 1 | ||
fi | ||
|
||
while true; do | ||
h=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100) | ||
_debug h "$h" | ||
|
||
if [ -z "$h" ]; then | ||
return 1 | ||
fi | ||
|
||
if _contains "$res" "$h"; then | ||
_domain_id=$(echo "$res" | _egrep_o "\"id\":[0-9]*,\"fqdn\":\"$h\"" | cut -d , -f1 | cut -d : -f2) | ||
if [ "$_domain_id" ]; then | ||
if [ "$h" != "$fulldomain" ]; then | ||
_sub_domain=$(echo "$fulldomain" | cut -d . -f 1-"$p") | ||
else | ||
_sub_domain="" | ||
fi | ||
_domain=$h | ||
return 0 | ||
fi | ||
return 1 | ||
fi | ||
p="$i" | ||
i=$(_math "$i" + 1) | ||
done | ||
return 1 | ||
} | ||
|
||
# Parce DNS records from json string | ||
# Usage: _parce_records [j_str] [record_name] | ||
_parce_records() { | ||
j_str=$1 | ||
record_name=$2 | ||
res="\"$record_name\":[" | ||
res=${res}$(echo "$j_str" | _egrep_o "\"$record_name\":\[.*" | cut -d '[' -f2 | cut -d ']' -f1) | ||
res=${res}"]," | ||
echo "$res" | ||
} | ||
|
||
# Usage: _add_record [data] [record_name] [record_data] | ||
_add_record() { | ||
data=$1 | ||
record_name=$2 | ||
record_data=$3 | ||
echo "$data" | sed "s/\"$record_name\":\[/\"$record_name\":\[$record_data,/" | sed "s/,\]/\]/" | ||
} | ||
|
||
# Usage: _rm_record [data] [record_data] | ||
_rm_record() { | ||
data=$1 | ||
record_data=$2 | ||
echo "$data" | sed "s/$record_data//g" | sed "s/,\+/,/g" | | ||
sed "s/{,/{/g" | sed "s/,}/}/g" | | ||
sed "s/\[,/\[/g" | sed "s/,\]/\]/g" | ||
} | ||
|
||
_txt_to_dns_json() { | ||
echo "{\"ttl\":600,\"txtdata\":\"$1\"}" | ||
} | ||
|
||
# Usage: _api_call [api_url] [input_data] | ||
_api_call() { | ||
api_url="$1" | ||
input_data="$2" | ||
|
||
_debug "_api_call $api_url" | ||
_debug "Request: $input_data" | ||
|
||
# res=$(curl -s -L -D ./http.header \ | ||
# "$api_url" \ | ||
# --data-urlencode login=$Beget_Username \ | ||
# --data-urlencode passwd=$Beget_Password \ | ||
# --data-urlencode input_format=json \ | ||
# --data-urlencode output_format=json \ | ||
# --data-urlencode "input_data=$input_data") | ||
|
||
url="$api_url?login=$Beget_Username&passwd=$Beget_Password&input_format=json&output_format=json" | ||
if [ -n "$input_data" ]; then | ||
url=${url}"&input_data=" | ||
url=${url}$(echo "$input_data" | _url_encode) | ||
fi | ||
res=$(_get "$url") | ||
|
||
_debug "Reply: $res" | ||
echo "$res" | ||
} | ||
|
||
# Usage: _is_api_reply_ok [api_reply] | ||
_is_api_reply_ok() { | ||
_contains "$1" '^{"status":"success","answer":{"status":"success","result":.*}}$' | ||
} |