mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-05-18 11:53:56 +08:00
9
.github/copilot-instructions.md
vendored
9
.github/copilot-instructions.md
vendored
@@ -58,6 +58,7 @@ Never use raw shell commands when acme.sh provides a wrapper function. This is t
|
||||
| Instead of | Use |
|
||||
|---|---|
|
||||
| `tr '[:upper:]' '[:lower:]'` | `_lower_case()` |
|
||||
| `tr '[:lower:]' '[:upper:]'` | `_upper_case()` |
|
||||
| `head -n 1` | `_head_n 1` |
|
||||
| `openssl dgst` / `openssl` | `_digest()` / `_hmac()` |
|
||||
| `date` | `_utc_date()` with `sed`/`tr` |
|
||||
@@ -68,6 +69,14 @@ Never use raw shell commands when acme.sh provides a wrapper function. This is t
|
||||
| `grep -E` / `grep -Po` | `_egrep_o()` |
|
||||
| `printf` | `echo` |
|
||||
| `idn` command | `_idn()` / `_is_idn()` |
|
||||
| `mktemp` | `_mktemp()` |
|
||||
| `[:space:]` | ` ` |
|
||||
| `[:alnum:]` | `A-Za-z0-9` |
|
||||
| `[:alpha:]` | `A-Za-z` |
|
||||
| `[:digit:]` | `0-9` |
|
||||
| `awk` | `cut` / `sed` / `while read` loops |
|
||||
|
||||
|
||||
|
||||
When fixing a pattern issue, fix **all instances** in the file, not just the one highlighted.
|
||||
|
||||
|
||||
14
acme.sh
14
acme.sh
@@ -65,7 +65,7 @@ ID_TYPE_IP="ip"
|
||||
|
||||
LOCAL_ANY_ADDRESS="0.0.0.0"
|
||||
|
||||
DEFAULT_RENEW=30
|
||||
DEFAULT_RENEW="${DEFAULT_RENEW:-30}"
|
||||
|
||||
NO_VALUE="no"
|
||||
|
||||
@@ -1599,6 +1599,7 @@ createCSR() {
|
||||
domain="$1"
|
||||
domainlist="$2"
|
||||
_isEcc="$3"
|
||||
_csreku="$4"
|
||||
|
||||
_initpath "$domain" "$_isEcc"
|
||||
|
||||
@@ -1612,7 +1613,7 @@ createCSR() {
|
||||
_err "Please create it first."
|
||||
return 1
|
||||
fi
|
||||
_createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
|
||||
_createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF" "" "$_csreku"
|
||||
|
||||
}
|
||||
|
||||
@@ -5324,6 +5325,11 @@ $_authorizations_map"
|
||||
_link_cert_retry="$(_math $_link_cert_retry + 1)"
|
||||
done
|
||||
|
||||
# cover case where the final poll returned 'valid'
|
||||
if [ -z "$Le_LinkCert" ] && _contains "$response" "\"status\":\"valid\""; then
|
||||
Le_LinkCert="$(echo "$response" | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
|
||||
fi
|
||||
|
||||
if [ -z "$Le_LinkCert" ]; then
|
||||
_err "Signing failed. Could not get Le_LinkCert, and stopped retrying after reaching the retry limit."
|
||||
_err "$response"
|
||||
@@ -5667,7 +5673,7 @@ renewAll() {
|
||||
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
|
||||
_debug "_set_level" "$_set_level"
|
||||
export _ACME_IN_RENEWALL=1
|
||||
for di in "${CERT_HOME}"/*[.:]*/; do
|
||||
for di in "${CERT_HOME}"/*.* "${CERT_HOME}"/*:*; do
|
||||
_debug di "$di"
|
||||
if ! [ -d "$di" ]; then
|
||||
_debug "Not a directory, skipping: $di"
|
||||
@@ -8254,7 +8260,7 @@ _process() {
|
||||
createDomainKey "$_domain" "$_keylength"
|
||||
;;
|
||||
createCSR)
|
||||
createCSR "$_domain" "$_altdomains" "$_ecc"
|
||||
createCSR "$_domain" "$_altdomains" "$_ecc" "$_extended_key_usage"
|
||||
;;
|
||||
setnotify)
|
||||
setnotify "$_notify_hook" "$_notify_level" "$_notify_mode" "$_notify_source"
|
||||
|
||||
@@ -68,8 +68,8 @@ deployer() {
|
||||
# Get Version Info to test key
|
||||
content="type=version&key=$_panos_key"
|
||||
## Exclude all scopes for the empty commit
|
||||
#_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
|
||||
#content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
|
||||
#_exclude_scope="<device-and-network>excluded</device-and-network><shared-object>excluded</shared-object>"
|
||||
#content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>$_panos_user</member></admin></partial></commit>"
|
||||
fi
|
||||
|
||||
# Generate API Key
|
||||
@@ -128,10 +128,9 @@ deployer() {
|
||||
#Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
|
||||
if [ "$FORCE" ]; then
|
||||
_debug "Force switch detected. Committing ALL changes to the firewall."
|
||||
cmd=$(printf "%s" "<commit><partial><force><admin><member>$_panos_user</member></admin></force></partial></commit>" | _url_encode)
|
||||
cmd=$(printf "%s" "<commit><force><partial><admin><member>$_panos_user</member></admin></partial></force></commit>" | _url_encode)
|
||||
else
|
||||
_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network>"
|
||||
cmd=$(printf "%s" "<commit><partial>$_exclude_scope<admin><member>$_panos_user</member></admin></partial></commit>" | _url_encode)
|
||||
cmd=$(printf "%s" "<commit><partial><admin><member>$_panos_user</member></admin></partial></commit>" | _url_encode)
|
||||
fi
|
||||
content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
|
||||
fi
|
||||
|
||||
@@ -387,7 +387,7 @@ synology_dsm_deploy() {
|
||||
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
|
||||
_info "Restart HTTP services succeeded."
|
||||
else
|
||||
_info "Restart HTTP services failed."
|
||||
_info "Restart HTTP services not necessary."
|
||||
fi
|
||||
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||
_logout
|
||||
|
||||
548
dnsapi/dns_baidu.sh
Normal file
548
dnsapi/dns_baidu.sh
Normal file
@@ -0,0 +1,548 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# Global variables for returning results (avoid stdout pollution from logging)
|
||||
_BAIDU_FIND_RESULT=""
|
||||
_BAIDU_BCE_AUTH_RESULT=""
|
||||
|
||||
: "${BAIDU_LOG_LEVEL:=2}"
|
||||
|
||||
_baidu_log_ts() {
|
||||
date
|
||||
}
|
||||
|
||||
_baidu_log_ge() {
|
||||
_want="$1"
|
||||
[ "${BAIDU_LOG_LEVEL:-0}" -ge "$_want" ]
|
||||
}
|
||||
|
||||
_baidu_log() {
|
||||
_lvl="$1"
|
||||
_tag="$2"
|
||||
_msg="$3"
|
||||
if [ "$_lvl" = "0" ] || _baidu_log_ge "$_lvl"; then
|
||||
printf -- "[%s] %s %s\n" "$(_baidu_log_ts)" "$_tag" "$_msg"
|
||||
fi
|
||||
}
|
||||
|
||||
_baidu_err() {
|
||||
_baidu_log 0 "baidu_bcd.err" "$1"
|
||||
return 1
|
||||
}
|
||||
|
||||
_baidu_info() {
|
||||
_baidu_log 1 "baidu_bcd.info" "$1"
|
||||
return 0
|
||||
}
|
||||
|
||||
_baidu_debug() {
|
||||
_baidu_log 2 "$1" "$2"
|
||||
return 0
|
||||
}
|
||||
|
||||
dns_baidu_info='Baidu Cloud BCD DNS
|
||||
Site: cloud.baidu.com
|
||||
Docs: https://cloud.baidu.com/doc/BCD/
|
||||
Signature: https://cloud.baidu.com/doc/Reference/s/njwvz1yfu
|
||||
Options:
|
||||
Baidu_AK AccessKeyId
|
||||
Baidu_SK SecretAccessKey
|
||||
OptionsAlt:
|
||||
Baidu_BCD_Host API host, default: bcd.baidubce.com
|
||||
Baidu_BCD_Version API version number, default: 1
|
||||
Baidu_BCD_Expire Signature expiration seconds, default: 3600
|
||||
Baidu_View Resolve view, default: DEFAULT
|
||||
Baidu_TTL Resolve ttl seconds, default: 300
|
||||
Baidu_RM_Max Max records to delete in one run, default: 20
|
||||
'
|
||||
|
||||
BAIDU_BCD_DEFAULT_HOST="bcd.baidubce.com"
|
||||
|
||||
# --- Public API ---
|
||||
dns_baidu_add() {
|
||||
fulldomain=$(_idn "$1")
|
||||
txtvalue=$2
|
||||
|
||||
if ! _baidu_prepare_record "$fulldomain"; then
|
||||
_baidu_err "baidu_prepare_record failed for add: $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for add: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
_existing_ids="$_BAIDU_FIND_RESULT"
|
||||
if [ "$_existing_ids" ]; then
|
||||
_baidu_info "txt exists, skip add: $_record_domain.$_zone_name"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_ttl="${Baidu_TTL:-300}"
|
||||
_ttl="$(_baidu_trim_ws "$_ttl")"
|
||||
case "$_ttl" in
|
||||
"" | *[!0-9]*)
|
||||
_ttl="300"
|
||||
;;
|
||||
esac
|
||||
_view="$(_baidu_trim_ws "${Baidu_View:-DEFAULT}")"
|
||||
txtvalue="$(_baidu_trim_ws "$txtvalue")"
|
||||
_record_domain="$(_baidu_trim_ws "$_record_domain")"
|
||||
_zone_name="$(_baidu_trim_ws "$_zone_name")"
|
||||
|
||||
_body="$(_baidu_payload_add_txt "$_zone_name" "$_record_domain" "$txtvalue" "$_ttl" "$_view")"
|
||||
|
||||
if ! _baidu_bcd_post "/domain/resolve/add" "$_body"; then
|
||||
_baidu_err "baidu_bcd_post failed: add record"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _baidu_is_api_error "$response"; then
|
||||
_baidu_err "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
dns_baidu_rm() {
|
||||
fulldomain=$(_idn "$1")
|
||||
txtvalue=$2
|
||||
|
||||
if ! _baidu_prepare_record "$fulldomain"; then
|
||||
_baidu_err "baidu_prepare_record failed for delete: $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for delete: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
_ids="$_BAIDU_FIND_RESULT"
|
||||
if [ -z "$_ids" ]; then
|
||||
_baidu_info "no matching txt to delete: $_record_domain.$_zone_name"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_rm_max="${Baidu_RM_Max:-20}"
|
||||
_rm_max="$(_baidu_trim_ws "$_rm_max")"
|
||||
case "$_rm_max" in
|
||||
"" | *[!0-9]*)
|
||||
_rm_max="20"
|
||||
;;
|
||||
esac
|
||||
_rm_cnt="$(printf "%s\n" "$_ids" | sed '/^$/d' | wc -l | tr -d ' ')"
|
||||
if [ "$_rm_cnt" ] && [ "$_rm_cnt" -gt "$_rm_max" ]; then
|
||||
_baidu_err "Refusing to delete $_rm_cnt records (limit: $_rm_max)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for _rid in $_ids; do
|
||||
_body="$(_baidu_payload_delete "$_zone_name" "$_rid")"
|
||||
if ! _baidu_bcd_post "/domain/resolve/delete" "$_body"; then
|
||||
_baidu_err "baidu_bcd_post failed: delete recordId=$_rid"
|
||||
return 1
|
||||
fi
|
||||
if _baidu_is_api_error "$response"; then
|
||||
_baidu_err "$response"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for delete verify: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
_left_ids="$_BAIDU_FIND_RESULT"
|
||||
if [ -z "$_left_ids" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ -n "$_left_ids" ]; then
|
||||
_baidu_err "delete verification failed: $_record_domain.$_zone_name still has TXT records"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- Config / Record Context ---
|
||||
_baidu_load_credentials() {
|
||||
Baidu_AK="${Baidu_AK:-$(_readaccountconf_mutable Baidu_AK)}"
|
||||
Baidu_SK="${Baidu_SK:-$(_readaccountconf_mutable Baidu_SK)}"
|
||||
|
||||
Baidu_AK="$(_baidu_trim_ws "$Baidu_AK")"
|
||||
Baidu_SK="$(_baidu_trim_ws "$Baidu_SK")"
|
||||
|
||||
if [ -z "$Baidu_AK" ] || [ -z "$Baidu_SK" ]; then
|
||||
_baidu_err "Baidu_AK and Baidu_SK are required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable Baidu_AK "$Baidu_AK"
|
||||
_saveaccountconf_mutable Baidu_SK "$Baidu_SK"
|
||||
|
||||
BAIDU_BCD_HOST="${Baidu_BCD_Host:-$BAIDU_BCD_DEFAULT_HOST}"
|
||||
BAIDU_BCD_VERSION="${Baidu_BCD_Version:-1}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_baidu_prepare_record() {
|
||||
_fulldomain="$1"
|
||||
if ! _baidu_load_credentials; then
|
||||
_baidu_err "baidu_load_credentials failed"
|
||||
return 1
|
||||
fi
|
||||
if ! _baidu_get_root "$_fulldomain"; then
|
||||
_baidu_err "Could not find zone for $_fulldomain"
|
||||
return 1
|
||||
fi
|
||||
_record_domain="$_sub_domain"
|
||||
_zone_name="$_domain"
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- Zone / Records ---
|
||||
_baidu_get_root() {
|
||||
domain=$1
|
||||
i=1
|
||||
p=1
|
||||
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||
if [ -z "$h" ]; then
|
||||
_baidu_err "invalid domain: $domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _baidu_bcd_post "/domain/resolve/list" "$(_baidu_payload_list "$h" 1 1)"; then
|
||||
_baidu_err "baidu_bcd_post failed: list zones"
|
||||
return 1
|
||||
fi
|
||||
if ! _baidu_is_api_error "$response" && (_contains "$response" "\"totalCount\"" || _contains "$response" "\"result\""); then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain=$h
|
||||
if [ "$_sub_domain" = "$_domain" ]; then
|
||||
_sub_domain="@"
|
||||
fi
|
||||
_baidu_info "zone matched: $_domain (host: $_sub_domain)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
}
|
||||
|
||||
_baidu_find_record_ids() {
|
||||
_zone_name="$1"
|
||||
_record_domain="$2"
|
||||
_rdtype="$3"
|
||||
_rdata="$4"
|
||||
|
||||
# Reset global result variable
|
||||
_BAIDU_FIND_RESULT=""
|
||||
|
||||
_zone_name_e="$(_baidu_json_escape "$_zone_name")"
|
||||
_record_domain_e="$(_baidu_json_escape "$_record_domain")"
|
||||
_rdtype_e="$(_baidu_json_escape "$_rdtype")"
|
||||
_rdata_e="$(_baidu_json_escape "$_rdata")"
|
||||
|
||||
_page=1
|
||||
_page_size=100
|
||||
_ids=""
|
||||
|
||||
_max_page=""
|
||||
while true; do
|
||||
if ! _baidu_bcd_post "/domain/resolve/list" "$(_baidu_payload_list "$_zone_name" "$_page" "$_page_size")"; then
|
||||
_baidu_err "baidu_bcd_post failed: list records"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _baidu_is_api_error "$response"; then
|
||||
_baidu_err "baidu_bcd error: $(_baidu_json_get_str "$response" "code") $(_baidu_json_get_str "$response" "message")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_normalized="$(
|
||||
printf "%s" "$response" | _normalizeJson
|
||||
)"
|
||||
|
||||
if [ -z "$_max_page" ]; then
|
||||
_total="$(_baidu_parse_totalcount "$_normalized")"
|
||||
_max_page="$(_baidu_calc_max_page "$_total" "$_page_size")"
|
||||
fi
|
||||
|
||||
_records=$(printf "%s" "$_normalized" | sed 's/},{/}\n{/g')
|
||||
while IFS= read -r _line; do
|
||||
_id="$(_baidu_match_record_id "$_line" "$_record_domain_e" "$_rdtype_e" "$_rdata_e")"
|
||||
if [ "$_id" ]; then
|
||||
_ids="$_ids $_id"
|
||||
fi
|
||||
done <<EOF
|
||||
$_records
|
||||
EOF
|
||||
|
||||
if [ "$_page" -ge "$_max_page" ]; then
|
||||
break
|
||||
fi
|
||||
_page=$(_math "$_page" + 1)
|
||||
done
|
||||
|
||||
# Store result in global variable instead of stdout
|
||||
_BAIDU_FIND_RESULT="$_ids"
|
||||
}
|
||||
|
||||
# --- HTTP ---
|
||||
_baidu_bcd_post() {
|
||||
_api_path="$1"
|
||||
_payload="$2"
|
||||
|
||||
# BCD API requires JSON payload. Some call sites build fragments; normalize defensively.
|
||||
_payload="$(_baidu_normalize_payload "$_payload")"
|
||||
|
||||
_ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
_expire="${Baidu_BCD_Expire:-3600}"
|
||||
_content_type="application/json; charset=utf-8"
|
||||
_payload_hash="$(printf "%s" "$_payload" | _digest sha256 hex)"
|
||||
|
||||
_uri="/v${BAIDU_BCD_VERSION}${_api_path}"
|
||||
if ! _baidu_bce_auth "POST" "$_uri" "" "$BAIDU_BCD_HOST" "$_ts" "$_expire" "$_content_type" "$_payload_hash"; then
|
||||
_baidu_err "baidu_bcd auth failed"
|
||||
return 1
|
||||
fi
|
||||
_auth="$_BAIDU_BCE_AUTH_RESULT"
|
||||
if [ -z "$_auth" ]; then
|
||||
_baidu_err "baidu_bcd auth failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_H1="Authorization: $_auth"
|
||||
_H2="x-bce-date: $_ts"
|
||||
_H3="x-bce-content-sha256: $_payload_hash"
|
||||
_H4="Host: $BAIDU_BCD_HOST"
|
||||
_H5=""
|
||||
|
||||
_url="https://${BAIDU_BCD_HOST}${_uri}"
|
||||
_signed_headers_dbg="$(printf "%s" "$_auth" | cut -d / -f 5)"
|
||||
_baidu_info "POST ${_uri}"
|
||||
_baidu_info "signedHeaders: $_signed_headers_dbg"
|
||||
_baidu_info "payload_sha256: $_payload_hash"
|
||||
_baidu_debug "baidu_bcd.http.payload" "$(_baidu_dbg_trim "$(_baidu_redact_txt "$_payload")")"
|
||||
response="$(_post "$_payload" "$_url" "" "POST" "$_content_type")"
|
||||
_ret="$?"
|
||||
_baidu_info "ret: $_ret"
|
||||
_req_id="$(_baidu_json_get_str "$response" "requestId")"
|
||||
_code="$(_baidu_json_get_str "$response" "code")"
|
||||
_msg="$(_baidu_json_get_str "$response" "message")"
|
||||
_baidu_info "response: requestId=${_req_id:-"-"} code=${_code:-"-"} message=$(_baidu_dbg_trim "${_msg:-"-"}")"
|
||||
if [ "$_ret" != "0" ]; then
|
||||
_baidu_err "baidu_bcd_post failed: $_uri"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- Auth / Signing ---
|
||||
_baidu_bce_auth() {
|
||||
# Signing algorithm (bce-auth-v1):
|
||||
# - SigningKey = HMAC-SHA256-HEX(sk, authStringPrefix)
|
||||
# - Signature = HMAC-SHA256-HEX(SigningKey, CanonicalRequest)
|
||||
# Reference: https://cloud.baidu.com/doc/Reference/s/njwvz1yfu
|
||||
_method="$1"
|
||||
_uri="$2"
|
||||
_query="$3"
|
||||
_host="$4"
|
||||
_ts="$5"
|
||||
_expire="$6"
|
||||
_ct="$7"
|
||||
_payload_hash="$8"
|
||||
|
||||
_BAIDU_BCE_AUTH_RESULT=""
|
||||
|
||||
_auth_prefix="bce-auth-v1/${Baidu_AK}/${_ts}/${_expire}"
|
||||
|
||||
_signed_headers="content-type;host;x-bce-content-sha256;x-bce-date"
|
||||
_canonical_uri="$(_baidu_bce_encode_path "$_uri")"
|
||||
_canonical_query=""
|
||||
|
||||
_host_v="$(_baidu_trim_ws "$_host")"
|
||||
_date_v="$(_baidu_trim_ws "$_ts")"
|
||||
_ct_v="$(_baidu_trim_ws "$_ct")"
|
||||
_host_e="$(printf "%s" "$_host_v" | _url_encode upper-hex)"
|
||||
_date_e="$(printf "%s" "$_date_v" | _url_encode upper-hex)"
|
||||
_ct_e="$(printf "%s" "$_ct_v" | _url_encode upper-hex)"
|
||||
_hash_e="$(printf "%s" "$_payload_hash" | _url_encode upper-hex)"
|
||||
|
||||
_canonical_headers="content-type:${_ct_e}
|
||||
host:${_host_e}
|
||||
x-bce-content-sha256:${_hash_e}
|
||||
x-bce-date:${_date_e}"
|
||||
|
||||
_canonical_request="${_method}
|
||||
${_canonical_uri}
|
||||
${_canonical_query}
|
||||
${_canonical_headers}"
|
||||
|
||||
_sk_hex="$(printf "%s" "$Baidu_SK" | _hex_dump | tr -d " ")"
|
||||
_signing_key="$(_baidu_hmac_sha256_hexkey "$_sk_hex" "$_auth_prefix")"
|
||||
_signing_key_hex="$(printf "%s" "$_signing_key" | _hex_dump | tr -d " ")"
|
||||
_signature="$(_baidu_hmac_sha256_hexkey "$_signing_key_hex" "$_canonical_request")"
|
||||
|
||||
_baidu_debug "baidu_bcd.auth" "bce_auth"
|
||||
_baidu_debug "baidu_bcd.auth.auth_prefix" "bce-auth-v1/[ak]/${_ts}/${_expire}/$_signed_headers/[signature]"
|
||||
_baidu_debug "baidu_bcd.auth.canonical_request_l" "$(printf "%s" "$_canonical_request" | sed -n 'l')"
|
||||
_baidu_debug "baidu_bcd.auth.signature" "$(printf "%s" "$_signature" | cut -c 1-16)..."
|
||||
_BAIDU_BCE_AUTH_RESULT="${_auth_prefix}/${_signed_headers}/${_signature}"
|
||||
return 0
|
||||
}
|
||||
|
||||
_baidu_bce_encode_path() {
|
||||
_p="$1"
|
||||
_out=""
|
||||
if [ "${_p#"/"}" != "$_p" ]; then
|
||||
_out="/"
|
||||
fi
|
||||
|
||||
_rest="${_p#/}"
|
||||
while [ -n "$_rest" ]; do
|
||||
_seg="${_rest%%/*}"
|
||||
if [ "$_seg" ]; then
|
||||
if [ -z "$_out" ] || [ "$_out" = "/" ]; then
|
||||
_out="${_out}$(printf "%s" "$_seg" | _url_encode upper-hex)"
|
||||
else
|
||||
_out="${_out}/$(printf "%s" "$_seg" | _url_encode upper-hex)"
|
||||
fi
|
||||
fi
|
||||
if [ "${_rest#*/}" = "$_rest" ]; then
|
||||
break
|
||||
fi
|
||||
_rest="${_rest#*/}"
|
||||
done
|
||||
|
||||
if [ -z "$_out" ]; then
|
||||
_out="/"
|
||||
fi
|
||||
printf "%s" "$_out"
|
||||
}
|
||||
|
||||
# --- Utils ---
|
||||
_baidu_trim() {
|
||||
printf "%s" "$1" | sed 's/^ *//;s/ *$//'
|
||||
}
|
||||
|
||||
_baidu_trim_ws() {
|
||||
printf "%s" "$1" | tr '\r\n\t' ' ' | tr -s ' ' | sed 's/^ *//;s/ *$//'
|
||||
}
|
||||
|
||||
_baidu_dbg_trim() {
|
||||
printf "%s" "$1" | tr '\r\n' ' ' | cut -c 1-800
|
||||
}
|
||||
|
||||
_baidu_is_api_error() {
|
||||
_contains "$1" "\"code\"" && _contains "$1" "\"message\""
|
||||
}
|
||||
|
||||
_baidu_normalize_payload() {
|
||||
_p="$(_baidu_trim "$(printf "%s" "$1" | tr -d '\r')")"
|
||||
if [ -z "$_p" ]; then
|
||||
printf "%s" ""
|
||||
return 0
|
||||
fi
|
||||
case "$_p" in
|
||||
\{* | \[*)
|
||||
printf "%s" "$_p"
|
||||
;;
|
||||
*)
|
||||
printf "%s" "{$_p}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_baidu_redact_txt() {
|
||||
printf "%s" "$1" | sed 's/"rdata" *: *"[^"]*"/"rdata":"[redacted]"/g'
|
||||
}
|
||||
|
||||
_baidu_json_get_str() {
|
||||
_json="$1"
|
||||
_key="$2"
|
||||
printf "%s" "$_json" | _normalizeJson | sed -n "s/.*\"${_key}\" *: *\"\\([^\"]*\\)\".*/\\1/p" | _head_n 1
|
||||
}
|
||||
|
||||
_baidu_json_escape() {
|
||||
_s="$1"
|
||||
_s="$(printf "%s" "$_s" | tr -d '\r\n')"
|
||||
printf "%s" "$_s" |
|
||||
sed 's/\\/\\\\/g; s/ /\\t/g' |
|
||||
_baidu_json_encode
|
||||
}
|
||||
|
||||
_baidu_json_encode() {
|
||||
_j_str="$(sed 's/"/\\"/g' | sed "s/\r/\\r/g")"
|
||||
printf "%s" "$_j_str" | _hex_dump | _lower_case | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n"
|
||||
}
|
||||
|
||||
_baidu_payload_list() {
|
||||
_domain="$(_baidu_json_escape "$1")"
|
||||
_pageNo="$2"
|
||||
_pageSize="$3"
|
||||
printf "%s" "{\"domain\":\"${_domain}\",\"pageNo\":${_pageNo},\"pageSize\":${_pageSize}}"
|
||||
}
|
||||
|
||||
_baidu_payload_add_txt() {
|
||||
_zoneName="$(_baidu_json_escape "$1")"
|
||||
_domain="$(_baidu_json_escape "$2")"
|
||||
_rdata="$(_baidu_json_escape "$3")"
|
||||
_ttl="$4"
|
||||
_view="$(_baidu_json_escape "$5")"
|
||||
printf "%s" "{\"domain\":\"${_domain}\",\"view\":\"${_view}\",\"rdType\":\"TXT\",\"ttl\":${_ttl},\"rdata\":\"${_rdata}\",\"zoneName\":\"${_zoneName}\"}"
|
||||
}
|
||||
|
||||
_baidu_payload_delete() {
|
||||
_zoneName="$(_baidu_json_escape "$1")"
|
||||
_recordId="$2"
|
||||
printf "%s" "{\"zoneName\":\"${_zoneName}\",\"recordId\":${_recordId}}"
|
||||
}
|
||||
|
||||
_baidu_parse_totalcount() {
|
||||
_json="$1"
|
||||
printf "%s" "$_json" | _egrep_o "\"totalCount\": *[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d " "
|
||||
}
|
||||
|
||||
_baidu_calc_max_page() {
|
||||
_total="$1"
|
||||
_page_size="$2"
|
||||
if [ -z "$_total" ]; then
|
||||
printf "%s" "1"
|
||||
return 0
|
||||
fi
|
||||
_max=$(((_total + _page_size - 1) / _page_size))
|
||||
if [ "$_max" -lt 1 ]; then
|
||||
_max=1
|
||||
fi
|
||||
printf "%s" "$_max"
|
||||
}
|
||||
|
||||
_baidu_match_record_id() {
|
||||
_line="$1"
|
||||
_domain_e="$2"
|
||||
_rdtype_e="$3"
|
||||
_rdata_e="$4"
|
||||
if ! _contains "$_line" "\"recordId\"" || (! _contains "$_line" "\"domain\":\"$_domain_e\"" && ! _contains "$_line" "\"domain\":\"${_domain_e}.\""); then
|
||||
return 0
|
||||
fi
|
||||
if ! _contains "$_line" "\"rdtype\":\"$_rdtype_e\"" && ! _contains "$_line" "\"rdType\":\"$_rdtype_e\""; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$_rdata_e" ] && ! _contains "$_line" "\"rdata\":\"$_rdata_e\""; then
|
||||
return 0
|
||||
fi
|
||||
printf "%s" "$_line" | _egrep_o "\"recordId\": *[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d " "
|
||||
}
|
||||
|
||||
_baidu_hmac_sha256_hexkey() {
|
||||
_key_hex="$1"
|
||||
_msg="$2"
|
||||
printf "%s" "$_msg" | _hmac sha256 "$_key_hex" hex
|
||||
}
|
||||
373
dnsapi/dns_bhosted.sh
Normal file
373
dnsapi/dns_bhosted.sh
Normal file
@@ -0,0 +1,373 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
dns_bhosted_info='bHosted.nl DNS API
|
||||
Site: bHosted.nl
|
||||
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bhosted
|
||||
Options:
|
||||
BHOSTED_Username API username
|
||||
BHOSTED_Password API password (MD5 hash like bHosted web services example)
|
||||
BHOSTED_TTL TTL for TXT record (default: 300)
|
||||
BHOSTED_SLD Optional override (useful for multi-part TLDs like co.uk)
|
||||
BHOSTED_TLD Optional override (useful for multi-part TLDs like co.uk)
|
||||
Notes:
|
||||
- Plugin uses addrecord + delrecord for DNS-01 challenge
|
||||
- Record ID is retrieved from addrecord XML response and cached for cleanup
|
||||
'
|
||||
|
||||
BHOSTED_API_ROOT="https://webservices.bhosted.com/dns"
|
||||
|
||||
############ Public functions #####################
|
||||
|
||||
# Usage: dns_bhosted_add _acme-challenge.www.example.com "txt-value"
|
||||
dns_bhosted_add() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_debug "fulldomain" "$fulldomain"
|
||||
_debug "txtvalue" "$txtvalue"
|
||||
|
||||
_bhosted_load_credentials || return 1
|
||||
_bhosted_get_root "$fulldomain" || return 1
|
||||
|
||||
_info "Adding TXT record: ${_bhosted_name}.${_domain}"
|
||||
|
||||
BHOSTED_TTL="${BHOSTED_TTL:-$(_readaccountconf_mutable BHOSTED_TTL)}"
|
||||
BHOSTED_TTL="${BHOSTED_TTL:-300}"
|
||||
_saveaccountconf_mutable BHOSTED_TTL "$BHOSTED_TTL"
|
||||
|
||||
_bhosted_api_add_txt "$_bhosted_sld" "$_bhosted_tld" "$_bhosted_name" "$txtvalue" "$BHOSTED_TTL" || return 1
|
||||
|
||||
# Extract and cache record id in-memory for cleanup in this run
|
||||
_rec_id="$(_bhosted_extract_id "$response")"
|
||||
if [ -n "$_rec_id" ]; then
|
||||
_hash="$(_bhosted_cache_hash "$fulldomain" "$txtvalue")"
|
||||
_debug "_hash" "$_hash"
|
||||
_debug "_rec_id" "$_rec_id"
|
||||
_bhosted_mem_set_id "$_hash" "$_rec_id"
|
||||
else
|
||||
_err "TXT record added but no record id found in response."
|
||||
_err "Cleanup may fail unless bHosted addrecord returns <id>...</id>."
|
||||
_debug2 "add response" "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Usage: dns_bhosted_rm _acme-challenge.www.example.com "txt-value"
|
||||
dns_bhosted_rm() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_debug "fulldomain" "$fulldomain"
|
||||
_debug "txtvalue" "$txtvalue"
|
||||
|
||||
_bhosted_load_credentials || return 1
|
||||
_bhosted_get_root "$fulldomain" || return 1
|
||||
|
||||
_hash="$(_bhosted_cache_hash "$fulldomain" "$txtvalue")"
|
||||
_rec_id="$(_bhosted_mem_get_id "$_hash")"
|
||||
|
||||
if [ -z "$_rec_id" ]; then
|
||||
_err "No cached bHosted record id found for cleanup."
|
||||
_err "Please delete TXT manually in bHosted DNS for: ${_bhosted_name}.${_domain}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Removing TXT record id=${_rec_id}: ${_bhosted_name}.${_domain}"
|
||||
_bhosted_api_del_record "$_bhosted_sld" "$_bhosted_tld" "$_rec_id" || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
######## Private functions #####################
|
||||
|
||||
_bhosted_load_credentials() {
|
||||
BHOSTED_Username="${BHOSTED_Username:-$(_readaccountconf_mutable BHOSTED_Username)}"
|
||||
BHOSTED_Password="${BHOSTED_Password:-$(_readaccountconf_mutable BHOSTED_Password)}"
|
||||
|
||||
if [ -z "$BHOSTED_Username" ] || [ -z "$BHOSTED_Password" ]; then
|
||||
BHOSTED_Username=""
|
||||
BHOSTED_Password=""
|
||||
_err "You didn't specify bHosted credentials."
|
||||
_err "Please export BHOSTED_Username and BHOSTED_Password (MD5 hash)."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable BHOSTED_Username "$BHOSTED_Username"
|
||||
_saveaccountconf_mutable BHOSTED_Password "$BHOSTED_Password"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Determine root zone and host part
|
||||
# Supports simple domains automatically (example.com, example.nl)
|
||||
# For multi-part TLDs (example.co.uk), set:
|
||||
# BHOSTED_SLD=example
|
||||
# BHOSTED_TLD=co.uk
|
||||
_bhosted_get_root() {
|
||||
domain="$1"
|
||||
|
||||
BHOSTED_SLD="${BHOSTED_SLD:-$(_readdomainconf BHOSTED_SLD)}"
|
||||
BHOSTED_TLD="${BHOSTED_TLD:-$(_readdomainconf BHOSTED_TLD)}"
|
||||
|
||||
if [ -n "$BHOSTED_SLD" ] && [ -n "$BHOSTED_TLD" ]; then
|
||||
_savedomainconf BHOSTED_SLD "$BHOSTED_SLD"
|
||||
_savedomainconf BHOSTED_TLD "$BHOSTED_TLD"
|
||||
|
||||
_domain="${BHOSTED_SLD}.${BHOSTED_TLD}"
|
||||
case "$domain" in
|
||||
*."$_domain") ;;
|
||||
"$_domain") ;;
|
||||
*)
|
||||
_err "BHOSTED_SLD/BHOSTED_TLD do not match requested domain: $domain"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
_bhosted_sld="$BHOSTED_SLD"
|
||||
_bhosted_tld="$BHOSTED_TLD"
|
||||
_bhosted_name="${domain%."$_domain"}"
|
||||
if [ "$_bhosted_name" = "$domain" ]; then
|
||||
_bhosted_name=""
|
||||
fi
|
||||
|
||||
[ -n "$_bhosted_name" ] || _bhosted_name="@"
|
||||
|
||||
_debug "_domain" "$_domain"
|
||||
_debug "_bhosted_sld" "$_bhosted_sld"
|
||||
_debug "_bhosted_tld" "$_bhosted_tld"
|
||||
_debug "_bhosted_name" "$_bhosted_name"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Auto-parse: assume last label = tld, label before = sld
|
||||
# Works for .nl / .com / .org etc.
|
||||
_bhosted_tld="$(printf "%s" "$domain" | awk -F. '{print $NF}')"
|
||||
_bhosted_sld="$(printf "%s" "$domain" | awk -F. '{print $(NF-1)}')"
|
||||
|
||||
if [ -z "$_bhosted_sld" ] || [ -z "$_bhosted_tld" ]; then
|
||||
_err "Could not parse SLD/TLD from domain: $domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_domain="${_bhosted_sld}.${_bhosted_tld}"
|
||||
_bhosted_name="${domain%."$_domain"}"
|
||||
if [ "$_bhosted_name" = "$domain" ]; then
|
||||
_bhosted_name=""
|
||||
fi
|
||||
|
||||
[ -n "$_bhosted_name" ] || _bhosted_name="@"
|
||||
|
||||
_debug "_domain" "$_domain"
|
||||
_debug "_bhosted_sld" "$_bhosted_sld"
|
||||
_debug "_bhosted_tld" "$_bhosted_tld"
|
||||
_debug "_bhosted_name" "$_bhosted_name"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_bhosted_api_add_txt() {
|
||||
_sld="$1"
|
||||
_tld="$2"
|
||||
_name="$3"
|
||||
_content="$4"
|
||||
_ttl="$5"
|
||||
|
||||
_u_user="$(printf "%s" "$BHOSTED_Username" | _url_encode)"
|
||||
_u_pass="$(printf "%s" "$BHOSTED_Password" | _url_encode)"
|
||||
_u_sld="$(printf "%s" "$_sld" | _url_encode)"
|
||||
_u_tld="$(printf "%s" "$_tld" | _url_encode)"
|
||||
_u_name="$(printf "%s" "$_name" | _url_encode)"
|
||||
_u_content="$(printf "%s" "$_content" | _url_encode)"
|
||||
_u_ttl="$(printf "%s" "$_ttl" | _url_encode)"
|
||||
|
||||
_data="user=${_u_user}&password=${_u_pass}&tld=${_u_tld}&sld=${_u_sld}&type=TXT&name=${_u_name}&content=${_u_content}&ttl=${_u_ttl}"
|
||||
|
||||
_debug "bHosted add endpoint" "${BHOSTED_API_ROOT}/addrecord"
|
||||
response="$(_post "$_data" "${BHOSTED_API_ROOT}/addrecord")"
|
||||
_ret="$?"
|
||||
|
||||
_debug2 "bHosted add response" "$response"
|
||||
|
||||
if [ "$_ret" != "0" ]; then
|
||||
_err "bHosted addrecord request failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _bhosted_response_has_error "$response"; then
|
||||
_err "bHosted addrecord returned an error"
|
||||
_debug2 "response" "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_bhosted_api_del_record() {
|
||||
_sld="$1"
|
||||
_tld="$2"
|
||||
_id="$3"
|
||||
|
||||
_u_user="$(printf "%s" "$BHOSTED_Username" | _url_encode)"
|
||||
_u_pass="$(printf "%s" "$BHOSTED_Password" | _url_encode)"
|
||||
_u_sld="$(printf "%s" "$_sld" | _url_encode)"
|
||||
_u_tld="$(printf "%s" "$_tld" | _url_encode)"
|
||||
_u_id="$(printf "%s" "$_id" | _url_encode)"
|
||||
|
||||
_url="${BHOSTED_API_ROOT}/delrecord"
|
||||
_data="user=${_u_user}&password=${_u_pass}&tld=${_u_tld}&sld=${_u_sld}&id=${_u_id}"
|
||||
|
||||
_debug "bHosted delete endpoint" "$_url"
|
||||
response="$(_post "$_data" "$_url")"
|
||||
_ret="$?"
|
||||
|
||||
_debug2 "bHosted delete response" "$response"
|
||||
|
||||
if [ "$_ret" != "0" ]; then
|
||||
_err "bHosted delrecord request failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _bhosted_response_has_error "$response"; then
|
||||
_err "bHosted delrecord returned an error"
|
||||
_debug2 "response" "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Extract XML tag value from response, e.g. <id>12345</id>
|
||||
_bhosted_xml_value() {
|
||||
_tag="$1"
|
||||
_resp="$2"
|
||||
|
||||
# Flatten response to simplify parsing
|
||||
_flat="$(printf "%s" "$_resp" | tr -d '\r\n\t')"
|
||||
printf "%s" "$_flat" | sed -n "s:.*<${_tag}>\\([^<]*\\)</${_tag}>.*:\\1:p" | _head_n 1
|
||||
}
|
||||
|
||||
# Return code convention:
|
||||
# return 0 => response HAS error
|
||||
# return 1 => response has NO error (success)
|
||||
_bhosted_response_has_error() {
|
||||
_resp="$1"
|
||||
|
||||
# Empty response = error
|
||||
if [ -z "$_resp" ]; then
|
||||
_debug "Empty API response"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Prefer explicit bHosted XML response fields
|
||||
if _contains "$_resp" "<response>"; then
|
||||
_errors="$(_bhosted_xml_value "errors" "$_resp")"
|
||||
_done="$(_bhosted_xml_value "done" "$_resp")"
|
||||
_subcommand="$(_bhosted_xml_value "subcommand" "$_resp")"
|
||||
_id="$(_bhosted_xml_value "id" "$_resp")"
|
||||
|
||||
_debug "bHosted XML subcommand" "$_subcommand"
|
||||
_debug "bHosted XML id" "$_id"
|
||||
_debug "bHosted XML errors" "$_errors"
|
||||
_debug "bHosted XML done" "$_done"
|
||||
|
||||
# Success according to provided format
|
||||
if [ "$_errors" = "0" ] && [ "$_done" = "true" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "bHosted XML indicates failure"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback for unexpected/non-XML responses
|
||||
_resp_lc="$(_lower_case "$_resp")"
|
||||
|
||||
if _contains "$_resp_lc" "error"; then
|
||||
_debug "Detected 'error' in response"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$_resp_lc" "fout"; then
|
||||
_debug "Detected 'fout' in response"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$_resp_lc" "invalid"; then
|
||||
_debug "Detected 'invalid' in response"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$_resp_lc" "failed"; then
|
||||
_debug "Detected 'failed' in response"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$_resp_lc" "denied"; then
|
||||
_debug "Detected 'denied' in response"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# If no explicit error markers found, assume success
|
||||
return 1
|
||||
}
|
||||
|
||||
# Extract record id from response
|
||||
# Supports bHosted XML first, then generic fallbacks
|
||||
_bhosted_extract_id() {
|
||||
_resp="$1"
|
||||
|
||||
# bHosted XML: <id>12345</id>
|
||||
_id="$(_bhosted_xml_value "id" "$_resp" | tr -cd '0-9')"
|
||||
if [ -n "$_id" ]; then
|
||||
printf "%s" "$_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# JSON: "id":12345
|
||||
_id="$(printf "%s" "$_resp" | _egrep_o '"id"[[:space:]]*:[[:space:]]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
|
||||
if [ -n "$_id" ]; then
|
||||
printf "%s" "$_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# key=value: id=12345
|
||||
_id="$(printf "%s" "$_resp" | _egrep_o '(^|[[:space:][:punct:]])id[[:space:]]*=[[:space:]]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
|
||||
if [ -n "$_id" ]; then
|
||||
printf "%s" "$_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# "record id 12345" / "recordid 12345"
|
||||
_id="$(printf "%s" "$_resp" | _egrep_o '(record[[:space:]]*id|recordid)[^0-9]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
|
||||
if [ -n "$_id" ]; then
|
||||
printf "%s" "$_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Create a unique config key for cached record ids
|
||||
_bhosted_cache_hash() {
|
||||
_fd="$1"
|
||||
_tv="$2"
|
||||
# md5 hex of fulldomain|txtvalue
|
||||
printf "%s|%s" "$_fd" "$_tv" | _digest md5 hex
|
||||
}
|
||||
|
||||
_bhosted_cache_key() {
|
||||
_hash="$1"
|
||||
printf "%s" "BHOSTED_TXT_ID_${_hash}"
|
||||
}
|
||||
|
||||
_bhosted_mem_set_id() {
|
||||
_hash="$1"
|
||||
_id="$2"
|
||||
_key="$(_bhosted_cache_key "$_hash")"
|
||||
_savedomainconf "$_key" "$_id"
|
||||
}
|
||||
|
||||
_bhosted_mem_get_id() {
|
||||
_hash="$1"
|
||||
_key="$(_bhosted_cache_key "$_hash")"
|
||||
_readdomainconf "$_key"
|
||||
}
|
||||
303
dnsapi/dns_gname.sh
Normal file
303
dnsapi/dns_gname.sh
Normal file
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_gname_info='GNAME
|
||||
Site: www.gname.com
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gname
|
||||
Options:
|
||||
GNAME_APPID Your APPID
|
||||
GNAME_APPKEY Your APPKEY
|
||||
GNAME_TTL DNS resolution record TTL value, default 120.
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/6874
|
||||
Author: GNDevProd <tech@gname.com>
|
||||
'
|
||||
|
||||
GNAME_TLD_Api="https://www.gname.com/request/tlds?lx=all"
|
||||
GNAME_Api="https://api.gname.com"
|
||||
GNAME_TLDS_CACHE=""
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "T1rxqRBosdIK90xWCG3KLZNf6q_0HG9i01zxXp5CAS3"
|
||||
dns_gname_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$(printf "%s" "$2" | _url_encode)
|
||||
#Compatible with gname API RFC 1738 standard URL encoding
|
||||
txtvalue=$(printf '%s' "$txtvalue" | sed 's/%20/+/g')
|
||||
|
||||
GNAME_APPID="${GNAME_APPID:-$(_readaccountconf_mutable GNAME_APPID)}"
|
||||
GNAME_APPKEY="${GNAME_APPKEY:-$(_readaccountconf_mutable GNAME_APPKEY)}"
|
||||
GNAME_TTL="${GNAME_TTL:-$(_readaccountconf_mutable GNAME_TTL)}"
|
||||
GNAME_TTL="${GNAME_TTL:-120}"
|
||||
|
||||
if [ -z "$GNAME_APPID" ] || [ -z "$GNAME_APPKEY" ]; then
|
||||
GNAME_APPID=""
|
||||
GNAME_APPKEY=""
|
||||
_err "You have not configured the APPID and APPKEY for the GNAME API."
|
||||
_err "You can get yours from here https://www.gname.com/domain/api."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable GNAME_APPID "$GNAME_APPID"
|
||||
_saveaccountconf_mutable GNAME_APPKEY "$GNAME_APPKEY"
|
||||
_saveaccountconf_mutable GNAME_TTL "$GNAME_TTL"
|
||||
|
||||
if ! _extract_domain "$fulldomain"; then
|
||||
_err "Failed to extract domain. Please check your network or API response."
|
||||
return 1
|
||||
fi
|
||||
|
||||
gntime=$(date +%s)
|
||||
|
||||
#If the hostname is empty, you need to replace it with @.
|
||||
final_hostname=$(printf "%s" "${ext_hostname:-@}" | _url_encode)
|
||||
|
||||
# Parameters need to be sorted by key
|
||||
body="appid=$GNAME_APPID&exist=1&gntime=$gntime&jlz=$txtvalue&lang=us&lx=TXT&mx=0&ttl=$GNAME_TTL&xl=0&ym=$ext_domain&zj=$final_hostname"
|
||||
|
||||
_info "Adding TXT record for $ext_domain, host: $final_hostname"
|
||||
|
||||
if _post_to_api "/api/resolution/add" "$body"; then
|
||||
_info "Successfully added DNS record."
|
||||
return 0
|
||||
else
|
||||
_err "Failed to add DNS record via Gname API."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#Usage: remove _acme-challenge.www.domain.com "T1rxqRBosdIK90xWCG3KLZNf6q_0HG9i01zxXp5CASc"
|
||||
dns_gname_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
GNAME_APPID="${GNAME_APPID:-$(_readaccountconf_mutable GNAME_APPID)}"
|
||||
GNAME_APPKEY="${GNAME_APPKEY:-$(_readaccountconf_mutable GNAME_APPKEY)}"
|
||||
|
||||
if [ -z "$GNAME_APPID" ] || [ -z "$GNAME_APPKEY" ]; then
|
||||
GNAME_APPID=""
|
||||
GNAME_APPKEY=""
|
||||
_err "You have not configured the APPID and APPKEY for the GNAME API."
|
||||
_err "You can get yours from here https://www.gname.com/domain/api."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable GNAME_APPID "$GNAME_APPID"
|
||||
_saveaccountconf_mutable GNAME_APPKEY "$GNAME_APPKEY"
|
||||
|
||||
if ! _extract_domain "$fulldomain"; then
|
||||
_err "Failed to extract domain. Please check your network or API response."
|
||||
return 1
|
||||
fi
|
||||
|
||||
final_hostname="${ext_hostname:-@}"
|
||||
|
||||
_debug "Query DNS record ID $ext_domain $final_hostname $txtvalue"
|
||||
|
||||
if ! record_id=$(_get_record_id "$ext_domain" "$final_hostname" "$txtvalue"); then
|
||||
_err "Error occurred during record lookup. Skipping deletion to avoid errors."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$record_id" ]; then
|
||||
_info "DNS record not found, skip removing."
|
||||
return 0
|
||||
fi
|
||||
|
||||
_debug "DNS record ID:$record_id"
|
||||
gntime=$(date +%s)
|
||||
body="appid=$GNAME_APPID&gntime=$gntime&jxid=$record_id&lang=us&ym=$ext_domain"
|
||||
|
||||
if ! _post_to_api "/api/resolution/delete" "$body"; then
|
||||
_err "DNS record deletion failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "DNS record deletion successful"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Find the DNS record ID by hostname, record type, and record value.
|
||||
_get_record_id() {
|
||||
target_ym="$1"
|
||||
target_zjt="$2"
|
||||
target_jxz="$3"
|
||||
target_lx="TXT"
|
||||
|
||||
GNAME_APPID="${GNAME_APPID:-$(_readaccountconf_mutable GNAME_APPID)}"
|
||||
GNAME_APPKEY="${GNAME_APPKEY:-$(_readaccountconf_mutable GNAME_APPKEY)}"
|
||||
gntime=$(date +%s)
|
||||
body="appid=$GNAME_APPID&gntime=$gntime&limit=1000&lx=$target_lx&page=1&ym=$target_ym"
|
||||
|
||||
if ! _post_to_api "/api/resolution/list" "$body"; then
|
||||
_err "Query and parsing records failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
clean_response=$(echo "$post_response" | tr -d '\r')
|
||||
records=$(echo "$clean_response" | sed 's/.*"data":\[//; s/\],"count".*//; s/},/}\n/g' | grep "^{")
|
||||
matched_rows=$(echo "$records" | grep -Fi "\"zjt\":\"$target_zjt\"")
|
||||
|
||||
if [ -z "$matched_rows" ]; then
|
||||
_debug "No records found for host: $target_zjt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
exact_row=$(echo "$matched_rows" | grep -F "\"jxz\":\"$target_jxz\"" | _head_n 1)
|
||||
dns_record_id=""
|
||||
if [ -n "$exact_row" ]; then
|
||||
dns_record_id=$(echo "$exact_row" | _egrep_o "\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '"')
|
||||
fi
|
||||
|
||||
if [ -n "$dns_record_id" ]; then
|
||||
_debug "Successfully found exact record ID: $dns_record_id"
|
||||
printf "%s" "$dns_record_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_debug "Can not find exact DNS record match for: $target_zjt"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Request GNAME API,post_response: Response content
|
||||
_post_to_api() {
|
||||
uri=$1
|
||||
body=$2
|
||||
url="$GNAME_Api$uri"
|
||||
gntoken=$(_gntoken "$body")
|
||||
body="$body&gntoken=$gntoken"
|
||||
post_response="$(_post "$body" "$url" "" "POST" "application/x-www-form-urlencoded")"
|
||||
|
||||
http_err_code=$?
|
||||
if [ "$http_err_code" != "0" ]; then
|
||||
_err "POST API $url request failed:$http_err_code"
|
||||
return 1
|
||||
fi
|
||||
|
||||
normalized_response="$(echo "$post_response" | _normalizeJson)"
|
||||
if [ -z "$normalized_response" ]; then
|
||||
_err "Failed to normalize JSON response for [$uri]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ret_code=$(echo "$normalized_response" | sed 's/.*"code":\([-0-9]*\).*/\1/')
|
||||
|
||||
if [ "$ret_code" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$uri" = "/api/resolution/add" ]; then
|
||||
if _contains "$normalized_response" "the same host records and record values"; then
|
||||
_info "DNS record already exists, treat as success."
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
ret_msg=$(echo "$normalized_response" | sed 's/.*"msg":"\([^"]*\)".*/\1/')
|
||||
_err "POST API $url error: [$ret_code] $ret_msg"
|
||||
_debug "Full response: $normalized_response"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Split the complete domain into a host and a main domain.
|
||||
# example, www.gname.com can be split into ext_hostname=www,ext_domain=gname.com
|
||||
_extract_domain() {
|
||||
|
||||
host="$1"
|
||||
|
||||
# Prioritize reading from the cache and reduce network caching
|
||||
if [ -z "$GNAME_TLDS_CACHE" ]; then
|
||||
GNAME_TLDS_CACHE=$(_get_suffixes_json)
|
||||
fi
|
||||
|
||||
if [ -z "$GNAME_TLDS_CACHE" ]; then
|
||||
_err "The list of domain suffixes is empty after retrieval; cannot extract domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
main_part=$(echo "$GNAME_TLDS_CACHE" | sed 's/.*"main":\[\([^]]*\)\].*/\1/' | tr -d '"' | tr ',' ' ')
|
||||
sub_part=$(echo "$GNAME_TLDS_CACHE" | sed 's/.*"sub":\[\([^]]*\)\].*/\1/' | tr -d '"' | tr ',' ' ')
|
||||
suffix_list=$(echo "$main_part $sub_part" | tr -s ' ' | sed 's/^[ ]//;s/[ ]$//')
|
||||
|
||||
dot_count=$(echo "$host" | _egrep_o "\." | wc -l)
|
||||
|
||||
if [ "$dot_count" -eq 0 ]; then
|
||||
_err "Invalid domain format: $host (missing dot)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$dot_count" -eq 1 ]; then
|
||||
ext_hostname=""
|
||||
ext_domain="$host"
|
||||
|
||||
elif [ "$dot_count" -gt 1 ]; then
|
||||
matched_suffix=""
|
||||
for suffix in $suffix_list; do
|
||||
case "$host" in
|
||||
*".$suffix")
|
||||
if [ -z "$matched_suffix" ] || [ "${#suffix}" -gt "${#matched_suffix}" ]; then
|
||||
matched_suffix="$suffix"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$matched_suffix" ]; then
|
||||
prefix="${host%."$matched_suffix"}"
|
||||
main_name="${prefix##*.}"
|
||||
ext_domain="$main_name.$matched_suffix"
|
||||
else
|
||||
_tld="${host##*.}"
|
||||
_tmp="${host%.*}"
|
||||
_main="${_tmp##*.}"
|
||||
ext_domain="$_main.$_tld"
|
||||
fi
|
||||
|
||||
if [ "$host" = "$ext_domain" ]; then
|
||||
ext_hostname=""
|
||||
else
|
||||
ext_hostname="${host%."$ext_domain"}"
|
||||
fi
|
||||
|
||||
fi
|
||||
_debug "ext_hostname:$ext_hostname"
|
||||
_debug "ext_domain:$ext_domain"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Obtain the list of domain suffixes via API
|
||||
_get_suffixes_json() {
|
||||
_debug "GET request URL: $GNAME_TLD_Api Retrieves a list of domain suffixes."
|
||||
|
||||
if ! response="$(_get "$GNAME_TLD_Api")"; then
|
||||
_err "Failed to retrieve list of domain suffixes"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$response" ]; then
|
||||
_err "The list of domain suffixes is empty"
|
||||
return 1
|
||||
fi
|
||||
|
||||
normalized_response="$(echo "$response" | _normalizeJson)"
|
||||
if [ -z "$normalized_response" ]; then
|
||||
_err "Failed to normalize JSON response for domain suffix list"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$normalized_response" "\"code\":1"; then
|
||||
_err "Failed to retrieve list of domain name suffixes; code is not 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$normalized_response"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Generate API authentication signature
|
||||
_gntoken() {
|
||||
data_to_sign="$1"
|
||||
full_data="${data_to_sign}${GNAME_APPKEY}"
|
||||
hash=$(printf "%s" "$full_data" | _digest md5 hex | tr -d ' ')
|
||||
hash_upper=$(echo "$hash" | _upper_case)
|
||||
printf "%s" "$hash_upper"
|
||||
}
|
||||
220
dnsapi/dns_sitehost.sh
Executable file
220
dnsapi/dns_sitehost.sh
Executable file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_sitehost_info='SiteHost
|
||||
Site: sitehost.nz
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_sitehost
|
||||
Options:
|
||||
SITEHOST_API_KEY API Key
|
||||
SITEHOST_CLIENT_ID Client ID. The numeric client ID for your SiteHost account.
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/6892
|
||||
Author: Jordan Russell <jordanbrussell@gmail.com>
|
||||
'
|
||||
|
||||
SITEHOST_API="https://api.sitehost.nz/1.5"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
# Usage: dns_sitehost_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_sitehost_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if ! _sitehost_load_creds; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
# SiteHost expects the full record name as the name parameter
|
||||
_info "Adding TXT record for ${fulldomain}"
|
||||
if _sitehost_rest POST "dns/add_record.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&domain=$(printf '%s' "${_domain}" | _url_encode)&type=TXT&name=$(printf '%s' "${fulldomain}" | _url_encode)&content=$(printf '%s' "${txtvalue}" | _url_encode)"; then
|
||||
if _contains "$response" '"status":true'; then
|
||||
_info "TXT record added successfully."
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
_err "Could not add TXT record for ${fulldomain}"
|
||||
_err "$response"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Usage: dns_sitehost_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
# Remove the txt record after validation.
|
||||
dns_sitehost_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if ! _sitehost_load_creds; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting TXT records for ${_domain}"
|
||||
if ! _sitehost_rest GET "dns/list_records.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&domain=$(printf '%s' "${_domain}" | _url_encode)"; then
|
||||
_err "Could not list DNS records"
|
||||
_err "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$response" '"status":true'; then
|
||||
_err "Error listing DNS records"
|
||||
_err "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract record ID matching our fulldomain, type TXT, and txtvalue
|
||||
# Response format: {"return":[{"id":"123","name":"...","type":"TXT","content":"..."},...]}
|
||||
# SiteHost returns flat single-line JSON objects in the records array
|
||||
# Escape regex metacharacters in values before grep matching
|
||||
_fulldomain_grep="$(printf "%s" "$fulldomain" | sed 's/[][\\.^$*]/\\&/g')"
|
||||
_txtvalue_grep="$(printf "%s" "$txtvalue" | sed 's/[][\\.^$*]/\\&/g')"
|
||||
# Use field-specific matching to avoid false positives from substring matches
|
||||
_record_id="$(echo "$response" | _egrep_o '\{[^}]*\}' | grep '"name" *: *"'"${_fulldomain_grep}"'"' | grep '"type" *: *"TXT"' | grep '"content" *: *"'"${_txtvalue_grep}"'"' | _head_n 1 | _egrep_o '"id" *: *"?[0-9]+"?' | _egrep_o '[0-9]+')"
|
||||
|
||||
if [ -z "$_record_id" ]; then
|
||||
_info "TXT record not found, nothing to remove."
|
||||
return 0
|
||||
fi
|
||||
|
||||
_debug _record_id "$_record_id"
|
||||
|
||||
_info "Deleting TXT record ${_record_id} for ${fulldomain}"
|
||||
if _sitehost_rest POST "dns/delete_record.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&domain=$(printf '%s' "${_domain}" | _url_encode)&record_id=$(printf '%s' "${_record_id}" | _url_encode)"; then
|
||||
if _contains "$response" '"status":true'; then
|
||||
_info "TXT record deleted successfully."
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
_err "Could not delete TXT record for ${fulldomain}"
|
||||
_err "$response"
|
||||
return 1
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_sitehost_load_creds() {
|
||||
SITEHOST_API_KEY="${SITEHOST_API_KEY:-$(_readaccountconf_mutable SITEHOST_API_KEY)}"
|
||||
SITEHOST_CLIENT_ID="${SITEHOST_CLIENT_ID:-$(_readaccountconf_mutable SITEHOST_CLIENT_ID)}"
|
||||
|
||||
if [ -z "$SITEHOST_API_KEY" ] || [ -z "$SITEHOST_CLIENT_ID" ]; then
|
||||
SITEHOST_API_KEY=""
|
||||
SITEHOST_CLIENT_ID=""
|
||||
_err "You didn't specify SITEHOST_API_KEY and/or SITEHOST_CLIENT_ID."
|
||||
_err "Please export them and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable SITEHOST_API_KEY "$SITEHOST_API_KEY"
|
||||
_saveaccountconf_mutable SITEHOST_CLIENT_ID "$SITEHOST_CLIENT_ID"
|
||||
return 0
|
||||
}
|
||||
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
_get_root() {
|
||||
domain=$1
|
||||
|
||||
_debug "Getting domain list"
|
||||
|
||||
# Fetch ALL pages of domains first so we can match the most specific zone
|
||||
# (a more specific zone on a later page must take precedence over a broader match)
|
||||
_all_domains=""
|
||||
_page=1
|
||||
|
||||
while true; do
|
||||
if ! _sitehost_rest GET "dns/list_domains.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&filters%5Bpage_number%5D=${_page}"; then
|
||||
_err "Could not list domains"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$response" '"status":true'; then
|
||||
_err "Error listing domains"
|
||||
_err "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_all_domains="${_all_domains} ${response}"
|
||||
|
||||
_total_pages=$(echo "$response" | _egrep_o '"total_pages" *: *[0-9]+' | _egrep_o '[0-9]+')
|
||||
if [ -z "$_total_pages" ] || [ "$_page" -ge "$_total_pages" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
_page=$(_math "$_page" + 1)
|
||||
done
|
||||
|
||||
# Try each subdomain level, most specific first
|
||||
_i=1
|
||||
_p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f "${_i}"-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if echo "$_all_domains" | grep -F "\"${h}\"" >/dev/null 2>&1; then
|
||||
if [ "$_i" = "1" ]; then
|
||||
# DNS alias mode - fulldomain is the zone itself
|
||||
_sub_domain=""
|
||||
else
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"${_p}")
|
||||
fi
|
||||
_domain="${h}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_p="${_i}"
|
||||
_i=$(_math "$_i" + 1)
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Usage: _sitehost_rest method endpoint data
|
||||
_sitehost_rest() {
|
||||
m="$1"
|
||||
ep="$2"
|
||||
data="$3"
|
||||
url="${SITEHOST_API}/${ep}"
|
||||
|
||||
_debug url "$url"
|
||||
|
||||
_apikey="$(printf "%s" "${SITEHOST_API_KEY}" | _url_encode)"
|
||||
|
||||
if [ "$m" = "GET" ]; then
|
||||
response="$(_get "${url}?apikey=${_apikey}&${data}")"
|
||||
else
|
||||
_debug2 data "$data"
|
||||
response="$(_post "apikey=${_apikey}&${data}" "$url")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error ${ep}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
response="$(printf '%s' "$response" | tr -d '\r')"
|
||||
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
@@ -61,7 +61,7 @@ dns_world4you_add() {
|
||||
if _contains "$res" "successfully"; then
|
||||
return 0
|
||||
else
|
||||
msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "<h3[^>]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
|
||||
msg=$(echo "$res" | grep -A 20 'alert-notification' | grep 'class="weak-title">[^<]' | sed 's/<[^>]*>//g;s/^\s*//g')
|
||||
if [ "$msg" = '' ]; then
|
||||
_err "Unable to add record: Unknown error"
|
||||
echo "$ret" >'error-01.html'
|
||||
@@ -110,7 +110,7 @@ dns_world4you_rm() {
|
||||
return 3
|
||||
fi
|
||||
|
||||
recordid=$(printf "TXT:%s.:\"%s\"" "$fqdn" "$value" | _base64)
|
||||
recordid=$(echo "$form" | grep 'data-records="' | sed 's/.*"\([^"]*\)".*/\1/;s/"/"/g;s/},{/}\n{/g' | grep '"type":"TXT"' | grep "\"name\":\"$fqdn\"" | grep "\"value\":\"$value\"" | sed 's/^.*"id":"\([^"]*\)".*$/\1/')
|
||||
_debug recordid "$recordid"
|
||||
|
||||
_resethttp
|
||||
@@ -125,7 +125,7 @@ dns_world4you_rm() {
|
||||
if _contains "$res" "successfully"; then
|
||||
return 0
|
||||
else
|
||||
msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "<h3[^>]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
|
||||
msg=$(echo "$res" | grep -A 20 'alert-notification' | grep 'class="weak-title">[^<]' | sed 's/<[^>]*>//g;s/^\s*//g')
|
||||
if [ "$msg" = '' ]; then
|
||||
_err "Unable to remove record: Unknown error"
|
||||
echo "$ret" >'error-01.html'
|
||||
|
||||
Reference in New Issue
Block a user