Browse Source
Update dns_czechia.sh
Update dns_czechia.sh
Refactor the Czechia/Zoner DNS API script to ensure stability and better user experience. Changes: - Replaced Bash-specific substitutions with POSIX-compliant code (fixes 'Bad substitution' errors on dash/sh). - Improved JSON payload construction to be more robust across different shell environments. - Updated hostname logic to correctly use "@" for apex domains. - Refined error reporting: API error messages are now captured and displayed in English. - Cleaned up internal debug messages and aligned them with acme.sh standards. - Optimized zone picking logic for faster matching in multi-zone setups.pull/6764/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 104 additions and 132 deletions
@ -1,175 +1,147 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# dns_czechia.sh - Czechia/ZONER DNS API for acme.sh (DNS-01) |
|||
# |
|||
# Endpoint: |
|||
# https://api.czechia.com/api/DNS/<zone>/TXT |
|||
# Header: |
|||
# authorizationToken: <token> |
|||
# Body: |
|||
# {"hostName":"...","text":"...","ttl":3600,"publishZone":1} |
|||
# Documentation: https://api.czechia.com/swagger/index.html |
|||
# |
|||
# Required env: |
|||
# CZ_AuthorizationToken (saved to account.conf for automatic renewals) |
|||
# CZ_Zones zone(s) separated by comma/space, e.g. "example.com" or "example.com,example.net" |
|||
# For SAN/wildcard, the plugin picks the longest matching zone suffix per-domain. |
|||
# Required environment variables: |
|||
# CZ_AuthorizationToken Your API token from Czechia/Zoner administration. |
|||
# CZ_Zones Managed zones separated by comma or space (e.g. "example.com,example.net"). |
|||
# The plugin picks the best matching zone for each domain. |
|||
# |
|||
# Optional env (can be saved): |
|||
# CZ_TTL (default 3600) |
|||
# CZ_PublishZone (default 1) |
|||
# CZ_API_BASE (default https://api.czechia.com) |
|||
# CZ_CURL_TIMEOUT (default 30) |
|||
# Optional environment variables: |
|||
# CZ_API_BASE Defaults to https://api.czechia.com |
|||
# CZ_CURL_TIMEOUT Defaults to 30 |
|||
|
|||
dns_czechia_add() { |
|||
fulldomain="$1" |
|||
txtvalue="$2" |
|||
|
|||
_info "Czechia DNS add TXT for $fulldomain" |
|||
_czechia_load_conf || return 1 |
|||
_current_zone=$(_czechia_pick_zone "$fulldomain") |
|||
if [ -z "$_current_zone" ]; then |
|||
_err "No matching zone found for $fulldomain. Please check CZ_Zones." |
|||
return 1 |
|||
fi |
|||
|
|||
_url="$CZ_API_BASE/api/DNS/$_current_zone/TXT" |
|||
|
|||
# Calculate hostname: remove zone from fulldomain |
|||
_h=$(printf "%s" "$fulldomain" | sed "s/\.$_current_zone//; s/$_current_zone//") |
|||
|
|||
# Apex domain handling |
|||
if [ -z "$_h" ]; then |
|||
_h="@" |
|||
fi |
|||
|
|||
# Build JSON body (POSIX compatible) |
|||
_q='"' |
|||
_body="{$_q"hostName"$_q:$_q$_h$_q,$_q"text"$_q:$_q$txtvalue$_q,$_q"ttl"$_q:3600,$_q"publishZone"$_q:1}" |
|||
|
|||
zone="$(_czechia_pick_zone "$fulldomain")" || return 1 |
|||
host="$(_czechia_rel_host "$fulldomain" "$zone")" || return 1 |
|||
url="$CZ_API_BASE/api/DNS/$zone/TXT" |
|||
body="$(_czechia_build_body "$host" "$txtvalue")" |
|||
_info "Adding TXT record for $fulldomain" |
|||
|
|||
export _H1="Content-Type: application/json" |
|||
export _H2="authorizationToken: $CZ_AuthorizationToken" |
|||
|
|||
_czechia_api_request "POST" "$url" "$body" |
|||
_res=$(_post "$_body" "$_url" "" "POST") |
|||
_debug "API Response: $_res" |
|||
|
|||
if _contains "$_res" "errors" || _contains "$_res" "400"; then |
|||
_err "API error: $_res" |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
dns_czechia_rm() { |
|||
fulldomain="$1" |
|||
txtvalue="$2" |
|||
|
|||
_info "Czechia DNS remove TXT for $fulldomain" |
|||
_czechia_load_conf || return 1 |
|||
_current_zone=$(_czechia_pick_zone "$fulldomain") |
|||
[ -z "$_current_zone" ] && return 1 |
|||
|
|||
_url="$CZ_API_BASE/api/DNS/$_current_zone/TXT" |
|||
_h=$(printf "%s" "$fulldomain" | sed "s/\.$_current_zone//; s/$_current_zone//") |
|||
[ -z "$_h" ] && _h="@" |
|||
|
|||
zone="$(_czechia_pick_zone "$fulldomain")" || return 1 |
|||
host="$(_czechia_rel_host "$fulldomain" "$zone")" || return 1 |
|||
url="$CZ_API_BASE/api/DNS/$zone/TXT" |
|||
body="$(_czechia_build_body "$host" "$txtvalue")" |
|||
_q='"' |
|||
_body="{$_q"hostName"$_q:$_q$_h$_q,$_q"text"$_q:$_q$txtvalue$_q,$_q"publishZone"$_q:1}" |
|||
|
|||
_czechia_api_request "DELETE" "$url" "$body" |
|||
_info "Removing TXT record for $fulldomain" |
|||
|
|||
export _H1="Content-Type: application/json" |
|||
export _H2="authorizationToken: $CZ_AuthorizationToken" |
|||
|
|||
_res=$(_post "$_body" "$_url" "" "DELETE") |
|||
_debug "API Response: $_res" |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
######################################################################## |
|||
# Private functions |
|||
######################################################################## |
|||
|
|||
_czechia_load_conf() { |
|||
CZ_AuthorizationToken="${CZ_AuthorizationToken:-$(_readaccountconf_mutable CZ_AuthorizationToken)}" |
|||
if [ -z "$CZ_AuthorizationToken" ]; then |
|||
_err "CZ_AuthorizationToken is missing." |
|||
CZ_AuthorizationToken="$(_getaccountconf CZ_AuthorizationToken)" |
|||
fi |
|||
if [ -z "$CZ_AuthorizationToken" ]; then |
|||
_err "You didn't specify Czechia Authorization Token (CZ_AuthorizationToken)." |
|||
return 1 |
|||
fi |
|||
_saveaccountconf_mutable CZ_AuthorizationToken "$CZ_AuthorizationToken" |
|||
|
|||
CZ_Zones="${CZ_Zones:-$(_readaccountconf_mutable CZ_Zones)}" |
|||
CZ_TTL="${CZ_TTL:-$(_readaccountconf_mutable CZ_TTL)}" |
|||
CZ_PublishZone="${CZ_PublishZone:-$(_readaccountconf_mutable CZ_PublishZone)}" |
|||
CZ_API_BASE="${CZ_API_BASE:-$(_readaccountconf_mutable CZ_API_BASE)}" |
|||
CZ_CURL_TIMEOUT="${CZ_CURL_TIMEOUT:-$(_readaccountconf_mutable CZ_CURL_TIMEOUT)}" |
|||
|
|||
if [ -z "$CZ_Zones" ]; then |
|||
_err "CZ_Zones is required." |
|||
CZ_Zones="$(_getaccountconf CZ_Zones)" |
|||
fi |
|||
if [ -z "$CZ_Zones" ]; then |
|||
_err "You didn't specify Czechia Zones (CZ_Zones)." |
|||
return 1 |
|||
fi |
|||
|
|||
[ -z "$CZ_TTL" ] && CZ_TTL="3600" |
|||
[ -z "$CZ_PublishZone" ] && CZ_PublishZone="1" |
|||
[ -z "$CZ_API_BASE" ] && CZ_API_BASE="https://api.czechia.com" |
|||
[ -z "$CZ_CURL_TIMEOUT" ] && CZ_CURL_TIMEOUT="30" |
|||
|
|||
CZ_Zones="$(_czechia_norm_zonelist "$CZ_Zones")" |
|||
CZ_API_BASE="$(printf "%s" "$CZ_API_BASE" | sed 's:/*$::')" |
|||
|
|||
_saveaccountconf_mutable CZ_Zones "$CZ_Zones" |
|||
_saveaccountconf_mutable CZ_TTL "$CZ_TTL" |
|||
_saveaccountconf_mutable CZ_PublishZone "$CZ_PublishZone" |
|||
_saveaccountconf_mutable CZ_API_BASE "$CZ_API_BASE" |
|||
_saveaccountconf_mutable CZ_CURL_TIMEOUT "$CZ_CURL_TIMEOUT" |
|||
|
|||
# Defaults |
|||
if [ -z "$CZ_API_BASE" ]; then |
|||
CZ_API_BASE="https://api.czechia.com" |
|||
fi |
|||
|
|||
# Save to account.conf for renewals |
|||
_saveaccountconf CZ_AuthorizationToken "$CZ_AuthorizationToken" |
|||
_saveaccountconf CZ_Zones "$CZ_Zones" |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
_czechia_norm_zonelist() { |
|||
in="$1" |
|||
[ -z "$in" ] && return 0 |
|||
# Převedeme na lowercase a pomocí tr -d smažeme bílé znaky (POSIX safe) |
|||
_lower_case "$in" | tr -d '\t\r\n' | tr ' ' ',' | tr -s ',' | sed 's/\.$//; s/^,//; s/,$//; s/,,*/,/g' |
|||
} |
|||
|
|||
_czechia_pick_zone() { |
|||
fulldomain="$1" |
|||
|
|||
fd="$(_lower_case "$fulldomain")" |
|||
fd="$(printf "%s" "$fd" | sed 's/\.$//')" |
|||
|
|||
best="" |
|||
bestlen=0 |
|||
|
|||
oldifs="$IFS" |
|||
IFS=',' |
|||
for z in $CZ_Zones; do |
|||
z="$(printf "%s" "$z" | sed 's/^ *//; s/ *$//; s/\.$//')" |
|||
[ -z "$z" ] && continue |
|||
|
|||
case "$fd" in |
|||
"$z" | *".$z") |
|||
if [ "${#z}" -gt "$bestlen" ]; then |
|||
best="$z" |
|||
bestlen=${#z} |
|||
fi |
|||
;; |
|||
_fulldomain="$1" |
|||
# Lowercase and remove trailing dot |
|||
_fd=$(printf "%s" "$_fulldomain" | tr '[:upper:]' '[:lower:]' | sed 's/\.$//') |
|||
|
|||
_best_zone="" |
|||
|
|||
# Split zones by comma or space |
|||
_zones_space=$(printf "%s" "$CZ_Zones" | tr ',' ' ') |
|||
|
|||
for _z in $_zones_space; do |
|||
_clean_z=$(printf "%s" "$_z" | tr -d ' ' | tr '[:upper:]' '[:lower:]' | sed 's/\.$//') |
|||
[ -z "$_clean_z" ] && continue |
|||
|
|||
case "$_fd" in |
|||
"$_clean_z"|*".$_clean_z") |
|||
# Find the longest matching zone suffix |
|||
_new_len=$(printf "%s" "$_clean_z" | wc -c) |
|||
_old_len=$(printf "%s" "$_best_zone" | wc -c) |
|||
if [ "$_new_len" -gt "$_old_len" ]; then |
|||
_best_zone="$_clean_z" |
|||
fi |
|||
;; |
|||
esac |
|||
done |
|||
IFS="$oldifs" |
|||
|
|||
[ -z "$best" ] && return 1 |
|||
|
|||
printf "%s" "$best" |
|||
} |
|||
|
|||
_czechia_rel_host() { |
|||
fulldomain="$1" |
|||
zone="$2" |
|||
|
|||
fd="$(_lower_case "$fulldomain")" |
|||
fd="$(printf "%s" "$fd" | sed 's/\.$//')" |
|||
|
|||
z="$(_lower_case "$zone")" |
|||
z="$(printf "%s" "$z" | sed 's/\.$//')" |
|||
|
|||
if [ "$fd" = "$z" ]; then |
|||
printf "%s" "@" |
|||
return 0 |
|||
if [ -z "$_best_zone" ]; then |
|||
return 1 |
|||
fi |
|||
|
|||
suffix=".$z" |
|||
case "$fd" in |
|||
*"$suffix") |
|||
rel="${fd%"$suffix"}" |
|||
[ -z "$rel" ] && rel="@" |
|||
printf "%s" "$rel" |
|||
return 0 |
|||
;; |
|||
esac |
|||
|
|||
return 1 |
|||
} |
|||
|
|||
_czechia_build_body() { |
|||
host="$1" |
|||
txt="$2" |
|||
txt_escaped="$(_czechia_json_escape "$txt")" |
|||
printf "%s" "{\"hostName\":\"$host\",\"text\":\"$txt_escaped\",\"ttl\":$CZ_TTL,\"publishZone\":$CZ_PublishZone}" |
|||
} |
|||
|
|||
_czechia_json_escape() { |
|||
printf "%s" "$1" | sed 's/\\/\\\\/g; s/\"/\\\"/g' |
|||
} |
|||
|
|||
_czechia_api_request() { |
|||
method="$1" |
|||
url="$2" |
|||
body="$3" |
|||
|
|||
_H1="authorizationToken: $CZ_AuthorizationToken" |
|||
_H2="Content-Type: application/json" |
|||
_CURL_TIMEOUT="$CZ_CURL_TIMEOUT" |
|||
|
|||
_post "$body" "$url" "" "$method" "application/json" |
|||
printf "%s" "$_best_zone" |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue