| $label<\/td> | \([^<]\{1,\}\)<\/td><\/tr>/\1/i")
+ printf "%s" "$lookup_result"
+ return 0
+}
+
+# html
+_zyxel_gs1900_get_model() {
+ html="$1"
+ model_name=$(_zyxel_html_table_lookup "$html" "Model Name:")
+ printf "%s" "$model_name"
+}
+
+# html
+_zyxel_gs1900_get_firmware_version() {
+ html="$1"
+ firmware_version=$(_zyxel_html_table_lookup "$html" "Firmware Version:" | _egrep_o "V[^.]+.[^(]+")
+ printf "%s" "$firmware_version"
+}
+
+# version_number
+_zyxel_gs1900_parse_major_version() {
+ printf "%s" "$1" | sed 's/^V\([0-9]\{1,\}\).\{1,\}$/\1/gi'
+}
+
+# version_number
+_zyxel_gs1900_parse_minor_version() {
+ printf "%s" "$1" | sed 's/^.\{1,\}\.\([0-9]\{1,\}\)$/\1/gi'
+}
diff --git a/dnsapi/dns_1984hosting.sh b/dnsapi/dns_1984hosting.sh
index 6accc597..8d9676ac 100755
--- a/dnsapi/dns_1984hosting.sh
+++ b/dnsapi/dns_1984hosting.sh
@@ -1,46 +1,42 @@
#!/usr/bin/env sh
-#This file name is "dns_1984hosting.sh"
-#So, here must be a method dns_1984hosting_add()
-#Which will be called by acme.sh to add the txt record to your api system.
-#returns 0 means success, otherwise error.
-
-#Author: Adrian Fedoreanu
-#Report Bugs here: https://github.com/acmesh-official/acme.sh
-# or here... https://github.com/acmesh-official/acme.sh/issues/2851
-#
-######## Public functions #####################
-
-# Export 1984HOSTING username and password in following variables
-#
-# One984HOSTING_Username=username
-# One984HOSTING_Password=password
-#
-# sessionid cookie is saved in ~/.acme.sh/account.conf
-# username/password need to be set only when changed.
-
-#Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# shellcheck disable=SC2034
+dns_1984hosting_info='1984.hosting
+Domains: 1984.is
+Site: 1984.hosting
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_1984hosting
+Options:
+ One984HOSTING_Username Username
+ One984HOSTING_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2851
+Author: Adrian Fedoreanu
+'
+
+######## Public functions #####################
+
+# Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Add a text record.
dns_1984hosting_add() {
fulldomain=$1
txtvalue=$2
- _info "Add TXT record using 1984Hosting"
+ _info "Add TXT record using 1984Hosting."
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
- _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
+ _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
return 1
fi
- _debug "First detect the root zone"
+ _debug "First detect the root zone."
if ! _get_root "$fulldomain"; then
- _err "invalid domain" "$fulldomain"
+ _err "Invalid domain '$fulldomain'."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- _debug "Add TXT record $fulldomain with value '$txtvalue'"
+ _debug "Add TXT record $fulldomain with value '$txtvalue'."
value="$(printf '%s' "$txtvalue" | _url_encode)"
url="https://1984.hosting/domains/entry/"
@@ -53,102 +49,108 @@ dns_1984hosting_add() {
_debug2 postdata "$postdata"
_authpost "$postdata" "$url"
- response="$(echo "$_response" | _normalizeJson)"
- _debug2 response "$response"
-
- if _contains "$response" '"haserrors": true'; then
- _err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post"
+ if _contains "$_response" '"haserrors": true'; then
+ _err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post."
return 1
- elif _contains "$response" "html>"; then
- _err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file"
+ elif _contains "$_response" "html>"; then
+ _err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file."
return 1
- elif _contains "$response" '"auth": false'; then
- _err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie"
+ elif _contains "$_response" '"auth": false'; then
+ _err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie."
return 1
fi
- _info "Added acme challenge TXT record for $fulldomain at 1984Hosting"
+ _info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
return 0
}
-#Usage: fulldomain txtvalue
-#Remove the txt record after validation.
+# Usage: fulldomain txtvalue
+# Remove the txt record after validation.
dns_1984hosting_rm() {
fulldomain=$1
txtvalue=$2
- _info "Delete TXT record using 1984Hosting"
+ _info "Delete TXT record using 1984Hosting."
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
- _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
+ _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
return 1
fi
- _debug "First detect the root zone"
+ _debug "First detect the root zone."
if ! _get_root "$fulldomain"; then
- _err "invalid domain" "$fulldomain"
+ _err "Invalid domain '$fulldomain'."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- _debug "Delete $fulldomain TXT record"
+ _debug "Delete $fulldomain TXT record."
url="https://1984.hosting/domains"
if ! _get_zone_id "$url" "$_domain"; then
- _err "invalid zone" "$_domain"
+ _err "Invalid zone '$_domain'."
return 1
fi
_htmlget "$url/$_zone_id" "$txtvalue"
- _debug2 _response "$_response"
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
_debug2 entry_id "$entry_id"
if [ -z "$entry_id" ]; then
- _err "Error getting TXT entry_id for $1"
+ _err "Error getting TXT entry_id for $1."
return 1
fi
_authpost "entry=$entry_id" "$url/delentry/"
- response="$(echo "$_response" | _normalizeJson)"
- _debug2 response "$response"
-
- if ! _contains "$response" '"ok": true'; then
- _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
+ if ! _contains "$_response" '"ok": true'; then
+ _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post."
return 1
fi
- _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting"
+ _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
return 0
}
#################### Private functions below ##################################
-
-# usage: _1984hosting_login username password
-# returns 0 success
_1984hosting_login() {
if ! _check_credentials; then return 1; fi
if _check_cookies; then
- _debug "Already logged in"
+ _debug "Already logged in."
return 0
fi
- _debug "Login to 1984Hosting as user $One984HOSTING_Username"
+ _debug "Login to 1984Hosting as user $One984HOSTING_Username."
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
- url="https://1984.hosting/accounts/checkuserauth/"
+ url="https://1984.hosting/api/auth/"
+
+ _get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
+ csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
+ sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
+
+ if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
+ _err "One or more cookies are empty: '$csrftoken', '$sessionid'."
+ return 1
+ fi
+
+ export _H1="Cookie: $csrftoken; $sessionid"
+ export _H2="Referer: https://1984.hosting/accounts/login/"
+ csrf_header=$(echo "$csrftoken" | sed 's/csrftoken=//' | _head_n 1)
+ export _H3="X-CSRFToken: $csrf_header"
response="$(_post "username=$username&password=$password&otpkey=" $url)"
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
if _contains "$response" '"loggedin": true'; then
- One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
+ One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
export One984HOSTING_SESSIONID_COOKIE
export One984HOSTING_CSRFTOKEN_COOKIE
+ _saveaccountconf_mutable One984HOSTING_Username "$One984HOSTING_Username"
+ _saveaccountconf_mutable One984HOSTING_Password "$One984HOSTING_Password"
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
return 0
@@ -157,9 +159,13 @@ _1984hosting_login() {
}
_check_credentials() {
+ One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
+ One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
One984HOSTING_Username=""
One984HOSTING_Password=""
+ _clearaccountconf_mutable One984HOSTING_Username
+ _clearaccountconf_mutable One984HOSTING_Password
_err "You haven't specified 1984Hosting username or password yet."
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
return 1
@@ -171,42 +177,43 @@ _check_cookies() {
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
- _debug "No cached cookie(s) found"
+ _debug "No cached cookie(s) found."
return 1
fi
- _authget "https://1984.hosting/accounts/loginstatus/"
- if _contains "$response" '"ok": true'; then
- _debug "Cached cookies still valid"
+ _authget "https://1984.hosting/api/auth/"
+ if _contains "$_response" '"ok": true'; then
+ _debug "Cached cookies still valid."
return 0
fi
- _debug "Cached cookies no longer valid"
+
+ _debug "Cached cookies no longer valid. Clearing cookies."
One984HOSTING_SESSIONID_COOKIE=""
One984HOSTING_CSRFTOKEN_COOKIE=""
- _saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
- _saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
+ _clearaccountconf_mutable One984HOSTING_SESSIONID_COOKIE
+ _clearaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE
return 1
}
-#_acme-challenge.www.domain.com
-#returns
-# _sub_domain=_acme-challenge.www
-# _domain=domain.com
+# _acme-challenge.www.domain.com
+# Returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
_get_root() {
domain="$1"
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ # not valid
if [ -z "$h" ]; then
- #not valid
return 1
fi
- _authget "https://1984.hosting/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
- if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _authget "https://1984.hosting/domains/zonestatus/$h/?cached=no"
+ if _contains "$_response" '"ok": true'; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
@@ -216,46 +223,46 @@ _get_root() {
return 1
}
-#usage: _get_zone_id url domain.com
-#returns zone id for domain.com
+# Usage: _get_zone_id url domain.com
+# Returns zone id for domain.com
_get_zone_id() {
url=$1
domain=$2
_htmlget "$url" "$domain"
- _debug2 _response "$_response"
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
_debug2 _zone_id "$_zone_id"
if [ -z "$_zone_id" ]; then
- _err "Error getting _zone_id for $2"
+ _err "Error getting _zone_id for $2."
return 1
fi
return 0
}
-# add extra headers to request
+# Add extra headers to request
_authget() {
- export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
+ export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
_response=$(_get "$1" | _normalizeJson)
_debug2 _response "$_response"
}
-# truncate huge HTML response
-# echo: Argument list too long
+# Truncate huge HTML response
_htmlget() {
- export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
+ export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
_response=$(_get "$1" | grep "$2")
if _contains "$_response" "@$2"; then
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
fi
+ _debug2 _response "$_response"
}
-# add extra headers to request
+# Add extra headers to request
_authpost() {
url="https://1984.hosting/domains"
_get_zone_id "$url" "$_domain"
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
- export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
+ export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
export _H3="X-CSRFToken: $csrf_header"
- _response=$(_post "$1" "$2")
+ _response="$(_post "$1" "$2" | _normalizeJson)"
+ _debug2 _response "$_response"
}
diff --git a/dnsapi/dns_acmedns.sh b/dnsapi/dns_acmedns.sh
index 057f9742..f3f50233 100755
--- a/dnsapi/dns_acmedns.sh
+++ b/dnsapi/dns_acmedns.sh
@@ -1,18 +1,18 @@
#!/usr/bin/env sh
-#
-#Author: Wolfgang Ebner
-#Author: Sven Neubuaer
-#Report Bugs here: https://github.com/dampfklon/acme.sh
-#
-# Usage:
-# export ACMEDNS_BASE_URL="https://auth.acme-dns.io"
-#
-# You can optionally define an already existing account:
-#
-# export ACMEDNS_USERNAME=""
-# export ACMEDNS_PASSWORD=""
-# export ACMEDNS_SUBDOMAIN=""
-#
+# shellcheck disable=SC2034
+dns_acmedns_info='acme-dns Server API
+ The acme-dns is a limited DNS server with RESTful API to handle ACME DNS challenges.
+Site: github.com/joohoi/acme-dns
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_acmedns
+Options:
+ ACMEDNS_USERNAME Username. Optional.
+ ACMEDNS_PASSWORD Password. Optional.
+ ACMEDNS_SUBDOMAIN Subdomain. Optional.
+ ACMEDNS_BASE_URL API endpoint. Default: "https://auth.acme-dns.io".
+Issues: github.com/dampfklon/acme.sh
+Author: Wolfgang Ebner, Sven Neubuaer
+'
+
######## Public functions #####################
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh
old mode 100644
new mode 100755
index 9d5533f9..a699f645
--- a/dnsapi/dns_acmeproxy.sh
+++ b/dnsapi/dns_acmeproxy.sh
@@ -1,9 +1,17 @@
#!/usr/bin/env sh
-
-## Acmeproxy DNS provider to be used with acmeproxy (https://github.com/mdbraber/acmeproxy)
-## API integration by Maarten den Braber
-##
-## Report any bugs via https://github.com/mdbraber/acme.sh
+# shellcheck disable=SC2034
+dns_acmeproxy_info='AcmeProxy Server API
+ AcmeProxy can be used to as a single host in your network to request certificates through a DNS API.
+ Clients can connect with the one AcmeProxy host so you do not need to store DNS API credentials on every single host.
+Site: github.com/mdbraber/acmeproxy
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_acmeproxy
+Options:
+ ACMEPROXY_ENDPOINT API Endpoint
+ ACMEPROXY_USERNAME Username
+ ACMEPROXY_PASSWORD Password
+Issues: github.com/acmesh-official/acme.sh/issues/2251
+Author: Maarten den Braber
+'
dns_acmeproxy_add() {
fulldomain="${1}"
diff --git a/dnsapi/dns_active24.sh b/dnsapi/dns_active24.sh
index 862f734f..0f24c53a 100755
--- a/dnsapi/dns_active24.sh
+++ b/dnsapi/dns_active24.sh
@@ -1,10 +1,17 @@
#!/usr/bin/env sh
-
-#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
-
-ACTIVE24_Api="https://api.active24.com"
-
-######## Public functions #####################
+# shellcheck disable=SC2034
+dns_active24_info='Active24.cz
+Site: Active24.cz
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_active24
+Options:
+ Active24_ApiKey API Key. Called "Identifier" in the Active24 Admin
+ Active24_ApiSecret API Secret. Called "Secret key" in the Active24 Admin
+Issues: github.com/acmesh-official/acme.sh/issues/2059
+'
+
+Active24_Api="https://rest.active24.cz"
+# export Active24_ApiKey=ak48l3h7-ak5d-qn4t-p8gc-b6fs8c3l
+# export Active24_ApiSecret=ajvkeo3y82ndsu2smvxy3o36496dcascksldncsq
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
@@ -15,8 +22,8 @@ dns_active24_add() {
_active24_init
_info "Adding txt record"
- if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
- if _contains "$response" "errors"; then
+ if _active24_rest POST "/v2/service/$_service_id/dns/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
+ if _contains "$response" "error"; then
_err "Add txt record error."
return 1
else
@@ -24,6 +31,7 @@ dns_active24_add() {
return 0
fi
fi
+
_err "Add txt record error."
return 1
}
@@ -37,19 +45,25 @@ dns_active24_rm() {
_active24_init
_debug "Getting txt records"
- _active24_rest GET "dns/$_domain/records/v1"
+ # The API needs to send data in body in order the filter to work
+ # TODO: web can also add content $txtvalue to filter and then get the id from response
+ _active24_rest GET "/v2/service/$_service_id/dns/record" "{\"page\":1,\"descending\":true,\"sortBy\":\"name\",\"rowsPerPage\":100,\"totalRecords\":0,\"filters\":{\"type\":[\"TXT\"],\"name\":\"${_sub_domain}\"}}"
+ #_active24_rest GET "/v2/service/$_service_id/dns/record?rowsPerPage=100"
- if _contains "$response" "errors"; then
+ if _contains "$response" "error"; then
_err "Error"
return 1
fi
- hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-)
+ # Note: it might never be more than one record actually, NEEDS more INVESTIGATION
+ record_ids=$(printf "%s" "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2)
+ _debug2 record_ids "$record_ids"
- for hash_id in $hash_ids; do
- _debug "Removing hash_id" "$hash_id"
- if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
- if _contains "$response" "errors"; then
+ for redord_id in $record_ids; do
+ _debug "Removing record_id" "$redord_id"
+ _debug "txtvalue" "$txtvalue"
+ if _active24_rest DELETE "/v2/service/$_service_id/dns/record/$redord_id" ""; then
+ if _contains "$response" "error"; then
_err "Unable to remove txt record."
return 1
else
@@ -63,23 +77,17 @@ dns_active24_rm() {
return 1
}
-#################### Private functions below ##################################
-#_acme-challenge.www.domain.com
-#returns
-# _sub_domain=_acme-challenge.www
-# _domain=domain.com
-# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
+ i=1
+ p=1
- if ! _active24_rest GET "dns/domains/v1"; then
+ if ! _active24_rest GET "/v1/user/self/service"; then
return 1
fi
- i=2
- p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug "h" "$h"
if [ -z "$h" ]; then
#not valid
@@ -87,7 +95,7 @@ _get_root() {
fi
if _contains "$response" "\"$h\"" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
@@ -97,45 +105,102 @@ _get_root() {
return 1
}
-_active24_rest() {
- m=$1
- ep="$2"
- data="$3"
- _debug "$ep"
-
- export _H1="Authorization: Bearer $ACTIVE24_Token"
-
- if [ "$m" != "GET" ]; then
- _debug "data" "$data"
- response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
- else
- response="$(_get "$ACTIVE24_Api/$ep")"
+_active24_init() {
+ Active24_ApiKey="${Active24_ApiKey:-$(_readaccountconf_mutable Active24_ApiKey)}"
+ Active24_ApiSecret="${Active24_ApiSecret:-$(_readaccountconf_mutable Active24_ApiSecret)}"
+ #Active24_ServiceId="${Active24_ServiceId:-$(_readaccountconf_mutable Active24_ServiceId)}"
+
+ if [ -z "$Active24_ApiKey" ] || [ -z "$Active24_ApiSecret" ]; then
+ Active24_ApiKey=""
+ Active24_ApiSecret=""
+ _err "You don't specify Active24 api key and ApiSecret yet."
+ _err "Please create your key and try again."
+ return 1
fi
- if [ "$?" != "0" ]; then
- _err "error $ep"
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable Active24_ApiKey "$Active24_ApiKey"
+ _saveaccountconf_mutable Active24_ApiSecret "$Active24_ApiSecret"
+
+ _debug "A24 API CHECK"
+ if ! _active24_rest GET "/v2/check"; then
+ _err "A24 API check failed with: $response"
return 1
fi
- _debug2 response "$response"
- return 0
-}
-_active24_init() {
- ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}"
- if [ -z "$ACTIVE24_Token" ]; then
- ACTIVE24_Token=""
- _err "You didn't specify a Active24 api token yet."
- _err "Please create the token and try again."
+ if ! echo "$response" | tr -d " " | grep \"verified\":true >/dev/null; then
+ _err "A24 API check failed with: $response"
return 1
fi
- _saveaccountconf_mutable ACTIVE24_Token "$ACTIVE24_Token"
-
_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"
+ _active24_get_service_id "$_domain"
+ _debug _service_id "$_service_id"
+}
+
+_active24_get_service_id() {
+ _d=$1
+ if ! _active24_rest GET "/v1/user/self/zone/${_d}"; then
+ return 1
+ else
+ response=$(echo "$response" | _json_decode)
+ _service_id=$(echo "$response" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2)
+ fi
+}
+
+_active24_rest() {
+ m=$1
+ ep_qs=$2 # with query string
+ # ep=$2
+ ep=$(printf "%s" "$ep_qs" | cut -d '?' -f1) # no query string
+ data="$3"
+
+ _debug "A24 $ep"
+ _debug "A24 $Active24_ApiKey"
+ _debug "A24 $Active24_ApiSecret"
+
+ timestamp=$(_time)
+ datez=$(date -u +"%Y%m%dT%H%M%SZ")
+ canonicalRequest="${m} ${ep} ${timestamp}"
+ signature=$(printf "%s" "$canonicalRequest" | _hmac sha1 "$(printf "%s" "$Active24_ApiSecret" | _hex_dump | tr -d " ")" hex)
+ authorization64="$(printf "%s:%s" "$Active24_ApiKey" "$signature" | _base64)"
+
+ export _H1="Date: ${datez}"
+ export _H2="Accept: application/json"
+ export _H3="Content-Type: application/json"
+ export _H4="Authorization: Basic ${authorization64}"
+
+ _debug2 H1 "$_H1"
+ _debug2 H2 "$_H2"
+ _debug2 H3 "$_H3"
+ _debug2 H4 "$_H4"
+
+ # _sleep 1
+
+ if [ "$m" != "GET" ]; then
+ _debug2 "${m} $Active24_Api${ep_qs}"
+ _debug "data" "$data"
+ response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")"
+ else
+ if [ -z "$data" ]; then
+ _debug2 "GET $Active24_Api${ep_qs}"
+ response="$(_get "$Active24_Api${ep_qs}")"
+ else
+ _debug2 "GET $Active24_Api${ep_qs} with data: ${data}"
+ response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")"
+ fi
+ fi
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
}
diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh
index fc4a664b..850af5b4 100755
--- a/dnsapi/dns_ad.sh
+++ b/dnsapi/dns_ad.sh
@@ -1,12 +1,13 @@
#!/usr/bin/env sh
-
-#
-#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
-
-#This is the Alwaysdata api wrapper for acme.sh
-#
-#Author: Paul Koppen
-#Report Bugs here: https://github.com/wpk-/acme.sh
+# shellcheck disable=SC2034
+dns_ad_info='AlwaysData.com
+Site: AlwaysData.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ad
+Options:
+ AD_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/pull/503
+Author: Paul Koppen
+'
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
@@ -94,7 +95,7 @@ _get_root() {
if _ad_rest GET "domain/"; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -105,7 +106,7 @@ _get_root() {
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh
index c2105672..53a82f91 100755
--- a/dnsapi/dns_ali.sh
+++ b/dnsapi/dns_ali.sh
@@ -1,27 +1,27 @@
#!/usr/bin/env sh
-
-Ali_API="https://alidns.aliyuncs.com/"
-
-#Ali_Key="LTqIA87hOKdjevsf5"
-#Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
+# shellcheck disable=SC2034
+dns_ali_info='AlibabaCloud.com
+Domains: Aliyun.com
+Site: AlibabaCloud.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ali
+Options:
+ Ali_Key API Key
+ Ali_Secret API Secret
+'
+
+# NOTICE:
+# This file is referenced by Alibaba Cloud Services deploy hooks
+# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
+# Be careful when modifying this file, especially when making breaking changes for common functions
+
+Ali_DNS_API="https://alidns.aliyuncs.com/"
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ali_add() {
fulldomain=$1
txtvalue=$2
- Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
- Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
- if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
- Ali_Key=""
- Ali_Secret=""
- _err "You don't specify aliyun api key and secret yet."
- return 1
- fi
-
- #save the api key and secret to the account conf file.
- _saveaccountconf_mutable Ali_Key "$Ali_Key"
- _saveaccountconf_mutable Ali_Secret "$Ali_Secret"
+ _prepare_ali_credentials || return 1
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -46,14 +46,74 @@ dns_ali_rm() {
_clean
}
-#################### Private functions below ##################################
+#################### Alibaba Cloud common functions below ####################
+
+_prepare_ali_credentials() {
+ Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
+ Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
+ if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
+ Ali_Key=""
+ Ali_Secret=""
+ _err "You don't specify aliyun api key and secret yet."
+ return 1
+ fi
+
+ #save the api key and secret to the account conf file.
+ _saveaccountconf_mutable Ali_Key "$Ali_Key"
+ _saveaccountconf_mutable Ali_Secret "$Ali_Secret"
+}
+
+# act ign mtd
+_ali_rest() {
+ act="$1"
+ ign="$2"
+ mtd="${3:-GET}"
+
+ signature=$(printf "%s" "$mtd&%2F&$(printf "%s" "$query" | _url_encode upper-hex)" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
+ signature=$(printf "%s" "$signature" | _url_encode upper-hex)
+ url="$endpoint?Signature=$signature"
+
+ if [ "$mtd" = "GET" ]; then
+ url="$url&$query"
+ response="$(_get "$url")"
+ else
+ response="$(_post "$query" "$url" "" "$mtd" "application/x-www-form-urlencoded")"
+ fi
+
+ _ret="$?"
+ _debug2 response "$response"
+ if [ "$_ret" != "0" ]; then
+ _err "Error <$act>"
+ return 1
+ fi
+
+ if [ -z "$ign" ]; then
+ message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+ if [ "$message" ]; then
+ _err "$message"
+ return 1
+ fi
+ fi
+}
+
+_ali_nonce() {
+ #_head_n 1 "
- return 1
- fi
-
- _debug2 response "$response"
- if [ -z "$2" ]; then
- message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
- if [ "$message" ]; then
- _err "$message"
- return 1
- fi
- fi
-}
-
-_ali_urlencode() {
- _str="$1"
- _str_len=${#_str}
- _u_i=1
- while [ "$_u_i" -le "$_str_len" ]; do
- _str_c="$(printf "%s" "$_str" | cut -c "$_u_i")"
- case $_str_c in [a-zA-Z0-9.~_-])
- printf "%s" "$_str_c"
- ;;
- *)
- printf "%%%02X" "'$_str_c"
- ;;
- esac
- _u_i="$(_math "$_u_i" + 1)"
- done
-}
-
-_ali_nonce() {
- #_head_n 1
+'
+
+########## API configuration ###################################################
+
+AF_API_SUCCESS='status":"OK'
+AF_URL_DCP='https://dcp.c.artfiles.de/api/'
+AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
+AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
+
+########## Public functions ####################################################
+
+# Adds a new TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_add() {
+ domain="$1"
+ txtValue="$2"
+ _info 'Using ArtFiles.de DNS addition API…'
+ _debug 'Domain' "$domain"
+ _debug 'txtValue' "$txtValue"
+
+ _set_credentials
+ _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
+ _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
+
+ _set_headers
+ _get_zone "$domain"
+ _dns 'GET'
+ if ! _contains "$response" 'TXT'; then
+ _err 'Retrieving TXT records failed.'
+
+ return 1
+ fi
+
+ _clean_records
+ _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
+ if ! _contains "$response" "$AF_API_SUCCESS"; then
+ _err 'Adding ACME challenge value failed.'
+
+ return 1
+ fi
+}
+
+# Removes the existing TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_rm() {
+ domain="$1"
+ txtValue="$2"
+ _info 'Using ArtFiles.de DNS removal API…'
+ _debug 'Domain' "$domain"
+ _debug 'txtValue' "$txtValue"
+
+ _set_credentials
+ _set_headers
+ _get_zone "$domain"
+ if ! _dns 'GET'; then
+ return 1
+ fi
+
+ if ! _contains "$response" "$txtValue"; then
+ _err 'Retrieved TXT records are missing given ACME challenge value.'
+
+ return 1
+ fi
+
+ _clean_records
+ response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')"
+ _dns 'SET' "$response"
+ if ! _contains "$response" "$AF_API_SUCCESS"; then
+ _err 'Removing ACME challenge value failed.'
+
+ return 1
+ fi
+}
+
+########## Private functions ###################################################
+
+# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
+# Usage: _clean_records
+_clean_records() {
+ _info 'Cleaning TXT records…'
+ # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
+ # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
+ # from '\"' & turn '\n' into real LF characters.
+ # Yup, awful API to use - but that's all we got to get this working, so… ;)
+ _debug2 'Raw ' "$response"
+ response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
+ _debug2 'Clean' "$response"
+}
+
+# Executes an HTTP GET or POST request for getting or setting DNS records,
+# containing given payload upon POST.
+# Usage: _dns [GET | SET] [payload]
+_dns() {
+ _info 'Executing HTTP request…'
+ action="$1"
+ payload="$(printf -- '%s' "$2" | _url_encode)"
+ url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
+
+ if [ "$action" = 'SET' ]; then
+ _debug2 'Payload' "$payload"
+ response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
+ else
+ response="$(_get "$url" '' 10)"
+ fi
+
+ if ! _contains "$response" "$AF_API_SUCCESS"; then
+ _err "DNS API error: $response"
+
+ return 1
+ fi
+
+ _debug 'Response' "$response"
+
+ return 0
+}
+
+# Gets the root domain zone for given domain.
+# Usage: _get_zone _acme-challenge.www.example.com
+_get_zone() {
+ fqdn="$1"
+ domains="$(_get "$AF_URL_DOMAINS" '' 10)"
+ _info 'Getting domain zone…'
+ _debug2 'FQDN' "$fqdn"
+ _debug2 'Domains' "$domains"
+
+ while _contains "$fqdn" "."; do
+ if _contains "$domains" "$fqdn"; then
+ domain="$fqdn"
+ _info "Found root domain zone: $domain"
+ break
+ else
+ fqdn="${fqdn#*.}"
+ _debug2 'FQDN' "$fqdn"
+ fi
+ done
+
+ if [ "$domain" = "$fqdn" ]; then
+ return 0
+ fi
+
+ _err 'Couldn'\''t find root domain zone.'
+
+ return 1
+}
+
+# Sets the credentials for accessing ArtFiles's API
+# Usage: _set_credentials
+_set_credentials() {
+ _info 'Setting credentials…'
+ AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
+ AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
+ if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
+ _err 'Missing ArtFiles.de username and/or password.'
+ _err 'Please ensure both are set via export command & try again.'
+
+ return 1
+ fi
+}
+
+# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
+# Usage: _set_headers
+_set_headers() {
+ _info 'Setting headers…'
+ encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
+ export _H1="Authorization: Basic $encoded"
+ export _H2='Content-Type: application/json'
+}
diff --git a/dnsapi/dns_arvan.sh b/dnsapi/dns_arvan.sh
index 4c9217e5..cbe6dc1f 100644
--- a/dnsapi/dns_arvan.sh
+++ b/dnsapi/dns_arvan.sh
@@ -1,11 +1,16 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_arvan_info='ArvanCloud.ir
+Site: ArvanCloud.ir
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_arvan
+Options:
+ Arvan_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/2796
+Author: Vahid Fardi
+'
+
+ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
-#Arvan_Token="Apikey xxxx"
-
-ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
-#Author: Vahid Fardi
-#Report Bugs here: https://github.com/Neilpang/acme.sh
-#
######## Public functions #####################
#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -18,7 +23,7 @@ dns_arvan_add() {
if [ -z "$Arvan_Token" ]; then
_err "You didn't specify \"Arvan_Token\" token yet."
- _err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
+ _err "You can get yours from here https://npanel.arvancloud.ir/profile/api-keys"
return 1
fi
#save the api token to the account conf file.
@@ -40,7 +45,7 @@ dns_arvan_add() {
_info "response id is $response"
_info "Added, OK"
return 0
- elif _contains "$response" "Record Data is Duplicated"; then
+ elif _contains "$response" "Record Data is duplicate"; then
_info "Already exists, OK"
return 0
else
@@ -102,7 +107,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -115,7 +120,7 @@ _get_root() {
if _contains "$response" "\"domain\":\"$h\""; then
_domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
@@ -141,6 +146,7 @@ _arvan_rest() {
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
elif [ "$mtd" = "POST" ]; then
export _H2="Content-Type: application/json"
+ export _H3="Accept: application/json"
_debug data "$data"
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
else
diff --git a/dnsapi/dns_aurora.sh b/dnsapi/dns_aurora.sh
index 00f44739..110ef0fa 100644
--- a/dnsapi/dns_aurora.sh
+++ b/dnsapi/dns_aurora.sh
@@ -1,9 +1,15 @@
#!/usr/bin/env sh
-
-#
-#AURORA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#AURORA_Secret="sdfsdfsdfljlbjkljlkjsdfoiwje"
+# shellcheck disable=SC2034
+dns_aurora_info='versio.nl AuroraDNS
+Domains: pcextreme.nl
+Site: versio.nl
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_aurora
+Options:
+ AURORA_Key API Key
+ AURORA_Secret API Secret
+Issues: github.com/acmesh-official/acme.sh/issues/3459
+Author: Jasper Zonneveld
+'
AURORA_Api="https://api.auroradns.eu"
@@ -111,7 +117,7 @@ _get_root() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -126,7 +132,7 @@ _get_root() {
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_autodns.sh b/dnsapi/dns_autodns.sh
index 92534489..ce566978 100644
--- a/dnsapi/dns_autodns.sh
+++ b/dnsapi/dns_autodns.sh
@@ -1,16 +1,15 @@
#!/usr/bin/env sh
-# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
-
-# This is the InternetX autoDNS xml api wrapper for acme.sh
-# Author: auerswald@gmail.com
-# Created: 2018-01-14
-#
-# export AUTODNS_USER="username"
-# export AUTODNS_PASSWORD="password"
-# export AUTODNS_CONTEXT="context"
-#
-# Usage:
-# acme.sh --issue --dns dns_autodns -d example.com
+# shellcheck disable=SC2034
+dns_autodns_info='InternetX autoDNS
+ InternetX autoDNS XML API
+Site: InternetX.com/autodns/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_autodns
+Options:
+ AUTODNS_USER Username
+ AUTODNS_PASSWORD Password
+ AUTODNS_CONTEXT Context
+Author:
+'
AUTODNS_API="https://gateway.autodns.com"
@@ -111,7 +110,7 @@ _get_autodns_zone() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
@@ -129,7 +128,7 @@ _get_autodns_zone() {
if _contains "$autodns_response" "1" >/dev/null; then
_zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
return 0
fi
diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh
index 50c93260..c88c9d9c 100755
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -1,13 +1,15 @@
#!/usr/bin/env sh
-
-#
-#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#AWS_SECRET_ACCESS_KEY="xxxxxxx"
-
-#This is the Amazon Route53 api wrapper for acme.sh
-#All `_sleep` commands are included to avoid Route53 throttling, see
-#https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
+# shellcheck disable=SC2034
+dns_aws_info='Amazon AWS Route53 domain API
+Site: docs.aws.amazon.com/route53/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_aws
+Options:
+ AWS_ACCESS_KEY_ID API Key ID
+ AWS_SECRET_ACCESS_KEY API Secret
+'
+
+# All `_sleep` commands are included to avoid Route53 throttling, see
+# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
AWS_HOST="route53.amazonaws.com"
AWS_URL="https://$AWS_HOST"
@@ -145,7 +147,6 @@ dns_aws_rm() {
fi
_sleep 1
return 1
-
}
#################### Private functions below ##################################
@@ -157,7 +158,7 @@ _get_root() {
# iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100 | sed 's/\./\\./g')
_debug "Checking domain: $h"
if [ -z "$h" ]; then
_error "invalid domain"
@@ -173,7 +174,7 @@ _get_root() {
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o ".*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
@@ -207,24 +208,40 @@ _use_container_role() {
}
_use_instance_role() {
- _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
- _debug "_url" "$_url"
- if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then
+ _instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
+
+ if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then
+ _debug "Using IMDSv2"
+ _token_url="http://169.254.169.254/latest/api/token"
+ export _H1="X-aws-ec2-metadata-token-ttl-seconds: 21600"
+ _token="$(_post "" "$_token_url" "" "PUT")"
+ _secure_debug3 "_token" "$_token"
+ if [ -z "$_token" ]; then
+ _debug "Unable to fetch IMDSv2 token from instance metadata"
+ return 1
+ fi
+ export _H1="X-aws-ec2-metadata-token: $_token"
+ fi
+
+ if ! _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 200; then
_debug "Unable to fetch IAM role from instance metadata"
return 1
fi
- _aws_role=$(_get "$_url" "" 1)
- _debug "_aws_role" "$_aws_role"
- _use_metadata "$_url$_aws_role"
+
+ _instance_role_name=$(_get "$_instance_role_name_url" "" 1)
+ _debug "_instance_role_name" "$_instance_role_name"
+ _use_metadata "$_instance_role_name_url$_instance_role_name" "$_token"
+
}
_use_metadata() {
+ export _H1="X-aws-ec2-metadata-token: $2"
_aws_creds="$(
_get "$1" "" 1 |
_normalizeJson |
tr '{,}' '\n' |
while read -r _line; do
- _key="$(echo "${_line%%:*}" | tr -d '"')"
+ _key="$(echo "${_line%%:*}" | tr -d '\"')"
_value="${_line#*:}"
_debug3 "_key" "$_key"
_secure_debug3 "_value" "$_value"
diff --git a/dnsapi/dns_azion.sh b/dnsapi/dns_azion.sh
index f215686d..1375e32f 100644
--- a/dnsapi/dns_azion.sh
+++ b/dnsapi/dns_azion.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-#
-#AZION_Email=""
-#AZION_Password=""
-#
+# shellcheck disable=SC2034
+dns_azion_info='Azion.om
+Site: Azion.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_azion
+Options:
+ AZION_Email Email
+ AZION_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/3555
+'
AZION_Api="https://api.azionapi.net"
@@ -96,7 +100,7 @@ _get_root() {
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
# not valid
@@ -107,7 +111,7 @@ _get_root() {
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh
index 1c33c13a..f9d84706 100644
--- a/dnsapi/dns_azure.sh
+++ b/dnsapi/dns_azure.sh
@@ -1,13 +1,25 @@
#!/usr/bin/env sh
-
-WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS"
+# shellcheck disable=SC2034
+dns_azure_info='Azure
+Site: Azure.microsoft.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_azure
+Options:
+ AZUREDNS_SUBSCRIPTIONID Subscription ID
+ AZUREDNS_TENANTID Tenant ID
+ AZUREDNS_APPID App ID. App ID of the service principal
+ AZUREDNS_CLIENTSECRET Client Secret. Secret from creating the service principal
+ AZUREDNS_MANAGEDIDENTITY Use Managed Identity. Use Managed Identity assigned to a resource instead of a service principal. "true"/"false"
+ AZUREDNS_BEARERTOKEN Bearer Token. Used instead of service principal credentials or managed identity. Optional.
+'
+
+wiki=https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
#
-# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
+# Ref: https://learn.microsoft.com/en-us/rest/api/dns/record-sets/create-or-update?view=rest-dns-2018-05-01&tabs=HTTP
#
dns_azure_add() {
@@ -20,6 +32,7 @@ dns_azure_add() {
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
_err "You didn't specify the Azure Subscription ID"
return 1
fi
@@ -34,37 +47,45 @@ dns_azure_add() {
_saveaccountconf_mutable AZUREDNS_TENANTID ""
_saveaccountconf_mutable AZUREDNS_APPID ""
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
+ _saveaccountconf_mutable AZUREDNS_BEARERTOKEN ""
else
- _info "You didn't ask to use Azure managed identity, checking service principal credentials"
+ _info "You didn't ask to use Azure managed identity, checking service principal credentials or provided bearer token"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
+ AZUREDNS_BEARERTOKEN="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
+ if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
+ if [ -z "$AZUREDNS_TENANTID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
+ _err "You didn't specify the Azure Tenant ID "
+ return 1
+ fi
- if [ -z "$AZUREDNS_TENANTID" ]; then
- AZUREDNS_SUBSCRIPTIONID=""
- AZUREDNS_TENANTID=""
- AZUREDNS_APPID=""
- AZUREDNS_CLIENTSECRET=""
- _err "You didn't specify the Azure Tenant ID "
- return 1
- fi
-
- if [ -z "$AZUREDNS_APPID" ]; then
- AZUREDNS_SUBSCRIPTIONID=""
- AZUREDNS_TENANTID=""
- AZUREDNS_APPID=""
- AZUREDNS_CLIENTSECRET=""
- _err "You didn't specify the Azure App ID"
- return 1
- fi
+ if [ -z "$AZUREDNS_APPID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
+ _err "You didn't specify the Azure App ID"
+ return 1
+ fi
- if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
- AZUREDNS_SUBSCRIPTIONID=""
- AZUREDNS_TENANTID=""
- AZUREDNS_APPID=""
- AZUREDNS_CLIENTSECRET=""
- _err "You didn't specify the Azure Client Secret"
- return 1
+ if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
+ _err "You didn't specify the Azure Client Secret"
+ return 1
+ fi
+ else
+ _info "Using provided bearer token"
fi
#save account details to account conf file, don't opt in for azure manages identity check.
@@ -72,9 +93,14 @@ dns_azure_add() {
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
+ _saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$AZUREDNS_BEARERTOKEN"
fi
- accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
+ if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
+ accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
+ else
+ accesstoken=$(echo "$AZUREDNS_BEARERTOKEN" | sed "s/Bearer //g")
+ fi
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
_err "invalid domain"
@@ -124,7 +150,7 @@ dns_azure_add() {
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
#
-# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete
+# Ref: https://learn.microsoft.com/en-us/rest/api/dns/record-sets/delete?view=rest-dns-2018-05-01&tabs=HTTP
#
dns_azure_rm() {
fulldomain=$1
@@ -136,6 +162,7 @@ dns_azure_rm() {
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
_err "You didn't specify the Azure Subscription ID "
return 1
fi
@@ -144,40 +171,51 @@ dns_azure_rm() {
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
_info "Using Azure managed identity"
else
- _info "You didn't ask to use Azure managed identity, checking service principal credentials"
+ _info "You didn't ask to use Azure managed identity, checking service principal credentials or provided bearer token"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
+ AZUREDNS_BEARERTOKEN="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
+ if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
+ if [ -z "$AZUREDNS_TENANTID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
+ _err "You didn't specify the Azure Tenant ID "
+ return 1
+ fi
- if [ -z "$AZUREDNS_TENANTID" ]; then
- AZUREDNS_SUBSCRIPTIONID=""
- AZUREDNS_TENANTID=""
- AZUREDNS_APPID=""
- AZUREDNS_CLIENTSECRET=""
- _err "You didn't specify the Azure Tenant ID "
- return 1
- fi
-
- if [ -z "$AZUREDNS_APPID" ]; then
- AZUREDNS_SUBSCRIPTIONID=""
- AZUREDNS_TENANTID=""
- AZUREDNS_APPID=""
- AZUREDNS_CLIENTSECRET=""
- _err "You didn't specify the Azure App ID"
- return 1
- fi
+ if [ -z "$AZUREDNS_APPID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
+ _err "You didn't specify the Azure App ID"
+ return 1
+ fi
- if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
- AZUREDNS_SUBSCRIPTIONID=""
- AZUREDNS_TENANTID=""
- AZUREDNS_APPID=""
- AZUREDNS_CLIENTSECRET=""
- _err "You didn't specify the Azure Client Secret"
- return 1
+ if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ AZUREDNS_BEARERTOKEN=""
+ _err "You didn't specify the Azure Client Secret"
+ return 1
+ fi
+ else
+ _info "Using provided bearer token"
fi
fi
- accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
+ if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
+ accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
+ else
+ accesstoken=$(echo "$AZUREDNS_BEARERTOKEN" | sed "s/Bearer //g")
+ fi
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
_err "invalid domain"
@@ -256,10 +294,10 @@ _azure_rest() {
if [ "$_code" = "401" ]; then
# we have an invalid access token set to expired
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
- _err "access denied make sure your Azure settings are correct. See $WIKI"
+ _err "Access denied. Invalid access token. Make sure your Azure settings are correct. See: $wiki"
return 1
fi
- # See https://docs.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes
+ # See https://learn.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then
_request_retry_times="$(_math "$_request_retry_times" + 1)"
_info "REST call error $_code retrying $ep in $_request_retry_times s"
@@ -277,14 +315,14 @@ _azure_rest() {
return 0
}
-## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
+## Ref: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#request-an-access-token
_azure_getaccess_token() {
managedIdentity=$1
tenantID=$2
clientID=$3
clientSecret=$4
- accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
+ accesstoken="${AZUREDNS_ACCESSTOKEN:-$(_readaccountconf_mutable AZUREDNS_ACCESSTOKEN)}"
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
# can we reuse the bearer token?
@@ -301,9 +339,18 @@ _azure_getaccess_token() {
_debug "getting new bearer token"
if [ "$managedIdentity" = true ]; then
- # https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
- export _H1="Metadata: true"
- response="$(_get http://169.254.169.254/metadata/identity/oauth2/token\?api-version=2018-02-01\&resource=https://management.azure.com/)"
+ # https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
+ if [ -n "$IDENTITY_ENDPOINT" ]; then
+ # Some Azure environments may set IDENTITY_ENDPOINT (formerly MSI_ENDPOINT) to have an alternative metadata endpoint
+ url="$IDENTITY_ENDPOINT?api-version=2019-08-01&resource=https://management.azure.com/"
+ headers="X-IDENTITY-HEADER: $IDENTITY_HEADER"
+ else
+ url="http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
+ headers="Metadata: true"
+ fi
+
+ export _H1="$headers"
+ response="$(_get "$url")"
response="$(echo "$response" | _normalizeJson)"
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
@@ -321,14 +368,14 @@ _azure_getaccess_token() {
fi
if [ -z "$accesstoken" ]; then
- _err "no acccess token received. Check your Azure settings see $WIKI"
+ _err "No acccess token received. Check your Azure settings. See: $wiki"
return 1
fi
if [ "$_ret" != "0" ]; then
_err "error $response"
return 1
fi
- _saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$accesstoken"
+ _saveaccountconf_mutable AZUREDNS_ACCESSTOKEN "$accesstoken"
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on"
printf "%s" "$accesstoken"
return 0
@@ -341,15 +388,18 @@ _get_root() {
i=1
p=1
- ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
- ## returns up to 100 zones in one response therefore handling more results is not not implemented
- ## (ZoneListResult with continuation token for the next page of results)
- ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways
+ ## Ref: https://learn.microsoft.com/en-us/rest/api/dns/zones/list?view=rest-dns-2018-05-01&tabs=HTTP
+ ## returns up to 100 zones in one response. Handling more results is not implemented
+ ## (ZoneListResult with continuation token for the next page of results)
+ ##
+ ## TODO: handle more than 100 results, as per:
+ ## https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-dns-limits
+ ## The new limit is 250 Public DNS zones per subscription, while the old limit was only 100
##
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken"
# Find matching domain name in Json response
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then
#not valid
@@ -364,7 +414,7 @@ _get_root() {
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
_sub_domain="@"
else
- _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
fi
_domain=$h
return 0
diff --git a/dnsapi/dns_beget.sh b/dnsapi/dns_beget.sh
new file mode 100755
index 00000000..5f3b1eb1
--- /dev/null
+++ b/dnsapi/dns_beget.sh
@@ -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
+'
+
+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":.*}}$'
+}
diff --git a/dnsapi/dns_bookmyname.sh b/dnsapi/dns_bookmyname.sh
new file mode 100644
index 00000000..cf3f1e3e
--- /dev/null
+++ b/dnsapi/dns_bookmyname.sh
@@ -0,0 +1,88 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_bookmyname_info='BookMyName.com
+Site: BookMyName.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bookmyname
+Options:
+ BOOKMYNAME_USERNAME Username
+ BOOKMYNAME_PASSWORD Password
+Issues: github.com/acmesh-official/acme.sh/issues/3209
+Author: @Neilpang
+'
+
+######## Public functions #####################
+
+# BookMyName urls:
+# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=add&value="XXXXXXXX"'
+# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=remove&value="XXXXXXXX"'
+
+# Output:
+#good: update done, cid 123456, domain id 456789, type txt, ip XXXXXXXX
+#good: remove done 1, cid 123456, domain id 456789, ttl 300, type txt, ip XXXXXXXX
+
+# Be careful, BMN DNS servers can be slow to pick up changes; using dnssleep is thus advised.
+
+# Usage:
+# export BOOKMYNAME_USERNAME="ABCDE-FREE"
+# export BOOKMYNAME_PASSWORD="MyPassword"
+# /usr/local/ssl/acme.sh/acme.sh --dns dns_bookmyname --dnssleep 600 --issue -d domain.tld
+
+#Usage: dns_bookmyname_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_bookmyname_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using bookmyname"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
+ BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
+
+ if [ -z "$BOOKMYNAME_USERNAME" ] || [ -z "$BOOKMYNAME_PASSWORD" ]; then
+ BOOKMYNAME_USERNAME=""
+ BOOKMYNAME_PASSWORD=""
+ _err "You didn't specify BookMyName username and password yet."
+ _err "Please specify them and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable BOOKMYNAME_USERNAME "$BOOKMYNAME_USERNAME"
+ _saveaccountconf_mutable BOOKMYNAME_PASSWORD "$BOOKMYNAME_PASSWORD"
+
+ uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
+ data="?hostname=${fulldomain}&type=TXT&ttl=300&do=add&value=${txtvalue}"
+ result="$(_get "${uri}${data}")"
+ _debug "Result: $result"
+
+ if ! _startswith "$result" 'good: update done, cid '; then
+ _err "Can't add $fulldomain"
+ return 1
+ fi
+
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_bookmyname_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using bookmyname"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
+ BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
+
+ uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
+ data="?hostname=${fulldomain}&type=TXT&ttl=300&do=remove&value=${txtvalue}"
+ result="$(_get "${uri}${data}")"
+ _debug "Result: $result"
+
+ if ! _startswith "$result" 'good: remove done 1, cid '; then
+ _info "Can't remove $fulldomain"
+ fi
+
+}
+
+#################### Private functions below ##################################
diff --git a/dnsapi/dns_bunny.sh b/dnsapi/dns_bunny.sh
index a9b1ea5a..780198e1 100644
--- a/dnsapi/dns_bunny.sh
+++ b/dnsapi/dns_bunny.sh
@@ -1,16 +1,13 @@
#!/usr/bin/env sh
-
-## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
-## returns 0 means success, otherwise error.
-
-## Author: nosilver4u
-## GitHub: https://github.com/nosilver4u/acme.sh
-
-##
-## Environment Variables Required:
-##
-## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
-##
+# shellcheck disable=SC2034
+dns_bunny_info='Bunny.net
+Site: Bunny.net/dns/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bunny
+Options:
+ BUNNY_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/4296
+Author:
+'
##################### Public functions #####################
@@ -199,7 +196,7 @@ _get_base_domain() {
_debug2 domain_list "$domain_list"
i=1
- while [ $i -gt 0 ]; do
+ while [ "$i" -gt 0 ]; do
## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
## check we got something back from our cut (or are we at the end)
@@ -211,7 +208,7 @@ _get_base_domain() {
## check if it exists
if [ -n "$found" ]; then
## exists - exit loop returning the parts
- sub_point=$(_math $i - 1)
+ sub_point=$(_math "$i" - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
_domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug _domain_id "$_domain_id"
@@ -221,11 +218,11 @@ _get_base_domain() {
return 0
fi
## increment cut point $i
- i=$(_math $i + 1)
+ i=$(_math "$i" + 1)
done
if [ -z "$found" ]; then
- page=$(_math $page + 1)
+ page=$(_math "$page" + 1)
nextpage="https://api.bunny.net/dnszone?page=$page"
## Find the next page if we don't have a match.
hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh
index cd8d9a8d..736742f3 100755
--- a/dnsapi/dns_cf.sh
+++ b/dnsapi/dns_cf.sh
@@ -1,13 +1,16 @@
#!/usr/bin/env sh
-
-#
-#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#CF_Email="xxxx@sss.com"
-
-#CF_Token="xxxx"
-#CF_Account_ID="xxxx"
-#CF_Zone_ID="xxxx"
+# shellcheck disable=SC2034
+dns_cf_info='CloudFlare
+Site: CloudFlare.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf
+Options:
+ CF_Key API Key
+ CF_Email Your account email
+OptionsAlt:
+ CF_Token API Token
+ CF_Account_ID Account ID
+ CF_Zone_ID Zone ID. Optional.
+'
CF_Api="https://api.cloudflare.com/client/v4"
@@ -183,7 +186,7 @@ _get_root() {
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -203,7 +206,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_clouddns.sh b/dnsapi/dns_clouddns.sh
index 31ae4ee9..b78d70a4 100755
--- a/dnsapi/dns_clouddns.sh
+++ b/dnsapi/dns_clouddns.sh
@@ -1,10 +1,15 @@
#!/usr/bin/env sh
-
-# Author: Radek Sprta
-
-#CLOUDDNS_EMAIL=XXXXX
-#CLOUDDNS_PASSWORD="YYYYYYYYY"
-#CLOUDDNS_CLIENT_ID=XXXXX
+# shellcheck disable=SC2034
+dns_clouddns_info='vshosting.cz CloudDNS
+Site: github.com/vshosting/clouddns
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_clouddns
+Options:
+ CLOUDDNS_EMAIL Email
+ CLOUDDNS_PASSWORD Password
+ CLOUDDNS_CLIENT_ID Client ID
+Issues: github.com/acmesh-official/acme.sh/issues/2699
+Author: Radek Sprta
+'
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh
index b03fd579..23a219da 100755
--- a/dnsapi/dns_cloudns.sh
+++ b/dnsapi/dns_cloudns.sh
@@ -1,12 +1,15 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_cloudns_info='ClouDNS.net
+Site: ClouDNS.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cloudns
+Options:
+ CLOUDNS_AUTH_ID Regular auth ID
+ CLOUDNS_SUB_AUTH_ID Sub auth ID
+ CLOUDNS_AUTH_PASSWORD Auth Password
+Author: Boyan Peychev
+'
-# Author: Boyan Peychev
-# Repository: https://github.com/ClouDNS/acme.sh/
-# Editor: I Komang Suryadana
-
-#CLOUDNS_AUTH_ID=XXXXX
-#CLOUDNS_SUB_AUTH_ID=XXXXX
-#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
CLOUDNS_API="https://api.cloudns.net"
DOMAIN_TYPE=
DOMAIN_MASTER=
@@ -78,7 +81,7 @@ dns_cloudns_rm() {
return 1
fi
- for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
+ for i in $(echo "$response" | tr '{' "\n" | grep -- "$record"); do
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
if [ -n "$record_id" ]; then
@@ -161,7 +164,7 @@ _dns_cloudns_get_zone_info() {
_dns_cloudns_get_zone_name() {
i=2
while true; do
- zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100)
+ zoneForCheck=$(printf "%s" "$1" | cut -d . -f "$i"-100)
if [ -z "$zoneForCheck" ]; then
return 1
@@ -194,10 +197,11 @@ _dns_cloudns_http_api_call() {
auth_user="auth-id=$CLOUDNS_AUTH_ID"
fi
+ encoded_password=$(echo "$CLOUDNS_AUTH_PASSWORD" | tr -d "\n\r" | _url_encode)
if [ -z "$2" ]; then
- data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD"
+ data="$auth_user&auth-password=$encoded_password"
else
- data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
+ data="$auth_user&auth-password=$encoded_password&$2"
fi
response="$(_get "$CLOUDNS_API/$method?$data")"
diff --git a/dnsapi/dns_cn.sh b/dnsapi/dns_cn.sh
index 38d1f4aa..79698e88 100644
--- a/dnsapi/dns_cn.sh
+++ b/dnsapi/dns_cn.sh
@@ -1,7 +1,14 @@
#!/usr/bin/env sh
-
-# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/).
-# created by 5ll and francis
+# shellcheck disable=SC2034
+dns_cn_info='Core-Networks.de
+Site: beta.api.Core-Networks.de/doc/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cn
+Options:
+ CN_User User
+ CN_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2142
+Author: 5ll, francis
+'
CN_API="https://beta.api.core-networks.de"
@@ -124,7 +131,7 @@ _cn_get_root() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
_debug _H1 "${_H1}"
@@ -142,7 +149,7 @@ _cn_get_root() {
fi
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
else
diff --git a/dnsapi/dns_conoha.sh b/dnsapi/dns_conoha.sh
index ddc32074..ecd56fc8 100755
--- a/dnsapi/dns_conoha.sh
+++ b/dnsapi/dns_conoha.sh
@@ -1,4 +1,15 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_conoha_info='ConoHa.jp
+Domains: ConoHa.io
+Site: ConoHa.jp
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_conoha
+Options:
+ CONOHA_Username Username
+ CONOHA_Password Password
+ CONOHA_TenantId TenantId
+ CONOHA_IdentityServiceApi Identity Service API. E.g. "https://identity.xxxx.conoha.io/v2.0"
+'
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
@@ -226,7 +237,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100).
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100).
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -240,7 +251,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_constellix.sh b/dnsapi/dns_constellix.sh
index 69d216f0..6a50e199 100644
--- a/dnsapi/dns_constellix.sh
+++ b/dnsapi/dns_constellix.sh
@@ -1,10 +1,16 @@
#!/usr/bin/env sh
-
-# Author: Wout Decre
+# shellcheck disable=SC2034
+dns_constellix_info='Constellix.com
+Site: Constellix.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_constellix
+Options:
+ CONSTELLIX_Key API Key
+ CONSTELLIX_Secret API Secret
+Issues: github.com/acmesh-official/acme.sh/issues/2724
+Author: Wout Decre
+'
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
-#CONSTELLIX_Key="XXX"
-#CONSTELLIX_Secret="XXX"
######## Public functions #####################
@@ -116,7 +122,7 @@ _get_root() {
p=1
_debug "Detecting root zone"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
return 1
fi
@@ -128,7 +134,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-"$p")
_domain="$h"
_debug _domain_id "$_domain_id"
diff --git a/dnsapi/dns_cpanel.sh b/dnsapi/dns_cpanel.sh
index f6126bcb..a6991403 100755
--- a/dnsapi/dns_cpanel.sh
+++ b/dnsapi/dns_cpanel.sh
@@ -1,18 +1,18 @@
#!/usr/bin/env sh
-#
-#Author: Bjarne Saltbaek
-#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/3732
-#
-#
+# shellcheck disable=SC2034
+dns_cpanel_info='cPanel Server API
+ Manage DNS via cPanel Dashboard.
+Site: cPanel.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_cpanel
+Options:
+ cPanel_Username Username
+ cPanel_Apitoken API Token
+ cPanel_Hostname Server URL. E.g. "https://hostname:port"
+Issues: github.com/acmesh-official/acme.sh/issues/3732
+Author: Bjarne Saltbaek
+'
+
######## Public functions #####################
-#
-# Export CPANEL username,api token and hostname in the following variables
-#
-# cPanel_Username=username
-# cPanel_Apitoken=apitoken
-# cPanel_Hostname=hostname
-#
-# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_cpanel_add() {
diff --git a/dnsapi/dns_curanet.sh b/dnsapi/dns_curanet.sh
index 4b39f365..f57afa1f 100644
--- a/dnsapi/dns_curanet.sh
+++ b/dnsapi/dns_curanet.sh
@@ -1,9 +1,15 @@
#!/usr/bin/env sh
-
-#Script to use with curanet.dk, scannet.dk, wannafind.dk, dandomain.dk DNS management.
-#Requires api credentials with scope: dns
-#Author: Peter L. Hansen
-#Version 1.0
+# shellcheck disable=SC2034
+dns_curanet_info='Curanet.dk
+Domains: scannet.dk wannafind.dk dandomain.dk
+Site: Curanet.dk
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_curanet
+Options:
+ CURANET_AUTHCLIENTID Auth ClientID. Requires scope dns
+ CURANET_AUTHSECRET Auth Secret
+Issues: github.com/acmesh-official/acme.sh/issues/3933
+Author: Peter L. Hansen
+'
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains"
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token"
@@ -136,7 +142,7 @@ _get_root() {
i=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
diff --git a/dnsapi/dns_cyon.sh b/dnsapi/dns_cyon.sh
index 830e8831..a585e772 100644
--- a/dnsapi/dns_cyon.sh
+++ b/dnsapi/dns_cyon.sh
@@ -1,21 +1,15 @@
#!/usr/bin/env sh
-
-########
-# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
-#
-# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
-#
-# Dependencies:
-# -------------
-# - oathtool (When using 2 Factor Authentication)
-#
-# Issues:
-# -------
-# Any issues / questions / suggestions can be posted here:
-# https://github.com/noplanman/cyon-api/issues
-#
-# Author: Armando Lüscher
-########
+# shellcheck disable=SC2034
+dns_cyon_info='cyon.ch
+Site: cyon.ch
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cyon
+Options:
+ CY_Username Username
+ CY_Password API Token
+ CY_OTP_Secret OTP token. Only required if using 2FA
+Issues: github.com/noplanman/cyon-api/issues
+Author: Armando Lüscher
+'
dns_cyon_add() {
_cyon_load_credentials &&
@@ -221,10 +215,8 @@ _cyon_change_domain_env() {
if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
- domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
-
# Bail if domain environment change fails.
- if [ "${domain_env_success}" != "true" ]; then
+ if [ "$(printf "%s" "${domain_env_response}" | _cyon_get_environment_change_status)" != "true" ]; then
_err " $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
_err ""
return 1
@@ -238,7 +230,7 @@ _cyon_add_txt() {
_info " - Adding DNS TXT entry..."
add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
- add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
+ add_txt_data="name=${fulldomain_idn}.&ttl=900&type=TXT&dnscontent=${txtvalue}"
add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
_debug add_txt_response "${add_txt_response}"
@@ -247,9 +239,10 @@ _cyon_add_txt() {
add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
+ add_txt_validation="$(printf "%s" "${add_txt_response}" | _cyon_get_validation_status)"
# Bail if adding TXT entry fails.
- if [ "${add_txt_status}" != "true" ]; then
+ if [ "${add_txt_status}" != "true" ] || [ "${add_txt_validation}" != "true" ]; then
_err " ${add_txt_message}"
_err ""
return 1
@@ -311,13 +304,21 @@ _cyon_get_response_message() {
}
_cyon_get_response_status() {
- _egrep_o '"status":\w*' | cut -d : -f 2
+ _egrep_o '"status":[a-zA-z0-9]*' | cut -d : -f 2
+}
+
+_cyon_get_validation_status() {
+ _egrep_o '"valid":[a-zA-z0-9]*' | cut -d : -f 2
}
_cyon_get_response_success() {
_egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
}
+_cyon_get_environment_change_status() {
+ _egrep_o '"authenticated":[a-zA-z0-9]*' | cut -d : -f 2
+}
+
_cyon_check_if_2fa_missed() {
# Did we miss the 2FA?
if test "${1#*multi_factor_form}" != "${1}"; then
diff --git a/dnsapi/dns_da.sh b/dnsapi/dns_da.sh
index 4d3e09b1..36251b05 100755
--- a/dnsapi/dns_da.sh
+++ b/dnsapi/dns_da.sh
@@ -1,31 +1,14 @@
#!/usr/bin/env sh
-# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
-# vim: et ts=2 sw=2
-#
-# DirectAdmin 1.41.0 API
-# The DirectAdmin interface has it's own Let's encrypt functionality, but this
-# script can be used to generate certificates for names which are not hosted on
-# DirectAdmin
-#
-# User must provide login data and URL to DirectAdmin incl. port.
-# You can create login key, by using the Login Keys function
-# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to
-# - CMD_API_DNS_CONTROL
-# - CMD_API_SHOW_DOMAINS
-#
-# See also https://www.directadmin.com/api.php and
-# https://www.directadmin.com/features.php?id=1298
-#
-# Report bugs to https://github.com/TigerP/acme.sh/issues
-#
-# Values to export:
-# export DA_Api="https://remoteUser:remotePassword@da.example.com:8443"
-# export DA_Api_Insecure=1
-#
-# Set DA_Api_Insecure to 1 for insecure and 0 for secure -> difference is
-# whether ssl cert is checked for validity (0) or whether it is just accepted
-# (1)
-#
+# shellcheck disable=SC2034
+dns_da_info='DirectAdmin Server API
+Site: DirectAdmin.com/api.php
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_da
+Options:
+ DA_Api API Server URL. E.g. "https://remoteUser:remotePassword@da.domain.tld:8443"
+ DA_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
+Issues: github.com/TigerP/acme.sh/issues
+'
+
######## Public functions #####################
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -78,7 +61,7 @@ _get_root() {
# response will contain "list[]=example.com&list[]=example.org"
_da_api CMD_API_SHOW_DOMAINS "" "${domain}"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
# not valid
@@ -86,7 +69,7 @@ _get_root() {
return 1
fi
if _contains "$response" "$h" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh
index b9da33ff..0ac353d4 100644
--- a/dnsapi/dns_ddnss.sh
+++ b/dnsapi/dns_ddnss.sh
@@ -1,16 +1,13 @@
#!/usr/bin/env sh
-
-#Created by RaidenII, to use DuckDNS's API to add/remove text records
-#modified by helbgd @ 03/13/2018 to support ddnss.de
-#modified by mod242 @ 04/24/2018 to support different ddnss domains
-#Please note: the Wildcard Feature must be turned on for the Host record
-#and the checkbox for TXT needs to be enabled
-
-# Pass credentials before "acme.sh --issue --dns dns_ddnss ..."
-# --
-# export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
-# --
-#
+# shellcheck disable=SC2034
+dns_ddnss_info='DDNSS.de
+Site: DDNSS.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ddnss
+Options:
+ DDNSS_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/2230
+Author: @helbgd, @mod242
+'
DDNSS_DNS_API="https://ddnss.de/upd.php"
diff --git a/dnsapi/dns_desec.sh b/dnsapi/dns_desec.sh
index 495a6780..d6b9c355 100644
--- a/dnsapi/dns_desec.sh
+++ b/dnsapi/dns_desec.sh
@@ -1,11 +1,13 @@
#!/usr/bin/env sh
-#
-# deSEC.io Domain API
-#
-# Author: Zheng Qian
-#
-# deSEC API doc
-# https://desec.readthedocs.io/en/latest/
+# shellcheck disable=SC2034
+dns_desec_info='deSEC.io
+Site: desec.readthedocs.io/en/latest/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_desec
+Options:
+ DDNSS_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/2180
+Author: Zheng Qian
+'
REST_API="https://desec.io/api/v1/domains"
@@ -174,7 +176,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -186,7 +188,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_df.sh b/dnsapi/dns_df.sh
index c0499ddf..513e350c 100644
--- a/dnsapi/dns_df.sh
+++ b/dnsapi/dns_df.sh
@@ -1,18 +1,15 @@
#!/usr/bin/env sh
-
-########################################################################
-# https://dyndnsfree.de hook script for acme.sh
-#
-# Environment variables:
-#
-# - $DF_user (your dyndnsfree.de username)
-# - $DF_password (your dyndnsfree.de password)
-#
-# Author: Thilo Gass
-# Git repo: https://github.com/ThiloGa/acme.sh
-
-#-- dns_df_add() - Add TXT record --------------------------------------
-# Usage: dns_df_add _acme-challenge.subdomain.domain.com "XyZ123..."
+# shellcheck disable=SC2034
+dns_df_info='DynDnsFree.de
+Domains: dynup.de
+Site: DynDnsFree.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_df
+Options:
+ DF_user Username
+ DF_password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2897
+Author: Thilo Gass
+'
dyndnsfree_api="https://dynup.de/acme.php"
diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh
index afe1b32e..cb887cfa 100755
--- a/dnsapi/dns_dgon.sh
+++ b/dnsapi/dns_dgon.sh
@@ -1,16 +1,12 @@
#!/usr/bin/env sh
-
-## Will be called by acme.sh to add the txt record to your api system.
-## returns 0 means success, otherwise error.
-
-## Author: thewer
-## GitHub: https://github.com/gitwer/acme.sh
-
-##
-## Environment Variables Required:
-##
-## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
-##
+# shellcheck disable=SC2034
+dns_dgon_info='DigitalOcean.com
+Site: DigitalOcean.com/help/api/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dgon
+Options:
+ DO_API_KEY API Key
+Author:
+'
##################### Public functions #####################
@@ -207,7 +203,7 @@ _get_base_domain() {
_debug2 domain_list "$domain_list"
i=1
- while [ $i -gt 0 ]; do
+ while [ "$i" -gt 0 ]; do
## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
## check we got something back from our cut (or are we at the end)
@@ -219,14 +215,14 @@ _get_base_domain() {
## check if it exists
if [ -n "$found" ]; then
## exists - exit loop returning the parts
- sub_point=$(_math $i - 1)
+ sub_point=$(_math "$i" - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
_debug _domain "$_domain"
_debug _sub_domain "$_sub_domain"
return 0
fi
## increment cut point $i
- i=$(_math $i + 1)
+ i=$(_math "$i" + 1)
done
if [ -z "$found" ]; then
diff --git a/dnsapi/dns_dnsexit.sh b/dnsapi/dns_dnsexit.sh
new file mode 100644
index 00000000..ec3b07a4
--- /dev/null
+++ b/dnsapi/dns_dnsexit.sh
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_dnsexit_info='DNSExit.com
+Site: DNSExit.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnsexit
+Options:
+ DNSEXIT_API_KEY API Key
+ DNSEXIT_AUTH_USER Username
+ DNSEXIT_AUTH_PASS Password
+Issues: github.com/acmesh-official/acme.sh/issues/4719
+Author: Samuel Jimenez
+'
+
+DNSEXIT_API_URL="https://api.dnsexit.com/dns/"
+DNSEXIT_HOSTS_URL="https://update.dnsexit.com/ipupdate/hosts.jsp"
+
+######## Public functions #####################
+#Usage: dns_dnsexit_add _acme-challenge.*.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dnsexit_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using DNSExit.com"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _debug 'Load account auth'
+ if ! get_account_info; then
+ return 1
+ fi
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"add\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":0,\"overwrite\":false}}"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_dnsexit_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using DNSExit.com"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _debug 'Load account auth'
+ if ! get_account_info; then
+ return 1
+ fi
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ _err "$response"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"delete\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\"}}"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ while true; do
+ _domain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug h "$_domain"
+ if [ -z "$_domain" ]; then
+ return 1
+ fi
+
+ _debug login "$DNSEXIT_AUTH_USER"
+ _debug password "$DNSEXIT_AUTH_PASS"
+ _debug domain "$_domain"
+
+ _dnsexit_http "login=$DNSEXIT_AUTH_USER&password=$DNSEXIT_AUTH_PASS&domain=$_domain"
+
+ if _contains "$response" "0=$_domain"; then
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+ return 0
+ else
+ _debug "Go to next level of $_domain"
+ fi
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_dnsexit_rest() {
+ m=POST
+ ep=""
+ data="$1"
+ _debug _dnsexit_rest "$ep"
+ _debug data "$data"
+
+ api_key_trimmed=$(echo "$DNSEXIT_API_KEY" | tr -d '"')
+
+ export _H1="apikey: $api_key_trimmed"
+ export _H2='Content-Type: application/json'
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$DNSEXIT_API_URL/$ep" "" "$m")"
+ else
+ response="$(_get "$DNSEXIT_API_URL/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "Error $ep"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
+
+_dnsexit_http() {
+ m=GET
+ param="$1"
+ _debug param "$param"
+ _debug get "$DNSEXIT_HOSTS_URL?$param"
+
+ response="$(_get "$DNSEXIT_HOSTS_URL?$param")"
+
+ _debug response "$response"
+
+ if [ "$?" != "0" ]; then
+ _err "Error $param"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
+
+get_account_info() {
+
+ DNSEXIT_API_KEY="${DNSEXIT_API_KEY:-$(_readaccountconf_mutable DNSEXIT_API_KEY)}"
+ if test -z "$DNSEXIT_API_KEY"; then
+ DNSEXIT_API_KEY=''
+ _err 'DNSEXIT_API_KEY was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable DNSEXIT_API_KEY "$DNSEXIT_API_KEY"
+
+ DNSEXIT_AUTH_USER="${DNSEXIT_AUTH_USER:-$(_readaccountconf_mutable DNSEXIT_AUTH_USER)}"
+ if test -z "$DNSEXIT_AUTH_USER"; then
+ DNSEXIT_AUTH_USER=""
+ _err 'DNSEXIT_AUTH_USER was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable DNSEXIT_AUTH_USER "$DNSEXIT_AUTH_USER"
+
+ DNSEXIT_AUTH_PASS="${DNSEXIT_AUTH_PASS:-$(_readaccountconf_mutable DNSEXIT_AUTH_PASS)}"
+ if test -z "$DNSEXIT_AUTH_PASS"; then
+ DNSEXIT_AUTH_PASS=""
+ _err 'DNSEXIT_AUTH_PASS was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable DNSEXIT_AUTH_PASS "$DNSEXIT_AUTH_PASS"
+
+ return 0
+}
diff --git a/dnsapi/dns_dnshome.sh b/dnsapi/dns_dnshome.sh
index 99608769..6d583246 100755
--- a/dnsapi/dns_dnshome.sh
+++ b/dnsapi/dns_dnshome.sh
@@ -1,15 +1,14 @@
#!/usr/bin/env sh
-
-# dnsHome.de API for acme.sh
-#
-# This Script adds the necessary TXT record to a Subdomain
-#
-# Author dnsHome.de (https://github.com/dnsHome-de)
-#
-# Report Bugs to https://github.com/acmesh-official/acme.sh/issues/3819
-#
-# export DNSHOME_Subdomain=""
-# export DNSHOME_SubdomainPassword=""
+# shellcheck disable=SC2034
+dns_dnshome_info='dnsHome.de
+Site: dnsHome.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnshome
+Options:
+ DNSHOME_Subdomain Subdomain
+ DNSHOME_SubdomainPassword Subdomain Password
+Issues: github.com/acmesh-official/acme.sh/issues/3819
+Author: @dnsHome-de
+'
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
diff --git a/dnsapi/dns_dnsimple.sh b/dnsapi/dns_dnsimple.sh
index d831eb2b..10a3821d 100644
--- a/dnsapi/dns_dnsimple.sh
+++ b/dnsapi/dns_dnsimple.sh
@@ -1,12 +1,12 @@
#!/usr/bin/env sh
-
-# DNSimple domain api
-# https://github.com/pho3nixf1re/acme.sh/issues
-#
-# This is your oauth token which can be acquired on the account page. Please
-# note that this must be an _account_ token and not a _user_ token.
-# https://dnsimple.com/a//account/access_tokens
-# DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
+# shellcheck disable=SC2034
+dns_dnsimple_info='DNSimple.com
+Site: DNSimple.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dnsimple
+Options:
+ DNSimple_OAUTH_TOKEN OAuth Token
+Issues: github.com/pho3nixf1re/acme.sh/issues
+'
DNSimple_API="https://api.dnsimple.com/v2"
@@ -92,7 +92,7 @@ _get_root() {
i=2
previous=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
# not valid
return 1
@@ -105,7 +105,7 @@ _get_root() {
if _contains "$response" 'not found'; then
_debug "$h not found"
else
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$previous")
_domain="$h"
_debug _domain "$_domain"
diff --git a/dnsapi/dns_dnsservices.sh b/dnsapi/dns_dnsservices.sh
index 008153a4..44cc6f45 100755
--- a/dnsapi/dns_dnsservices.sh
+++ b/dnsapi/dns_dnsservices.sh
@@ -1,12 +1,15 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_dnsservices_info='DNS.Services
+Site: DNS.Services
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnsservices
+Options:
+ DnsServices_Username Username
+ DnsServices_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/4152
+Author: Bjarke Bruun
+'
-#This file name is "dns_dnsservices.sh"
-#Script for Danish DNS registra and DNS hosting provider https://dns.services
-
-#Author: Bjarke Bruun
-#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152
-
-# Global variable to connect to the DNS.Services API
DNSServices_API=https://dns.services/api
######## Public functions #####################
diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh
deleted file mode 100755
index 3850890c..00000000
--- a/dnsapi/dns_do.sh
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/env sh
-
-# DNS API for Domain-Offensive / Resellerinterface / Domainrobot
-
-# Report bugs at https://github.com/seidler2547/acme.sh/issues
-
-# set these environment variables to match your customer ID and password:
-# DO_PID="KD-1234567"
-# DO_PW="cdfkjl3n2"
-
-DO_URL="https://soap.resellerinterface.de/"
-
-######## Public functions #####################
-
-#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_do_add() {
- fulldomain=$1
- txtvalue=$2
- if _dns_do_authenticate; then
- _info "Adding TXT record to ${_domain} as ${fulldomain}"
- _dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300
- if _contains "${response}" '>success<'; then
- return 0
- fi
- _err "Could not create resource record, check logs"
- fi
- return 1
-}
-
-#fulldomain
-dns_do_rm() {
- fulldomain=$1
- if _dns_do_authenticate; then
- if _dns_do_list_rrs; then
- _dns_do_had_error=0
- for _rrid in ${_rr_list}; do
- _info "Deleting resource record $_rrid for $_domain"
- _dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}"
- if ! _contains "${response}" '>success<'; then
- _dns_do_had_error=1
- _err "Could not delete resource record for ${_domain}, id ${_rrid}"
- fi
- done
- return $_dns_do_had_error
- fi
- fi
- return 1
-}
-
-#################### Private functions below ##################################
-_dns_do_authenticate() {
- _info "Authenticating as ${DO_PID}"
- _dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}"
- if _contains "${response}" '>success<'; then
- _get_root "$fulldomain"
- _debug "_domain $_domain"
- return 0
- else
- _err "Authentication failed, are DO_PID and DO_PW set correctly?"
- fi
- return 1
-}
-
-_dns_do_list_rrs() {
- _dns_do_soap getRRList origin "${_domain}"
- if ! _contains "${response}" 'SOAP-ENC:Array'; then
- _err "getRRList origin ${_domain} failed"
- return 1
- fi
- _rr_list="$(echo "${response}" |
- tr -d "\n\r\t" |
- sed -e 's/- /\n/g' |
- grep ">$(_regexcape "$fulldomain")" |
- sed -e 's/<\/item>/\n/g' |
- grep '>id[0-9]{1,16}<' |
- tr -d '><')"
- [ "${_rr_list}" ]
-}
-
-_dns_do_soap() {
- func="$1"
- shift
- # put the parameters to xml
- body=""
- while [ "$1" ]; do
- _k="$1"
- shift
- _v="$1"
- shift
- body="$body<$_k>$_v$_k>"
- done
- body="$body"
- _debug2 "SOAP request ${body}"
-
- # build SOAP XML
- _xml='
-
- '"$body"'
-'
-
- # set SOAP headers
- export _H1="SOAPAction: ${DO_URL}#${func}"
-
- if ! response="$(_post "${_xml}" "${DO_URL}")"; then
- _err "Error <$1>"
- return 1
- fi
- _debug2 "SOAP response $response"
-
- # retrieve cookie header
- _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
- export _H2
-
- return 0
-}
-
-_get_root() {
- domain=$1
- i=1
-
- _dns_do_soap getDomainList
- _all_domains="$(echo "${response}" |
- tr -d "\n\r\t " |
- _egrep_o 'domain]+>[^<]+' |
- sed -e 's/^domain<\/key>]*>//g')"
-
- while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
- if [ -z "$h" ]; then
- return 1
- fi
-
- if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
- _domain="$h"
- return 0
- fi
-
- i=$(_math $i + 1)
- done
- _debug "$domain not found"
-
- return 1
-}
-
-_regexcape() {
- echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
-}
diff --git a/dnsapi/dns_doapi.sh b/dnsapi/dns_doapi.sh
index a001d52c..0804f2e6 100755
--- a/dnsapi/dns_doapi.sh
+++ b/dnsapi/dns_doapi.sh
@@ -1,14 +1,16 @@
#!/usr/bin/env sh
-
-# Official Let's Encrypt API for do.de / Domain-Offensive
-#
-# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers
-# This API is also available to private customers/individuals
-#
-# Provide the required LetsEncrypt token like this:
-# DO_LETOKEN="FmD408PdqT1E269gUK57"
-
-DO_API="https://www.do.de/api/letsencrypt"
+# shellcheck disable=SC2034
+dns_doapi_info='Domain-Offensive do.de
+ Official LetsEncrypt API for do.de / Domain-Offensive.
+ This API is also available to private customers/individuals.
+Site: do.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_doapi
+Options:
+ DO_LETOKEN LetsEncrypt Token
+Issues: github.com/acmesh-official/acme.sh/issues/2057
+'
+
+DO_API="https://my.do.de/api/letsencrypt"
######## Public functions #####################
diff --git a/dnsapi/dns_domeneshop.sh b/dnsapi/dns_domeneshop.sh
index 9a3791f4..925ca335 100644
--- a/dnsapi/dns_domeneshop.sh
+++ b/dnsapi/dns_domeneshop.sh
@@ -1,4 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_domeneshop_info='DomeneShop.no
+Site: DomeneShop.no
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_domeneshop
+Options:
+ DOMENESHOP_Token Token
+ DOMENESHOP_Secret Secret
+Issues: github.com/acmesh-official/acme.sh/issues/2457
+'
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
@@ -84,7 +93,7 @@ _get_domainid() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug "h" "$h"
if [ -z "$h" ]; then
#not valid
@@ -93,7 +102,7 @@ _get_domainid() {
if _contains "$response" "\"$h\"" >/dev/null; then
# We have found the domain name.
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
return 0
diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh
index 9b8b7a8b..7bc331e2 100755
--- a/dnsapi/dns_dp.sh
+++ b/dnsapi/dns_dp.sh
@@ -1,10 +1,12 @@
#!/usr/bin/env sh
-
-# Dnspod.cn Domain api
-#
-#DP_Id="1234"
-#
-#DP_Key="sADDsdasdgdsf"
+# shellcheck disable=SC2034
+dns_dp_info='DNSPod.cn
+Site: DNSPod.cn
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dp
+Options:
+ DP_Id Id
+ DP_Key Key
+'
REST_API="https://dnsapi.cn"
@@ -107,7 +109,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -121,7 +123,7 @@ _get_root() {
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_debug _sub_domain "$_sub_domain"
_domain="$h"
_debug _domain "$_domain"
diff --git a/dnsapi/dns_dpi.sh b/dnsapi/dns_dpi.sh
index 2955effd..e8b9b5a5 100755
--- a/dnsapi/dns_dpi.sh
+++ b/dnsapi/dns_dpi.sh
@@ -1,10 +1,12 @@
#!/usr/bin/env sh
-
-# Dnspod.com Domain api
-#
-#DPI_Id="1234"
-#
-#DPI_Key="sADDsdasdgdsf"
+# shellcheck disable=SC2034
+dns_dpi_info='DNSPod.com
+Site: DNSPod.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dpi
+Options:
+ DPI_Id Id
+ DPI_Key Key
+'
REST_API="https://api.dnspod.com"
@@ -107,7 +109,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -121,7 +123,7 @@ _get_root() {
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_debug _sub_domain "$_sub_domain"
_domain="$h"
_debug _domain "$_domain"
diff --git a/dnsapi/dns_dreamhost.sh b/dnsapi/dns_dreamhost.sh
index a4017938..ce4fff87 100644
--- a/dnsapi/dns_dreamhost.sh
+++ b/dnsapi/dns_dreamhost.sh
@@ -1,10 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_dreamhost_info='DreamHost.com
+Site: DreamHost.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dreamhost
+Options:
+ DH_API_KEY API Key
+Issues: github.com/RhinoLance/acme.sh
+Author: RhinoLance
+'
-#Author: RhinoLance
-#Report Bugs here: https://github.com/RhinoLance/acme.sh
-#
-
-#define the api endpoint
DH_API_ENDPOINT="https://api.dreamhost.com/"
querystring=""
diff --git a/dnsapi/dns_duckdns.sh b/dnsapi/dns_duckdns.sh
index d6e1dbdc..33d401b0 100755
--- a/dnsapi/dns_duckdns.sh
+++ b/dnsapi/dns_duckdns.sh
@@ -1,14 +1,12 @@
#!/usr/bin/env sh
-
-#Created by RaidenII, to use DuckDNS's API to add/remove text records
-#06/27/2017
-
-# Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
-# --
-# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
-# --
-#
-# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh
+# shellcheck disable=SC2034
+dns_duckdns_info='DuckDNS.org
+Site: www.DuckDNS.org
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
+Options:
+ DuckDNS_Token API Token
+Author: @RaidenII
+'
DuckDNS_API="https://www.duckdns.org/update"
diff --git a/dnsapi/dns_durabledns.sh b/dnsapi/dns_durabledns.sh
index 677ae24d..d71f0ccb 100644
--- a/dnsapi/dns_durabledns.sh
+++ b/dnsapi/dns_durabledns.sh
@@ -1,7 +1,13 @@
#!/usr/bin/env sh
-
-#DD_API_User="xxxxx"
-#DD_API_Key="xxxxxx"
+# shellcheck disable=SC2034
+dns_durabledns_info='DurableDNS.com
+Site: DurableDNS.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_durabledns
+Options:
+ DD_API_User API User
+ DD_API_Key API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2281
+'
_DD_BASE="https://durabledns.com/services/dns"
@@ -104,7 +110,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -112,7 +118,7 @@ _get_root() {
fi
if _contains "$response" ">$h."; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_dyn.sh b/dnsapi/dns_dyn.sh
index 024e0a38..9b1a97a2 100644
--- a/dnsapi/dns_dyn.sh
+++ b/dnsapi/dns_dyn.sh
@@ -1,10 +1,16 @@
#!/usr/bin/env sh
-#
-# Dyn.com Domain API
-#
-# Author: Gerd Naschenweng
-# https://github.com/magicdude4eva
-#
+# shellcheck disable=SC2034
+dns_dyn_info='Dyn.com
+Domains: dynect.net
+Site: Dyn.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dyn
+Options:
+ DYN_Customer Customer
+ DYN_Username API Username
+ DYN_Password Secret
+Author: Gerd Naschenweng <@magicdude4eva>
+'
+
# Dyn Managed DNS API
# https://help.dyn.com/dns-api-knowledge-base/
#
@@ -20,13 +26,6 @@
# ZoneRemoveNode
# ZonePublish
# --
-#
-# Pass credentials before "acme.sh --issue --dns dns_dyn ..."
-# --
-# export DYN_Customer="customer"
-# export DYN_Username="apiuser"
-# export DYN_Password="secret"
-# --
DYN_API="https://api.dynect.net/REST"
diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh
index 406ef17d..1d1fc311 100644
--- a/dnsapi/dns_dynu.sh
+++ b/dnsapi/dns_dynu.sh
@@ -1,20 +1,21 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_dynu_info='Dynu.com
+Site: Dynu.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dynu
+Options:
+ Dynu_ClientId Client ID
+ Dynu_Secret Secret
+Issues: github.com/shar0119/acme.sh
+Author: Dynu Systems Inc
+'
-#Client ID
-#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
-#
-#Secret
-#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
-#
#Token
Dynu_Token=""
#
#Endpoint
Dynu_EndPoint="https://api.dynu.com/v2"
-#
-#Author: Dynu Systems, Inc.
-#Report Bugs here: https://github.com/shar0119/acme.sh
-#
+
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -125,7 +126,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -139,7 +140,7 @@ _get_root() {
if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
_domain_name=$h
- _node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _node=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
return 0
fi
p=$i
diff --git a/dnsapi/dns_dynv6.sh b/dnsapi/dns_dynv6.sh
index 9efc9aeb..0c9491f8 100644
--- a/dnsapi/dns_dynv6.sh
+++ b/dnsapi/dns_dynv6.sh
@@ -1,16 +1,23 @@
#!/usr/bin/env sh
-#Author StefanAbl
-#Usage specify a private keyfile to use with dynv6 'export KEY="path/to/keyfile"'
-#or use the HTTP REST API by by specifying a token 'export DYNV6_TOKEN="value"
-#if no keyfile is specified, you will be asked if you want to create one in /home/$USER/.ssh/dynv6 and /home/$USER/.ssh/dynv6.pub
+# shellcheck disable=SC2034
+dns_dynv6_info='DynV6.com
+Site: DynV6.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dynv6
+Options:
+ DYNV6_TOKEN REST API token. Get from https://DynV6.com/keys
+OptionsAlt:
+ KEY Path to SSH private key file. E.g. "/root/.ssh/dynv6"
+Issues: github.com/acmesh-official/acme.sh/issues/2702
+Author: @StefanAbl
+'
dynv6_api="https://dynv6.com/api/v2"
######## Public functions #####################
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
#Usage: dns_dynv6_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynv6_add() {
- fulldomain=$1
- txtvalue=$2
+ fulldomain="$(echo "$1" | _lower_case)"
+ txtvalue="$2"
_info "Using dynv6 api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
@@ -36,15 +43,14 @@ dns_dynv6_add() {
_err "Something went wrong! it does not seem like the record was added successfully"
return 1
fi
- return 1
fi
- return 1
+
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_dynv6_rm() {
- fulldomain=$1
- txtvalue=$2
+ fulldomain="$(echo "$1" | _lower_case)"
+ txtvalue="$2"
_info "Using dynv6 API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
@@ -94,8 +100,8 @@ _get_domain() {
_your_hosts="$(echo "$_your_hosts" | awk '/\./ {print $1}')"
for l in $_your_hosts; do
#echo "host: $l"
- if test "${_full_domain#*$l}" != "$_full_domain"; then
- _record="${_full_domain%.$l}"
+ if test "${_full_domain#*"$l"}" != "$_full_domain"; then
+ _record=${_full_domain%."$l"}
_host=$l
_debug "The host is $_host and the record $_record"
return 0
@@ -143,7 +149,7 @@ _dns_dynv6_add_http() {
return 1
fi
_get_zone_name "$_zone_id"
- record="${fulldomain%%.$_zone_name}"
+ record=${fulldomain%%."$_zone_name"}
_set_record TXT "$record" "$txtvalue"
if _contains "$response" "$txtvalue"; then
_info "Successfully added record"
@@ -161,7 +167,7 @@ _dns_dynv6_rm_http() {
return 1
fi
_get_zone_name "$_zone_id"
- record="${fulldomain%%.$_zone_name}"
+ record=${fulldomain%%."$_zone_name"}
_get_record_id "$_zone_id" "$record" "$txtvalue"
_del_record "$_zone_id" "$_record_id"
if [ -z "$response" ]; then
@@ -199,7 +205,7 @@ _get_zone_id() {
return 1
fi
- zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
+ zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep '"id":' | tr -d '"')"
_zone_id="${zone_id#id:}"
_debug "zone id: $_zone_id"
}
diff --git a/dnsapi/dns_easydns.sh b/dnsapi/dns_easydns.sh
index ab47a0bc..423def2b 100644
--- a/dnsapi/dns_easydns.sh
+++ b/dnsapi/dns_easydns.sh
@@ -1,14 +1,17 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_easydns_info='easyDNS.net
+Site: easyDNS.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_easydns
+Options:
+ EASYDNS_Token API Token
+ EASYDNS_Key API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2647
+Author: @Neilpang, wurzelpanzer
+'
-#######################################################
-#
-# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
-#
# API Documentation: https://sandbox.rest.easydns.net:3001/
-#
-# Author: wurzelpanzer [wurzelpanzer@maximolider.net]
-# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2647
-#
+
#################### Public functions #################
#EASYDNS_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
@@ -118,7 +121,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -130,7 +133,7 @@ _get_root() {
fi
if _contains "$response" "\"status\":200"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_edgecenter.sh b/dnsapi/dns_edgecenter.sh
new file mode 100644
index 00000000..8f4ad171
--- /dev/null
+++ b/dnsapi/dns_edgecenter.sh
@@ -0,0 +1,163 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_edgecenter_info='EdgeCenter.ru
+Site: EdgeCenter.ru
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgecenter
+Options:
+ EDGECENTER_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/6313
+Author: Konstantin Ruchev
+'
+
+EDGECENTER_API="https://api.edgecenter.ru"
+DOMAIN_TYPE=
+DOMAIN_MASTER=
+
+######## Public functions #####################
+
+#Usage: dns_edgecenter_add _acme-challenge.www.domain.com "TXT_RECORD_VALUE"
+dns_edgecenter_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Using EdgeCenter DNS API"
+
+ if ! _dns_edgecenter_init_check; then
+ return 1
+ fi
+
+ _debug "Detecting root zone for $fulldomain"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ subdomain="${fulldomain%."$_zone"}"
+ subdomain=${subdomain%.}
+
+ _debug "Zone: $_zone"
+ _debug "Subdomain: $subdomain"
+ _debug "TXT value: $txtvalue"
+
+ payload='{"resource_records": [ { "content": ["'"$txtvalue"'"] } ], "ttl": 60 }'
+ _dns_edgecenter_http_api_call "post" "dns/v2/zones/$_zone/$subdomain.$_zone/txt" "$payload"
+
+ if _contains "$response" '"error":"rrset is already exists"'; then
+ _debug "RRSet exists, merging values"
+ _dns_edgecenter_http_api_call "get" "dns/v2/zones/$_zone/$subdomain.$_zone/txt"
+ current="$response"
+ newlist=""
+ for v in $(echo "$current" | sed -n 's/.*"content":\["\([^"]*\)"\].*/\1/p'); do
+ newlist="$newlist {\"content\":[\"$v\"]},"
+ done
+ newlist="$newlist{\"content\":[\"$txtvalue\"]}"
+ putdata="{\"resource_records\":[${newlist}]}
+"
+ _dns_edgecenter_http_api_call "put" "dns/v2/zones/$_zone/$subdomain.$_zone/txt" "$putdata"
+ _info "Updated existing RRSet with new TXT value."
+ return 0
+ fi
+
+ if _contains "$response" '"exception":'; then
+ _err "Record cannot be added."
+ return 1
+ fi
+
+ _info "TXT record added successfully."
+ return 0
+}
+
+#Usage: dns_edgecenter_rm _acme-challenge.www.domain.com "TXT_RECORD_VALUE"
+dns_edgecenter_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Removing TXT record for $fulldomain"
+
+ if ! _dns_edgecenter_init_check; then
+ return 1
+ fi
+
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ subdomain="${fulldomain%."$_zone"}"
+ subdomain=${subdomain%.}
+
+ _dns_edgecenter_http_api_call "delete" "dns/v2/zones/$_zone/$subdomain.$_zone/txt"
+
+ if [ -z "$response" ]; then
+ _info "TXT record deleted successfully."
+ else
+ _info "TXT record may not have been deleted: $response"
+ fi
+ return 0
+}
+
+#################### Private functions below ##################################
+
+_dns_edgecenter_init_check() {
+ EDGECENTER_API_KEY="${EDGECENTER_API_KEY:-$(_readaccountconf_mutable EDGECENTER_API_KEY)}"
+ if [ -z "$EDGECENTER_API_KEY" ]; then
+ _err "EDGECENTER_API_KEY was not exported."
+ return 1
+ fi
+
+ _saveaccountconf_mutable EDGECENTER_API_KEY "$EDGECENTER_API_KEY"
+ export _H1="Authorization: APIKey $EDGECENTER_API_KEY"
+
+ _dns_edgecenter_http_api_call "get" "dns/v2/clients/me/features"
+ if ! _contains "$response" '"id":'; then
+ _err "Invalid API key."
+ return 1
+ fi
+ return 0
+}
+
+_get_root() {
+ domain="$1"
+ i=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-)
+ if [ -z "$h" ]; then
+ return 1
+ fi
+ _dns_edgecenter_http_api_call "get" "dns/v2/zones/$h"
+ if ! _contains "$response" 'zone is not found'; then
+ _zone="$h"
+ return 0
+ fi
+ i=$((i + 1))
+ done
+ return 1
+}
+
+_dns_edgecenter_http_api_call() {
+ mtd="$1"
+ endpoint="$2"
+ data="$3"
+
+ export _H1="Authorization: APIKey $EDGECENTER_API_KEY"
+
+ case "$mtd" in
+ get)
+ response="$(_get "$EDGECENTER_API/$endpoint")"
+ ;;
+ post)
+ response="$(_post "$data" "$EDGECENTER_API/$endpoint")"
+ ;;
+ delete)
+ response="$(_post "" "$EDGECENTER_API/$endpoint" "" "DELETE")"
+ ;;
+ put)
+ response="$(_post "$data" "$EDGECENTER_API/$endpoint" "" "PUT")"
+ ;;
+ *)
+ _err "Unknown HTTP method $mtd"
+ return 1
+ ;;
+ esac
+
+ _debug "HTTP $mtd response: $response"
+ return 0
+}
diff --git a/dnsapi/dns_edgedns.sh b/dnsapi/dns_edgedns.sh
index 11c132fa..e88a1483 100755
--- a/dnsapi/dns_edgedns.sh
+++ b/dnsapi/dns_edgedns.sh
@@ -1,4 +1,15 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_edgedns_info='Akamai.com Edge DNS
+Site: techdocs.Akamai.com/edge-dns/reference/edge-dns-api
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgedns
+Options: Specify individual credentials
+ AKAMAI_HOST Host
+ AKAMAI_ACCESS_TOKEN Access token
+ AKAMAI_CLIENT_TOKEN Client token
+ AKAMAI_CLIENT_SECRET Client secret
+Issues: github.com/acmesh-official/acme.sh/issues/3157
+'
# Akamai Edge DNS v2 API
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
@@ -6,18 +17,10 @@
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
-# Values to export:
-# --EITHER--
# *** TBD. NOT IMPLEMENTED YET ***
-# specify Edgegrid credentials file and section
-# AKAMAI_EDGERC=
-# AKAMAI_EDGERC_SECTION="default"
-## --OR--
-# specify indiviual credentials
-# export AKAMAI_HOST =
-# export AKAMAI_ACCESS_TOKEN =
-# export AKAMAI_CLIENT_TOKEN =
-# export AKAMAI_CLIENT_SECRET =
+# Specify Edgegrid credentials file and section.
+# AKAMAI_EDGERC Edge RC. Full file path
+# AKAMAI_EDGERC_SECTION Edge RC Section. E.g. "default"
ACME_EDGEDNS_VERSION="0.1.0"
@@ -418,7 +421,7 @@ _edgedns_make_data_to_sign() {
_secure_debug2 "hdr" "$hdr"
_edgedns_make_content_hash
path="$(echo "$_request_url_path" | tr -d "\n\r" | sed 's/https\?:\/\///')"
- path="${path#*$AKAMAI_HOST}"
+ path=${path#*"$AKAMAI_HOST"}
_debug "hier path" "$path"
# dont expose headers to sign so use MT string
_mdata="$(printf "%s\thttps\t%s\t%s\t%s\t%s\t%s" "$_request_method" "$AKAMAI_HOST" "$path" "" "$_hash" "$hdr")"
diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh
index cfb4b814..744f6ca6 100644
--- a/dnsapi/dns_euserv.sh
+++ b/dnsapi/dns_euserv.sh
@@ -1,18 +1,14 @@
#!/usr/bin/env sh
-
-#This is the euserv.eu api wrapper for acme.sh
-#
-#Author: Michael Brueckner
-#Report Bugs: https://www.github.com/initit/acme.sh or mbr@initit.de
-
-#
-#EUSERV_Username="username"
-#
-#EUSERV_Password="password"
-#
-# Dependencies:
-# -------------
-# - none -
+# shellcheck disable=SC2034
+dns_euserv_info='EUserv.com
+Domains: EUserv.eu
+Site: EUserv.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_euserv
+Options:
+ EUSERV_Username Username
+ EUSERV_Password Password
+Author: Michael Brueckner
+'
EUSERV_Api="https://api.euserv.net"
@@ -155,7 +151,7 @@ _get_root() {
response="$_euserv_domain_orders"
while true; do
- h=$(echo "$domain" | cut -d . -f $i-100)
+ h=$(echo "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -163,7 +159,7 @@ _get_root() {
fi
if _contains "$response" "$h"; then
- _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
_domain="$h"
if ! _euserv_get_domain_id "$_domain"; then
_err "invalid domain"
diff --git a/dnsapi/dns_exoscale.sh b/dnsapi/dns_exoscale.sh
index ccf05fc5..6898ce38 100755
--- a/dnsapi/dns_exoscale.sh
+++ b/dnsapi/dns_exoscale.sh
@@ -1,4 +1,12 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_exoscale_info='Exoscale.com
+Site: Exoscale.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_exoscale
+Options:
+ EXOSCALE_API_KEY API Key
+ EXOSCALE_SECRET_KEY API Secret key
+'
EXOSCALE_API=https://api.exoscale.com/dns/v1
@@ -111,7 +119,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -122,7 +130,7 @@ _get_root() {
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
_domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_token" ] && [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_fornex.sh b/dnsapi/dns_fornex.sh
index 53be307a..dcaa2297 100644
--- a/dnsapi/dns_fornex.sh
+++ b/dnsapi/dns_fornex.sh
@@ -1,8 +1,15 @@
#!/usr/bin/env sh
-
-#Author: Timur Umarov
-
-FORNEX_API_URL="https://fornex.com/api/dns/v0.1"
+# shellcheck disable=SC2034
+dns_fornex_info='Fornex.com
+Site: Fornex.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_fornex
+Options:
+ FORNEX_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/3998
+Author: Timur Umarov
+'
+
+FORNEX_API_URL="https://fornex.com/api"
######## Public functions #####################
@@ -23,12 +30,10 @@ dns_fornex_add() {
fi
_info "Adding record"
- if _rest POST "$_domain/entry_set/add/" "host=$fulldomain&type=TXT&value=$txtvalue&apikey=$FORNEX_API_KEY"; then
+ if _rest POST "dns/domain/$_domain/entry_set/" "{\"host\" : \"${fulldomain}\" , \"type\" : \"TXT\" , \"value\" : \"${txtvalue}\" , \"ttl\" : null}"; then
_debug _response "$response"
- if _contains "$response" '"ok": true' || _contains "$response" 'Такая запись уже существует.'; then
- _info "Added, OK"
- return 0
- fi
+ _info "Added, OK"
+ return 0
fi
_err "Add txt record error."
return 1
@@ -51,21 +56,21 @@ dns_fornex_rm() {
fi
_debug "Getting txt records"
- _rest GET "$_domain/entry_set.json?apikey=$FORNEX_API_KEY"
+ _rest GET "dns/domain/$_domain/entry_set?type=TXT&q=$fulldomain"
if ! _contains "$response" "$txtvalue"; then
_err "Txt record not found"
return 1
fi
- _record_id="$(echo "$response" | _egrep_o "{[^{]*\"value\"*:*\"$txtvalue\"[^}]*}" | sed -n -e 's#.*"id": \([0-9]*\).*#\1#p')"
+ _record_id="$(echo "$response" | _egrep_o "\{[^\{]*\"value\"*:*\"$txtvalue\"[^\}]*\}" | sed -n -e 's#.*"id":\([0-9]*\).*#\1#p')"
_debug "_record_id" "$_record_id"
if [ -z "$_record_id" ]; then
_err "can not find _record_id"
return 1
fi
- if ! _rest POST "$_domain/entry_set/$_record_id/delete/" "apikey=$FORNEX_API_KEY"; then
+ if ! _rest DELETE "dns/domain/$_domain/entry_set/$_record_id/"; then
_err "Delete record error."
return 1
fi
@@ -83,18 +88,18 @@ _get_root() {
i=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
- if ! _rest GET "domain_list.json?q=$h&apikey=$FORNEX_API_KEY"; then
+ if ! _rest GET "dns/domain/?q=$h"; then
return 1
fi
- if _contains "$response" "\"$h\"" >/dev/null; then
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain=$h
return 0
else
@@ -127,7 +132,9 @@ _rest() {
data="$3"
_debug "$ep"
- export _H1="Accept: application/json"
+ export _H1="Authorization: Api-Key $FORNEX_API_KEY"
+ export _H2="Content-Type: application/json"
+ export _H3="Accept: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh
index 29cee430..13d9f68b 100755
--- a/dnsapi/dns_freedns.sh
+++ b/dnsapi/dns_freedns.sh
@@ -1,14 +1,15 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_freedns_info='FreeDNS
+Site: FreeDNS.afraid.org
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_freedns
+Options:
+ FREEDNS_User Username
+ FREEDNS_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2305
+Author: David Kerr <@dkerr64>
+'
-#This file name is "dns_freedns.sh"
-#So, here must be a method dns_freedns_add()
-#Which will be called by acme.sh to add the txt record to your api system.
-#returns 0 means success, otherwise error.
-#
-#Author: David Kerr
-#Report Bugs here: https://github.com/dkerr64/acme.sh
-#or here... https://github.com/acmesh-official/acme.sh/issues/2305
-#
######## Public functions #####################
# Export FreeDNS userid and password in following variables...
diff --git a/dnsapi/dns_freemyip.sh b/dnsapi/dns_freemyip.sh
new file mode 100644
index 00000000..d598a657
--- /dev/null
+++ b/dnsapi/dns_freemyip.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_freemyip_info='FreeMyIP.com
+Site: FreeMyIP.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_freemyip
+Options:
+ FREEMYIP_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/6247
+Author: Recolic Keghart , @Giova96
+'
+
+FREEMYIP_DNS_API="https://freemyip.com/update?"
+
+################ Public functions ################
+
+#Usage: dns_freemyip_add fulldomain txtvalue
+dns_freemyip_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Add TXT record $txtvalue for $fulldomain using freemyip.com api"
+
+ FREEMYIP_Token="${FREEMYIP_Token:-$(_readaccountconf_mutable FREEMYIP_Token)}"
+ if [ -z "$FREEMYIP_Token" ]; then
+ FREEMYIP_Token=""
+ _err "You don't specify FREEMYIP_Token yet."
+ _err "Please specify your token and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable FREEMYIP_Token "$FREEMYIP_Token"
+
+ if _is_root_domain_published "$fulldomain"; then
+ _err "freemyip API don't allow you to set multiple TXT record for the same subdomain!"
+ _err "You must apply certificate for only one domain at a time!"
+ _err "===="
+ _err "For example, aaa.yourdomain.freemyip.com and bbb.yourdomain.freemyip.com and yourdomain.freemyip.com ALWAYS share the same TXT record. They will overwrite each other if you apply multiple domain at the same time."
+ _debug "If you are testing this workflow in github pipeline or acmetest, please set TEST_DNS_NO_SUBDOMAIN=1 and TEST_DNS_NO_WILDCARD=1"
+ return 1
+ fi
+
+ # txtvalue must be url-encoded. But it's not necessary for acme txt value.
+ _freemyip_get_until_ok "${FREEMYIP_DNS_API}token=$FREEMYIP_Token&domain=$fulldomain&txt=$txtvalue" 2>&1
+ return $?
+}
+
+#Usage: dns_freemyip_rm fulldomain txtvalue
+dns_freemyip_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Delete TXT record $txtvalue for $fulldomain using freemyip.com api"
+
+ FREEMYIP_Token="${FREEMYIP_Token:-$(_readaccountconf_mutable FREEMYIP_Token)}"
+ if [ -z "$FREEMYIP_Token" ]; then
+ FREEMYIP_Token=""
+ _err "You don't specify FREEMYIP_Token yet."
+ _err "Please specify your token and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable FREEMYIP_Token "$FREEMYIP_Token"
+
+ # Leave the TXT record as empty or "null" to delete the record.
+ _freemyip_get_until_ok "${FREEMYIP_DNS_API}token=$FREEMYIP_Token&domain=$fulldomain&txt=" 2>&1
+ return $?
+}
+
+################ Private functions below ################
+_get_root() {
+ _fmi_d="$1"
+
+ echo "$_fmi_d" | rev | cut -d '.' -f 1-3 | rev
+}
+
+# There is random failure while calling freemyip API too fast. This function automatically retry until success.
+_freemyip_get_until_ok() {
+ _fmi_url="$1"
+ for i in $(seq 1 8); do
+ _debug "HTTP GET freemyip.com API '$_fmi_url', retry $i/8..."
+ _get "$_fmi_url" | tee /dev/fd/2 | grep OK && return 0
+ _sleep 1 # DO NOT send the request too fast
+ done
+ _err "Failed to request freemyip API: $_fmi_url . Server does not say 'OK'"
+ return 1
+}
+
+# Verify in public dns if domain is already there.
+_is_root_domain_published() {
+ _fmi_d="$1"
+ _webroot="$(_get_root "$_fmi_d")"
+
+ _info "Verifying '""$_fmi_d""' freemyip webroot (""$_webroot"") is not published yet"
+ for i in $(seq 1 3); do
+ _debug "'$_webroot' ns lookup, retry $i/3..."
+ if [ "$(_ns_lookup "$_fmi_d" TXT)" ]; then
+ _debug "'$_webroot' already has a TXT record published!"
+ return 0
+ fi
+ _sleep 10 # Give it some time to propagate the TXT record
+ done
+ return 1
+}
diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
index 931da883..0516fee9 100644
--- a/dnsapi/dns_gandi_livedns.sh
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -1,31 +1,42 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_gandi_livedns_info='Gandi.net LiveDNS
+Site: Gandi.net/domain/dns
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gandi_livedns
+Options:
+ GANDI_LIVEDNS_KEY API Key
+Issues: github.com/fcrozat/acme.sh
+Author: Frédéric Crozat , Dominik Röttsches
+'
# Gandi LiveDNS v5 API
-# https://doc.livedns.gandi.net/
+# https://api.gandi.net/docs/livedns/
+# https://api.gandi.net/docs/authentication/ for token + apikey (deprecated) authentication
# currently under beta
-#
-# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
-#
-#Author: Frédéric Crozat
-# Dominik Röttsches
-#Report Bugs here: https://github.com/fcrozat/acme.sh
-#
+
######## Public functions #####################
-GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5"
+GANDI_LIVEDNS_API="https://api.gandi.net/v5/livedns"
#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gandi_livedns_add() {
fulldomain=$1
txtvalue=$2
- if [ -z "$GANDI_LIVEDNS_KEY" ]; then
- _err "No API key specified for Gandi LiveDNS."
- _err "Create your key and export it as GANDI_LIVEDNS_KEY"
+ if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then
+ _err "No Token or API key (deprecated) specified for Gandi LiveDNS."
+ _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively"
return 1
fi
- _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+ # Keep only one secret in configuration
+ if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+ _saveaccountconf GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN"
+ _clearaccountconf GANDI_LIVEDNS_KEY
+ elif [ -n "$GANDI_LIVEDNS_KEY" ]; then
+ _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+ _clearaccountconf GANDI_LIVEDNS_TOKEN
+ fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -70,7 +81,7 @@ dns_gandi_livedns_rm() {
_gandi_livedns_rest PUT \
"domains/$_domain/records/$_sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" &&
- _contains "$response" '{"message": "DNS Record Created"}' &&
+ _contains "$response" '{"message":"DNS Record Created"}' &&
_info "Removing record $(__green "success")"
}
@@ -84,7 +95,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -101,7 +112,7 @@ _get_root() {
elif _contains "$response" '"code": 404'; then
_debug "$h not found"
else
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
@@ -126,7 +137,7 @@ _dns_gandi_append_record() {
_debug new_rrset_values "$_rrset_values"
_gandi_livedns_rest PUT "domains/$_domain/records/$sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_rrset_values}" &&
- _contains "$response" '{"message": "DNS Record Created"}' &&
+ _contains "$response" '{"message":"DNS Record Created"}' &&
_info "Adding record $(__green "success")"
}
@@ -136,11 +147,11 @@ _dns_gandi_existing_rrset_values() {
if ! _gandi_livedns_rest GET "domains/$domain/records/$sub_domain"; then
return 1
fi
- if ! _contains "$response" '"rrset_type": "TXT"'; then
+ if ! _contains "$response" '"rrset_type":"TXT"'; then
_debug "Does not have a _acme-challenge TXT record yet."
return 1
fi
- if _contains "$response" '"rrset_values": \[\]'; then
+ if _contains "$response" '"rrset_values":\[\]'; then
_debug "Empty rrset_values for TXT record, no previous TXT record."
return 1
fi
@@ -157,7 +168,12 @@ _gandi_livedns_rest() {
_debug "$ep"
export _H1="Content-Type: application/json"
- export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+
+ if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+ export _H2="Authorization: Bearer $GANDI_LIVEDNS_TOKEN"
+ else
+ export _H2="Authorization: Apikey $GANDI_LIVEDNS_KEY"
+ fi
if [ "$m" = "GET" ]; then
response="$(_get "$GANDI_LIVEDNS_API/$ep")"
diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh
index 2788ad59..a6016abc 100755
--- a/dnsapi/dns_gcloud.sh
+++ b/dnsapi/dns_gcloud.sh
@@ -1,6 +1,12 @@
#!/usr/bin/env sh
-
-# Author: Janos Lenart
+# shellcheck disable=SC2034
+dns_gcloud_info='Google Cloud DNS
+Site: Cloud.Google.com/dns
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gcloud
+Options:
+ CLOUDSDK_ACTIVE_CONFIG_NAME Active config name. E.g. "default"
+Author: Janos Lenart
+'
######## Public functions #####################
@@ -42,7 +48,7 @@ dns_gcloud_rm() {
echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
_dns_gcloud_execute_tr || return $?
- _info "$fulldomain record added"
+ _info "$fulldomain record removed"
}
#################### Private functions below ##################################
diff --git a/dnsapi/dns_gcore.sh b/dnsapi/dns_gcore.sh
new file mode 100755
index 00000000..fbdba7ee
--- /dev/null
+++ b/dnsapi/dns_gcore.sh
@@ -0,0 +1,191 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_gcore_info='Gcore.com
+Site: Gcore.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gcore
+Options:
+ GCORE_Key API Key
+Issues: github.com/acmesh-official/acme.sh/issues/4460
+'
+
+GCORE_Api="https://api.gcore.com/dns/v2"
+GCORE_Doc="https://api.gcore.com/docs/dns"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_gcore_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ GCORE_Key="${GCORE_Key:-$(_readaccountconf_mutable GCORE_Key)}"
+
+ if [ -z "$GCORE_Key" ]; then
+ GCORE_Key=""
+ _err "You didn't specify a Gcore api key yet."
+ _err "You can get yours from here $GCORE_Doc"
+ return 1
+ fi
+
+ #save the api key to the account conf file.
+ _saveaccountconf_mutable GCORE_Key "$GCORE_Key" "base64"
+
+ _debug "First detect the zone name"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _zone_name "$_zone_name"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _gcore_rest GET "zones/$_zone_name/$fulldomain/TXT"
+ payload=""
+
+ if echo "$response" | grep "record is not found" >/dev/null; then
+ _info "Record doesn't exists"
+ payload="{\"resource_records\":[{\"content\":[\"$txtvalue\"],\"enabled\":true}],\"ttl\":120}"
+ elif echo "$response" | grep "$txtvalue" >/dev/null; then
+ _info "Already exists, OK"
+ return 0
+ elif echo "$response" | tr -d " " | grep \"name\":\""$fulldomain"\",\"type\":\"TXT\" >/dev/null; then
+ _info "Record with mismatch txtvalue, try update it"
+ payload=$(echo "$response" | tr -d " " | sed 's/"updated_at":[0-9]\+,//g' | sed 's/"meta":{}}]}/"meta":{}},{"content":['\""$txtvalue"\"'],"enabled":true}]}/')
+ fi
+
+ # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
+ # we can not use updating anymore.
+ # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
+ # _debug count "$count"
+ # if [ "$count" = "0" ]; then
+ _info "Adding record"
+ if _gcore_rest PUT "zones/$_zone_name/$fulldomain/TXT" "$payload"; then
+ if _contains "$response" "$txtvalue"; then
+ _info "Added, OK"
+ return 0
+ elif _contains "$response" "rrset is already exists"; then
+ _info "Already exists, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_gcore_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ GCORE_Key="${GCORE_Key:-$(_readaccountconf_mutable GCORE_Key)}"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _zone_name "$_zone_name"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _gcore_rest GET "zones/$_zone_name/$fulldomain/TXT"
+
+ if echo "$response" | grep "record is not found" >/dev/null; then
+ _info "No such txt recrod"
+ return 0
+ fi
+
+ if ! echo "$response" | tr -d " " | grep \"name\":\""$fulldomain"\",\"type\":\"TXT\" >/dev/null; then
+ _err "Error: $response"
+ return 1
+ fi
+
+ if ! echo "$response" | tr -d " " | grep \""$txtvalue"\" >/dev/null; then
+ _info "No such txt recrod"
+ return 0
+ fi
+
+ count="$(echo "$response" | grep -o "content" | wc -l)"
+
+ if [ "$count" = "1" ]; then
+ if ! _gcore_rest DELETE "zones/$_zone_name/$fulldomain/TXT"; then
+ _err "Delete record error. $response"
+ return 1
+ fi
+ return 0
+ fi
+
+ payload="$(echo "$response" | tr -d " " | sed 's/"updated_at":[0-9]\+,//g' | sed 's/{"id":[0-9]\+,"content":\["'"$txtvalue"'"\],"enabled":true,"meta":{}}//' | sed 's/\[,/\[/' | sed 's/,,/,/' | sed 's/,\]/\]/')"
+ if ! _gcore_rest PUT "zones/$_zone_name/$fulldomain/TXT" "$payload"; then
+ _err "Delete record error. $response"
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.sub.domain.com
+#returns
+# _sub_domain=_acme-challenge.sub or _acme-challenge
+# _domain=domain.com
+# _zone_name=domain.com or sub.domain.com
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _gcore_rest GET "zones/$h"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _zone_name=$h
+ if [ "$_zone_name" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_gcore_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ key_trimmed=$(echo "$GCORE_Key" | tr -d '"')
+
+ export _H1="Content-Type: application/json"
+ export _H2="Authorization: APIKey $key_trimmed"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$GCORE_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$GCORE_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh
index 44c3d279..ee66ee19 100755
--- a/dnsapi/dns_gd.sh
+++ b/dnsapi/dns_gd.sh
@@ -1,12 +1,12 @@
#!/usr/bin/env sh
-
-#Godaddy domain api
-# Get API key and secret from https://developer.godaddy.com/
-#
-# GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-# GD_Secret="asdfsdfsfsdfsdfdfsdf"
-#
-# Ex.: acme.sh --issue --staging --dns dns_gd -d "*.s.example.com" -d "s.example.com"
+# shellcheck disable=SC2034
+dns_gd_info='GoDaddy.com
+Site: GoDaddy.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gd
+Options:
+ GD_Key API Key
+ GD_Secret API Secret
+'
GD_Api="https://api.godaddy.com/v1"
@@ -22,8 +22,8 @@ dns_gd_add() {
if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
GD_Key=""
GD_Secret=""
- _err "You don't specify godaddy api key and secret yet."
- _err "Please create you key and try again."
+ _err "You didn't specify godaddy api key and secret yet."
+ _err "Please create your key and try again."
return 1
fi
@@ -46,7 +46,7 @@ dns_gd_add() {
fi
if _contains "$response" "$txtvalue"; then
- _info "The record is existing, skip"
+ _info "This record already exists, skipping"
return 0
fi
@@ -148,7 +148,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -161,7 +161,7 @@ _get_root() {
if _contains "$response" '"code":"NOT_FOUND"'; then
_debug "$h not found"
else
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_geoscaling.sh b/dnsapi/dns_geoscaling.sh
index 6ccf4daf..05887c7e 100755
--- a/dnsapi/dns_geoscaling.sh
+++ b/dnsapi/dns_geoscaling.sh
@@ -1,12 +1,12 @@
#!/usr/bin/env sh
-
-########################################################################
-# Geoscaling hook script for acme.sh
-#
-# Environment variables:
-#
-# - $GEOSCALING_Username (your Geoscaling username - this is usually NOT an amail address)
-# - $GEOSCALING_Password (your Geoscaling password)
+# shellcheck disable=SC2034
+dns_geoscaling_info='GeoScaling.com
+Site: GeoScaling.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_geoscaling
+Options:
+ GEOSCALING_Username Username. This is usually NOT an email address
+ GEOSCALING_Password Password
+'
#-- dns_geoscaling_add() - Add TXT record --------------------------------------
# Usage: dns_geoscaling_add _acme-challenge.subdomain.domain.com "XyZ123..."
@@ -202,7 +202,7 @@ find_zone() {
# Walk through all possible zone names
strip_counter=1
while true; do
- attempted_zone=$(echo "${domain}" | cut -d . -f ${strip_counter}-)
+ attempted_zone=$(echo "${domain}" | cut -d . -f "${strip_counter}"-)
# All possible zone names have been tried
if [ -z "${attempted_zone}" ]; then
diff --git a/dnsapi/dns_googledomains.sh b/dnsapi/dns_googledomains.sh
new file mode 100755
index 00000000..07a37e07
--- /dev/null
+++ b/dnsapi/dns_googledomains.sh
@@ -0,0 +1,178 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_googledomains_info='Google Domains
+Site: Domains.Google.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_googledomains
+Options:
+ GOOGLEDOMAINS_ACCESS_TOKEN API Access Token
+ GOOGLEDOMAINS_ZONE Zone
+Issues: github.com/acmesh-official/acme.sh/issues/4545
+Author: Alex Leigh
+'
+
+GOOGLEDOMAINS_API="https://acmedns.googleapis.com/v1/acmeChallengeSets"
+
+######## Public functions ########
+
+#Usage: dns_googledomains_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_googledomains_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Invoking Google Domains ACME DNS API."
+
+ if ! _dns_googledomains_setup; then
+ return 1
+ fi
+
+ zone="$(_dns_googledomains_get_zone "$fulldomain")"
+ if [ -z "$zone" ]; then
+ _err "Could not find a Google Domains-managed zone containing the requested domain."
+ return 1
+ fi
+
+ _debug zone "$zone"
+ _debug txtvalue "$txtvalue"
+
+ _info "Adding TXT record for $fulldomain."
+ if _dns_googledomains_api "$zone" ":rotateChallenges" "{\"accessToken\":\"$GOOGLEDOMAINS_ACCESS_TOKEN\",\"recordsToAdd\":[{\"fqdn\":\"$fulldomain\",\"digest\":\"$txtvalue\"}],\"keepExpiredRecords\":true}"; then
+ if _contains "$response" "$txtvalue"; then
+ _info "TXT record added."
+ return 0
+ else
+ _err "Error adding TXT record."
+ return 1
+ fi
+ fi
+
+ _err "Error adding TXT record."
+ return 1
+}
+
+#Usage: dns_googledomains_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_googledomains_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Invoking Google Domains ACME DNS API."
+
+ if ! _dns_googledomains_setup; then
+ return 1
+ fi
+
+ zone="$(_dns_googledomains_get_zone "$fulldomain")"
+ if [ -z "$zone" ]; then
+ _err "Could not find a Google Domains-managed domain based on request."
+ return 1
+ fi
+
+ _debug zone "$zone"
+ _debug txtvalue "$txtvalue"
+
+ _info "Removing TXT record for $fulldomain."
+ if _dns_googledomains_api "$zone" ":rotateChallenges" "{\"accessToken\":\"$GOOGLEDOMAINS_ACCESS_TOKEN\",\"recordsToRemove\":[{\"fqdn\":\"$fulldomain\",\"digest\":\"$txtvalue\"}],\"keepExpiredRecords\":true}"; then
+ if _contains "$response" "$txtvalue"; then
+ _err "Error removing TXT record."
+ return 1
+ else
+ _info "TXT record removed."
+ return 0
+ fi
+ fi
+
+ _err "Error removing TXT record."
+ return 1
+}
+
+######## Private functions ########
+
+_dns_googledomains_setup() {
+ if [ -n "$GOOGLEDOMAINS_SETUP_COMPLETED" ]; then
+ return 0
+ fi
+
+ GOOGLEDOMAINS_ACCESS_TOKEN="${GOOGLEDOMAINS_ACCESS_TOKEN:-$(_readaccountconf_mutable GOOGLEDOMAINS_ACCESS_TOKEN)}"
+ GOOGLEDOMAINS_ZONE="${GOOGLEDOMAINS_ZONE:-$(_readaccountconf_mutable GOOGLEDOMAINS_ZONE)}"
+
+ if [ -z "$GOOGLEDOMAINS_ACCESS_TOKEN" ]; then
+ GOOGLEDOMAINS_ACCESS_TOKEN=""
+ _err "Google Domains access token was not specified."
+ _err "Please visit Google Domains Security settings to provision an ACME DNS API access token."
+ return 1
+ fi
+
+ if [ "$GOOGLEDOMAINS_ZONE" ]; then
+ _savedomainconf GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
+ _savedomainconf GOOGLEDOMAINS_ZONE "$GOOGLEDOMAINS_ZONE"
+ else
+ _saveaccountconf_mutable GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
+ _clearaccountconf_mutable GOOGLEDOMAINS_ZONE
+ _clearaccountconf GOOGLEDOMAINS_ZONE
+ fi
+
+ _debug GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
+ _debug GOOGLEDOMAINS_ZONE "$GOOGLEDOMAINS_ZONE"
+
+ GOOGLEDOMAINS_SETUP_COMPLETED=1
+ return 0
+}
+
+_dns_googledomains_get_zone() {
+ domain=$1
+
+ # Use zone directly if provided
+ if [ "$GOOGLEDOMAINS_ZONE" ]; then
+ if ! _dns_googledomains_api "$GOOGLEDOMAINS_ZONE"; then
+ return 1
+ fi
+
+ echo "$GOOGLEDOMAINS_ZONE"
+ return 0
+ fi
+
+ i=2
+ while true; do
+ curr=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug curr "$curr"
+
+ if [ -z "$curr" ]; then
+ return 1
+ fi
+
+ if _dns_googledomains_api "$curr"; then
+ echo "$curr"
+ return 0
+ fi
+
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_dns_googledomains_api() {
+ zone=$1
+ apimethod=$2
+ data="$3"
+
+ if [ -z "$data" ]; then
+ response="$(_get "$GOOGLEDOMAINS_API/$zone$apimethod")"
+ else
+ _debug data "$data"
+ export _H1="Content-Type: application/json"
+ response="$(_post "$data" "$GOOGLEDOMAINS_API/$zone$apimethod")"
+ fi
+
+ _debug response "$response"
+
+ if [ "$?" != "0" ]; then
+ _err "Error"
+ return 1
+ fi
+
+ if _contains "$response" "\"error\": {"; then
+ return 1
+ fi
+
+ return 0
+}
diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh
index bf4a5030..a768f352 100755
--- a/dnsapi/dns_he.sh
+++ b/dnsapi/dns_he.sh
@@ -1,15 +1,14 @@
#!/usr/bin/env sh
-
-########################################################################
-# Hurricane Electric hook script for acme.sh
-#
-# Environment variables:
-#
-# - $HE_Username (your dns.he.net username)
-# - $HE_Password (your dns.he.net password)
-#
-# Author: Ondrej Simek
-# Git repo: https://github.com/angel333/acme.sh
+# shellcheck disable=SC2034
+dns_he_info='Hurricane Electric HE.net
+Site: dns.he.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_he
+Options:
+ HE_Username Username
+ HE_Password Password
+Issues: github.com/angel333/acme.sh/issues/
+Author: Ondrej Simek
+'
#-- dns_he_add() - Add TXT record --------------------------------------
# Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..."
@@ -144,7 +143,7 @@ _find_zone() {
# Walk through all possible zone names
_strip_counter=1
while true; do
- _attempted_zone=$(echo "$_domain" | cut -d . -f ${_strip_counter}-)
+ _attempted_zone=$(echo "$_domain" | cut -d . -f "${_strip_counter}"-)
# All possible zone names have been tried
if [ -z "$_attempted_zone" ]; then
diff --git a/dnsapi/dns_he_ddns.sh b/dnsapi/dns_he_ddns.sh
new file mode 100644
index 00000000..1fe9a7fd
--- /dev/null
+++ b/dnsapi/dns_he_ddns.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_he_ddns_info='Hurricane Electric HE.net DDNS
+Site: dns.he.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_he_ddns
+Options:
+ HE_DDNS_KEY The DDNS key
+Issues: https://github.com/acmesh-official/acme.sh/issues/5238
+Author: Markku Leiniö
+'
+
+HE_DDNS_URL="https://dyn.dns.he.net/nic/update"
+
+######## Public functions #####################
+
+#Usage: dns_he_ddns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_he_ddns_add() {
+ fulldomain=$1
+ txtvalue=$2
+ HE_DDNS_KEY="${HE_DDNS_KEY:-$(_readaccountconf_mutable HE_DDNS_KEY)}"
+ if [ -z "$HE_DDNS_KEY" ]; then
+ HE_DDNS_KEY=""
+ _err "You didn't specify a DDNS key for accessing the TXT record in HE API."
+ return 1
+ fi
+ #Save the DDNS key to the account conf file.
+ _saveaccountconf_mutable HE_DDNS_KEY "$HE_DDNS_KEY"
+
+ _info "Using Hurricane Electric DDNS API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ response="$(_post "hostname=$fulldomain&password=$HE_DDNS_KEY&txt=$txtvalue" "$HE_DDNS_URL")"
+ _info "Response: $response"
+ _contains "$response" "good" && return 0 || return 1
+}
+
+# dns_he_ddns_rm() is not doing anything because the API call always updates the
+# contents of the existing record (that the API key gives access to).
+
+dns_he_ddns_rm() {
+ fulldomain=$1
+ _debug "Delete TXT record called for '${fulldomain}', not doing anything."
+ return 0
+}
diff --git a/dnsapi/dns_hetzner.sh b/dnsapi/dns_hetzner.sh
old mode 100644
new mode 100755
index 911d4a35..f1bddc61
--- a/dnsapi/dns_hetzner.sh
+++ b/dnsapi/dns_hetzner.sh
@@ -1,8 +1,12 @@
#!/usr/bin/env sh
-
-#
-#HETZNER_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
+# shellcheck disable=SC2034
+dns_hetzner_info='Hetzner.com
+Site: Hetzner.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_hetzner
+Options:
+ HETZNER_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/2943
+'
HETZNER_Api="https://dns.hetzner.com/api/v1"
@@ -177,7 +181,7 @@ _get_root() {
_debug "Trying to get zone id by domain name for '$domain_without_acme'."
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -189,7 +193,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_entries":1'; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
HETZNER_Zone_ID=$_domain_id
_savedomainconf "$domain_param_name" "$HETZNER_Zone_ID"
@@ -208,7 +212,7 @@ _get_root() {
_response_has_error() {
unset _response_error
- err_part="$(echo "$response" | _egrep_o '"error":{[^}]*}')"
+ err_part="$(echo "$response" | _egrep_o '"error":\{[^\}]*\}')"
if [ -n "$err_part" ]; then
err_code=$(echo "$err_part" | _egrep_o '"code":[0-9]+' | cut -d : -f 2)
diff --git a/dnsapi/dns_hexonet.sh b/dnsapi/dns_hexonet.sh
index 525efe73..017641fd 100755
--- a/dnsapi/dns_hexonet.sh
+++ b/dnsapi/dns_hexonet.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-#
-# Hexonet_Login="username!roleId"
-#
-# Hexonet_Password="rolePassword"
+# shellcheck disable=SC2034
+dns_hexonet_info='Hexonet.com
+Site: Hexonet.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_hexonet
+Options:
+ Hexonet_Login Login. E.g. "username!roleId"
+ Hexonet_Password Role Password
+Issues: github.com/acmesh-official/acme.sh/issues/2389
+'
Hexonet_Api="https://coreapi.1api.net/api/call.cgi"
@@ -119,7 +123,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -131,7 +135,7 @@ _get_root() {
fi
if _contains "$response" "CODE=200"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh
index 9e3e5664..41ccab2b 100644
--- a/dnsapi/dns_hostingde.sh
+++ b/dnsapi/dns_hostingde.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env sh
-
-# hosting.de API
-
-# Values to export:
-# export HOSTINGDE_ENDPOINT='https://secure.hosting.de'
-# export HOSTINGDE_APIKEY='xxxxx'
+# shellcheck disable=SC2034
+dns_hostingde_info='Hosting.de
+Site: Hosting.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_hostingde
+Options:
+ HOSTINGDE_ENDPOINT Endpoint. E.g. "https://secure.hosting.de"
+ HOSTINGDE_APIKEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2058
+'
######## Public functions #####################
diff --git a/dnsapi/dns_huaweicloud.sh b/dnsapi/dns_huaweicloud.sh
index ceda9258..ee2d2b8e 100644
--- a/dnsapi/dns_huaweicloud.sh
+++ b/dnsapi/dns_huaweicloud.sh
@@ -1,8 +1,14 @@
#!/usr/bin/env sh
-
-# HUAWEICLOUD_Username
-# HUAWEICLOUD_Password
-# HUAWEICLOUD_DomainName
+# shellcheck disable=SC2034
+dns_huaweicloud_info='HuaweiCloud.com
+Site: HuaweiCloud.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_huaweicloud
+Options:
+ HUAWEICLOUD_Username Username
+ HUAWEICLOUD_Password Password
+ HUAWEICLOUD_DomainName DomainName
+Issues: github.com/acmesh-official/acme.sh/issues/3265
+'
iam_api="https://iam.myhuaweicloud.com"
dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work
@@ -23,7 +29,7 @@ dns_huaweicloud_add() {
HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
- HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
+ HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_DomainName)}"
# Check information
if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
@@ -74,7 +80,7 @@ dns_huaweicloud_rm() {
HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
- HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
+ HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_DomainName)}"
# Check information
if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
@@ -98,19 +104,59 @@ dns_huaweicloud_rm() {
fi
_debug "Zone ID is:" "${zoneid}"
+ record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")"
+ _recursive_rm_record "${token}" "${fulldomain}" "${zoneid}" "${record_id}"
+ ret="$?"
+ if [ "${ret}" != "0" ]; then
+ _err "dns_api(dns_huaweicloud): Error removing record."
+ return 1
+ fi
+
+ return 0
+}
+
+################### Private functions below ##################################
+
+# _recursive_rm_record
+# remove all records from the record set
+#
+# _token=$1
+# _domain=$2
+# _zoneid=$3
+# _record_id=$4
+#
+# Returns 0 on success
+_recursive_rm_record() {
+ _token=$1
+ _domain=$2
+ _zoneid=$3
+ _record_id=$4
+
+ # Most likely to have problems will huaweicloud side if more than 50 attempts but still cannot fully remove the record set
+ # Maybe can be removed manually in the dashboard
+ _retry_cnt=50
+
# Remove all records
# Therotically HuaweiCloud does not allow more than one record set
# But remove them recurringly to increase robusty
- while [ "${record_id}" != "0" ]; do
+
+ while [ "${_record_id}" != "0" ] && [ "${_retry_cnt}" != "0" ]; do
_debug "Removing Record"
- _rm_record "${token}" "${zoneid}" "${record_id}"
- record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")"
+ _retry_cnt=$((_retry_cnt - 1))
+ _rm_record "${_token}" "${_zoneid}" "${_record_id}"
+ _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${_zoneid}")"
+ _debug2 "Checking record exists: record_id=${_record_id}"
done
+
+ # Check if retry count is reached
+ if [ "${_retry_cnt}" = "0" ]; then
+ _debug "Failed to remove record after 50 attempts, please try removing it manually in the dashboard"
+ return 1
+ fi
+
return 0
}
-################### Private functions below ##################################
-
# _get_zoneid
#
# _token=$1
@@ -124,7 +170,7 @@ _get_zoneid() {
i=1
while true; do
- h=$(printf "%s" "${_domain_string}" | cut -d . -f $i-100)
+ h=$(printf "%s" "${_domain_string}" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -135,11 +181,11 @@ _get_zoneid() {
if _contains "${response}" '"id"'; then
zoneidlist=$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
zonenamelist=$(echo "${response}" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
- _debug2 "Return Zone ID(s):" "${zoneidlist}"
- _debug2 "Return Zone Name(s):" "${zonenamelist}"
+ _debug2 "Returned Zone ID(s):" "${zoneidlist}"
+ _debug2 "Returned Zone Name(s):" "${zonenamelist}"
zoneidnum=0
zoneidcount=$(echo "${zoneidlist}" | grep -c '^')
- _debug "Retund Zone ID(s) Count:" "${zoneidcount}"
+ _debug "Returned Zone ID(s) Count:" "${zoneidcount}"
while [ "${zoneidnum}" -lt "${zoneidcount}" ]; do
zoneidnum=$(_math "$zoneidnum" + 1)
_zoneid=$(echo "${zoneidlist}" | sed -n "${zoneidnum}p")
@@ -164,7 +210,7 @@ _get_recordset_id() {
_zoneid=$3
export _H1="X-Auth-Token: ${_token}"
- response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}")
+ response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}&status=ACTIVE")
if _contains "${response}" '"id"'; then
_id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")"
printf "%s" "${_id}"
@@ -181,7 +227,7 @@ _add_record() {
# Get Existing Records
export _H1="X-Auth-Token: ${_token}"
- response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}")
+ response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}&status=ACTIVE")
_debug2 "${response}"
_exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g')
@@ -206,8 +252,7 @@ _add_record() {
\"type\": \"TXT\",
\"ttl\": 1,
\"records\": [
- ${_exist_record},
- \"\\\"${_txtvalue}\\\"\"
+ ${_exist_record},\"\\\"${_txtvalue}\\\"\"
]
}"
fi
@@ -215,19 +260,16 @@ _add_record() {
_record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")"
_debug "Record Set ID is:" "${_record_id}"
- # Remove all records
- while [ "${_record_id}" != "0" ]; do
- _debug "Removing Record"
- _rm_record "${_token}" "${zoneid}" "${_record_id}"
- _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")"
- done
-
# Add brand new records with all old and new records
export _H2="Content-Type: application/json"
export _H1="X-Auth-Token: ${_token}"
_debug2 "${_post_body}"
- _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null
+ if [ -z "${_exist_record}" ]; then
+ _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null
+ else
+ _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets/${_record_id}" false "PUT" >/dev/null
+ fi
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$_code" != "202" ]; then
_err "dns_huaweicloud: http code ${_code}"
diff --git a/dnsapi/dns_infoblox.sh b/dnsapi/dns_infoblox.sh
index 6bfd36ee..27f1e61e 100644
--- a/dnsapi/dns_infoblox.sh
+++ b/dnsapi/dns_infoblox.sh
@@ -1,8 +1,14 @@
#!/usr/bin/env sh
-
-## Infoblox API integration by Jason Keller and Elijah Tenai
-##
-## Report any bugs via https://github.com/jasonkeller/acme.sh
+# shellcheck disable=SC2034
+dns_infoblox_info='Infoblox.com
+Site: Infoblox.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_infoblox
+Options:
+ Infoblox_Creds Credentials. E.g. "username:password"
+ Infoblox_Server Server hostname. IP or FQDN of infoblox appliance
+Issues: github.com/jasonkeller/acme.sh
+Author: Jason Keller, Elijah Tenai
+'
dns_infoblox_add() {
diff --git a/dnsapi/dns_infomaniak.sh b/dnsapi/dns_infomaniak.sh
index 765cf39d..ea5ef461 100755
--- a/dnsapi/dns_infomaniak.sh
+++ b/dnsapi/dns_infomaniak.sh
@@ -1,19 +1,20 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_infomaniak_info='Infomaniak.com
+Site: Infomaniak.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_infomaniak
+Options:
+ INFOMANIAK_API_TOKEN API Token
+Issues: github.com/acmesh-official/acme.sh/issues/3188
+'
-###############################################################################
-# Infomaniak API integration
-#
# To use this API you need visit the API dashboard of your account
# once logged into https://manager.infomaniak.com add /api/dashboard to the URL
#
-# Please report bugs to
-# https://github.com/acmesh-official/acme.sh/issues/3188
-#
# Note: the URL looks like this:
# https://manager.infomaniak.com/v3//api/dashboard
# Then generate a token with the scope Domain
# this is given as an environment variable INFOMANIAK_API_TOKEN
-###############################################################################
# base variables
@@ -76,7 +77,7 @@ dns_infomaniak_add() {
domain_id=${zone_and_id#* }
# extract first part of domain
- key=${fulldomain%.$zone}
+ key=${fulldomain%."$zone"}
_debug "zone:$zone id:$domain_id key:$key"
@@ -149,7 +150,7 @@ dns_infomaniak_rm() {
domain_id=${zone_and_id#* }
# extract first part of domain
- key=${fulldomain%.$zone}
+ key=${fulldomain%."$zone"}
_debug "zone:$zone id:$domain_id key:$key"
diff --git a/dnsapi/dns_internetbs.sh b/dnsapi/dns_internetbs.sh
index ae6b9e1e..4238bfe4 100755
--- a/dnsapi/dns_internetbs.sh
+++ b/dnsapi/dns_internetbs.sh
@@ -1,12 +1,14 @@
#!/usr/bin/env sh
-
-#This is the Internet.BS api wrapper for acme.sh
-#
-#Author: Ne-Lexa
-#Report Bugs here: https://github.com/Ne-Lexa/acme.sh
-
-#INTERNETBS_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#INTERNETBS_API_PASSWORD="sdfsdfsdfljlbjkljlkjsdfoiwje"
+# shellcheck disable=SC2034
+dns_internetbs_info='InternetBS.net
+Site: InternetBS.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_internetbs
+Options:
+ INTERNETBS_API_KEY API Key
+ INTERNETBS_API_PASSWORD API Password
+Issues: github.com/acmesh-official/acme.sh/issues/2261
+Author: Ne-Lexa
+'
INTERNETBS_API_URL="https://api.internet.bs"
@@ -131,7 +133,7 @@ _get_root() {
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f ${i}-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "${i}"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -139,7 +141,7 @@ _get_root() {
fi
if _contains "$response" "\"$h\""; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-${p})
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"${p}")
_domain=${h}
return 0
fi
diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh
index ba789da9..808fc3a9 100755
--- a/dnsapi/dns_inwx.sh
+++ b/dnsapi/dns_inwx.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_inwx_info='INWX.de
+Site: INWX.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_inwx
+Options:
+ INWX_User Username
+ INWX_Password Password
+'
-#
-#INWX_User="username"
-#
-#INWX_Password="password"
-#
# Dependencies:
# -------------
# - oathtool (When using 2 Factor Authentication)
@@ -160,6 +163,15 @@ _inwx_check_cookie() {
return 1
}
+_htmlEscape() {
+ _s="$1"
+ _s=$(echo "$_s" | sed "s/&/&/g")
+ _s=$(echo "$_s" | sed "s/\</g")
+ _s=$(echo "$_s" | sed "s/>/\>/g")
+ _s=$(echo "$_s" | sed 's/"/\"/g')
+ printf -- %s "$_s"
+}
+
_inwx_login() {
if _inwx_check_cookie; then
@@ -167,6 +179,8 @@ _inwx_login() {
return 0
fi
+ XML_PASS=$(_htmlEscape "$INWX_Password")
+
xml_content=$(printf '
account.login
@@ -190,11 +204,11 @@ _inwx_login() {
- ' "$INWX_User" "$INWX_Password")
+ ' "$INWX_User" "$XML_PASS")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
- INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+ INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
_H1=$INWX_Cookie
export _H1
export INWX_Cookie
@@ -279,7 +293,7 @@ _get_root() {
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -287,7 +301,7 @@ _get_root() {
fi
if _contains "$response" "$h"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_ionos.sh b/dnsapi/dns_ionos.sh
index e4ad3318..9a464253 100755
--- a/dnsapi/dns_ionos.sh
+++ b/dnsapi/dns_ionos.sh
@@ -1,14 +1,13 @@
#!/usr/bin/env sh
-
-# Supports IONOS DNS API v1.0.1
-#
-# Usage:
-# Export IONOS_PREFIX and IONOS_SECRET before calling acme.sh:
-#
-# $ export IONOS_PREFIX="..."
-# $ export IONOS_SECRET="..."
-#
-# $ acme.sh --issue --dns dns_ionos ...
+# shellcheck disable=SC2034
+dns_ionos_info='IONOS.de
+Site: IONOS.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_ionos
+Options:
+ IONOS_PREFIX Prefix
+ IONOS_SECRET Secret
+Issues: github.com/acmesh-official/acme.sh/issues/3379
+'
IONOS_API="https://api.hosting.ionos.com/dns"
IONOS_ROUTE_ZONES="/v1/zones"
@@ -88,7 +87,7 @@ _get_root() {
_response="$(echo "$_response" | tr -d "\n")"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
return 1
fi
@@ -97,7 +96,7 @@ _get_root() {
if [ "$_zone" ]; then
_zone_id=$(printf "%s\n" "$_zone" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
if [ "$_zone_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
diff --git a/dnsapi/dns_ionos_cloud.sh b/dnsapi/dns_ionos_cloud.sh
new file mode 100644
index 00000000..f255092f
--- /dev/null
+++ b/dnsapi/dns_ionos_cloud.sh
@@ -0,0 +1,147 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_ionos_cloud_info='IONOS Cloud DNS
+Site: ionos.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_ionos_cloud
+Options:
+ IONOS_TOKEN API Token.
+Issues: github.com/acmesh-official/acme.sh/issues/5243
+'
+
+# Supports IONOS Cloud DNS API v1.15.4
+
+IONOS_CLOUD_API="https://dns.de-fra.ionos.com"
+IONOS_CLOUD_ROUTE_ZONES="/zones"
+
+dns_ionos_cloud_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _ionos_init; then
+ return 1
+ fi
+
+ _record_name=$(printf "%s" "$fulldomain" | cut -d . -f 1)
+ _body="{\"properties\":{\"name\":\"$_record_name\", \"type\":\"TXT\", \"content\":\"$txtvalue\"}}"
+
+ if _ionos_cloud_rest POST "$IONOS_CLOUD_ROUTE_ZONES/$_zone_id/records" "$_body" && [ "$_code" = "202" ]; then
+ _info "TXT record has been created successfully."
+ return 0
+ fi
+
+ return 1
+}
+
+dns_ionos_cloud_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _ionos_init; then
+ return 1
+ fi
+
+ if ! _ionos_cloud_get_record "$_zone_id" "$txtvalue" "$fulldomain"; then
+ _err "Could not find _acme-challenge TXT record."
+ return 1
+ fi
+
+ if _ionos_cloud_rest DELETE "$IONOS_CLOUD_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ "$_code" = "202" ]; then
+ _info "TXT record has been deleted successfully."
+ return 0
+ fi
+
+ return 1
+}
+
+_ionos_init() {
+ IONOS_TOKEN="${IONOS_TOKEN:-$(_readaccountconf_mutable IONOS_TOKEN)}"
+
+ if [ -z "$IONOS_TOKEN" ]; then
+ _err "You didn't specify an IONOS token yet."
+ _err "Read https://api.ionos.com/docs/authentication/v1/#tag/tokens/operation/tokensGenerate to learn how to get a token."
+ _err "You need to set it before calling acme.sh:"
+ _err "\$ export IONOS_TOKEN=\"...\""
+ _err "\$ acme.sh --issue -d ... --dns dns_ionos_cloud"
+ return 1
+ fi
+
+ _saveaccountconf_mutable IONOS_TOKEN "$IONOS_TOKEN"
+
+ if ! _get_cloud_zone "$fulldomain"; then
+ _err "Cannot find zone $zone in your IONOS account."
+ return 1
+ fi
+
+ return 0
+}
+
+_get_cloud_zone() {
+ domain=$1
+ zone=$(printf "%s" "$domain" | cut -d . -f 2-)
+
+ if _ionos_cloud_rest GET "$IONOS_CLOUD_ROUTE_ZONES?filter.zoneName=$zone"; then
+ _response="$(echo "$_response" | tr -d "\n")"
+
+ _zone_list_items=$(echo "$_response" | _egrep_o "\"items\":.*")
+
+ _zone_id=$(printf "%s\n" "$_zone_list_items" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
+ if [ "$_zone_id" ]; then
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+_ionos_cloud_get_record() {
+ zone_id=$1
+ txtrecord=$2
+ # this is to transform the domain to lower case
+ fulldomain=$(printf "%s" "$3" | _lower_case)
+ # this is to transform record name to lower case
+ # IONOS Cloud API transforms all record names to lower case
+ _record_name=$(printf "%s" "$fulldomain" | cut -d . -f 1 | _lower_case)
+
+ if _ionos_cloud_rest GET "$IONOS_CLOUD_ROUTE_ZONES/$zone_id/records"; then
+ _response="$(echo "$_response" | tr -d "\n")"
+
+ pattern="\{\"id\":\"[a-fA-F0-9\-]*\",\"type\":\"record\",\"href\":\"/zones/$zone_id/records/[a-fA-F0-9\-]*\",\"metadata\":\{\"createdDate\":\"[A-Z0-9\:\.\-]*\",\"lastModifiedDate\":\"[A-Z0-9\:\.\-]*\",\"fqdn\":\"$fulldomain\",\"state\":\"AVAILABLE\",\"zoneId\":\"$zone_id\"\},\"properties\":\{\"content\":\"$txtrecord\",\"enabled\":true,\"name\":\"$_record_name\",\"priority\":[0-9]*,\"ttl\":[0-9]*,\"type\":\"TXT\"\}\}"
+
+ _record="$(echo "$_response" | _egrep_o "$pattern")"
+ if [ "$_record" ]; then
+ _record_id=$(printf "%s\n" "$_record" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+_ionos_cloud_rest() {
+ method="$1"
+ route="$2"
+ data="$3"
+
+ export _H1="Authorization: Bearer $IONOS_TOKEN"
+
+ # clear headers
+ : >"$HTTP_HEADER"
+
+ if [ "$method" != "GET" ]; then
+ _response="$(_post "$data" "$IONOS_CLOUD_API$route" "" "$method" "application/json")"
+ else
+ _response="$(_get "$IONOS_CLOUD_API$route")"
+ fi
+
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+
+ if [ "$?" != "0" ]; then
+ _err "Error $route: $_response"
+ return 1
+ fi
+
+ _debug2 "_response" "$_response"
+ _debug2 "_code" "$_code"
+
+ return 0
+}
diff --git a/dnsapi/dns_ipv64.sh b/dnsapi/dns_ipv64.sh
new file mode 100755
index 00000000..51025d1e
--- /dev/null
+++ b/dnsapi/dns_ipv64.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_ipv64_info='IPv64.net
+Site: IPv64.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_ipv64
+Options:
+ IPv64_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/4419
+Author: Roman Lumetsberger
+'
+
+IPv64_API="https://ipv64.net/api"
+
+######## Public functions ######################
+
+#Usage: dns_ipv64_add _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_ipv64_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
+ if [ -z "$IPv64_Token" ]; then
+ _err "You must export variable: IPv64_Token"
+ _err "The API Key for your IPv64 account is necessary."
+ _err "You can look it up in your IPv64 account."
+ return 1
+ fi
+
+ # Now save the credentials.
+ _saveaccountconf_mutable IPv64_Token "$IPv64_Token"
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ # convert to lower case
+ _domain="$(echo "$_domain" | _lower_case)"
+ _sub_domain="$(echo "$_sub_domain" | _lower_case)"
+ # Now add the TXT record
+ _info "Trying to add TXT record"
+ if _ipv64_rest "POST" "add_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
+ _info "TXT record has been successfully added."
+ return 0
+ else
+ _err "Errors happened during adding the TXT record, response=$_response"
+ return 1
+ fi
+
+}
+
+#Usage: fulldomain txtvalue
+#Usage: dns_ipv64_rm _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+#Remove the txt record after validation.
+dns_ipv64_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
+ if [ -z "$IPv64_Token" ]; then
+ _err "You must export variable: IPv64_Token"
+ _err "The API Key for your IPv64 account is necessary."
+ _err "You can look it up in your IPv64 account."
+ return 1
+ fi
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ # convert to lower case
+ _domain="$(echo "$_domain" | _lower_case)"
+ _sub_domain="$(echo "$_sub_domain" | _lower_case)"
+ # Now delete the TXT record
+ _info "Trying to delete TXT record"
+ if _ipv64_rest "DELETE" "del_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
+ _info "TXT record has been successfully deleted."
+ return 0
+ else
+ _err "Errors happened during deleting the TXT record, response=$_response"
+ return 1
+ fi
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain="$1"
+ i=1
+ p=1
+
+ _ipv64_get "get_domains"
+ domain_data=$_response
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ #if _contains "$domain_data" "\""$h"\"\:"; then
+ if _contains "$domain_data" "\"""$h""\"\:"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+#send get request to api
+# $1 has to set the api-function
+_ipv64_get() {
+ url="$IPv64_API?$1"
+ export _H1="Authorization: Bearer $IPv64_Token"
+
+ _response=$(_get "$url")
+ _response="$(echo "$_response" | _normalizeJson)"
+
+ if _contains "$_response" "429 Too Many Requests"; then
+ _info "API throttled, sleeping to reset the limit"
+ _sleep 10
+ _response=$(_get "$url")
+ _response="$(echo "$_response" | _normalizeJson)"
+ fi
+}
+
+_ipv64_rest() {
+ url="$IPv64_API"
+ export _H1="Authorization: Bearer $IPv64_Token"
+ export _H2="Content-Type: application/x-www-form-urlencoded"
+ _response=$(_post "$2" "$url" "" "$1")
+
+ if _contains "$_response" "429 Too Many Requests"; then
+ _info "API throttled, sleeping to reset the limit"
+ _sleep 10
+ _response=$(_post "$2" "$url" "" "$1")
+ fi
+
+ if ! _contains "$_response" "\"info\":\"success\""; then
+ return 1
+ fi
+ _debug2 response "$_response"
+ return 0
+}
diff --git a/dnsapi/dns_ispconfig.sh b/dnsapi/dns_ispconfig.sh
index 560f073e..edc789e1 100755
--- a/dnsapi/dns_ispconfig.sh
+++ b/dnsapi/dns_ispconfig.sh
@@ -1,16 +1,21 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_ispconfig_info='ISPConfig Server API
+Site: ISPConfig.org
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ispconfig
+Options:
+ ISPC_User Remote User
+ ISPC_Password Remote Password
+ ISPC_Api API URL. E.g. "https://ispc.domain.tld:8080/remote/json.php"
+ ISPC_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
+'
# ISPConfig 3.1 API
-# User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to:
+# User must provide login data and URL to the ISPConfig installation incl. port.
+# The remote user in ISPConfig must have access to:
# - DNS txt Functions
-
-# Report bugs to https://github.com/sjau/acme.sh
-
-# Values to export:
-# export ISPC_User="remoteUser"
-# export ISPC_Password="remotePassword"
-# export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
-# export ISPC_Api_Insecure=1 # Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
+# - DNS zone functions
+# - Client functions
######## Public functions #####################
diff --git a/dnsapi/dns_jd.sh b/dnsapi/dns_jd.sh
index d0f2a501..4b9067f2 100644
--- a/dnsapi/dns_jd.sh
+++ b/dnsapi/dns_jd.sh
@@ -1,9 +1,14 @@
#!/usr/bin/env sh
-
-#
-#JD_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#JD_ACCESS_KEY_SECRET="xxxxxxx"
-#JD_REGION="cn-north-1"
+# shellcheck disable=SC2034
+dns_jd_info='jdcloud.com
+Site: jdcloud.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_jd
+Options:
+ JD_ACCESS_KEY_ID Access key ID
+ JD_ACCESS_KEY_SECRET Access key secret
+ JD_REGION Region. E.g. "cn-north-1"
+Issues: github.com/acmesh-official/acme.sh/issues/2388
+'
_JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey"
@@ -130,7 +135,7 @@ _get_root() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug2 "Checking domain: $h"
if ! jd_rest GET "domain"; then
_err "error get domain list"
@@ -148,7 +153,7 @@ _get_root() {
if [ "$hostedzone" ]; then
_domain_id="$(echo "$hostedzone" | tr ',' '\n' | grep "\"id\":" | cut -d : -f 2)"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_joker.sh b/dnsapi/dns_joker.sh
index 78399a1d..401471be 100644
--- a/dnsapi/dns_joker.sh
+++ b/dnsapi/dns_joker.sh
@@ -1,27 +1,14 @@
#!/usr/bin/env sh
-
-# Joker.com API for acme.sh
-#
-# This script adds the necessary TXT record to a domain in Joker.com.
-#
-# You must activate Dynamic DNS in Joker.com DNS configuration first.
-# Username and password below refer to Dynamic DNS authentication,
-# not your Joker.com login credentials.
-# See: https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
-#
-# NOTE: This script does not support wildcard certificates, because
-# Joker.com API does not support adding two TXT records with the same
-# subdomain. Adding the second record will overwrite the first one.
-# See: https://joker.com/faq/content/6/496/en/let_s-encrypt-support.html
-# "... this request will replace all TXT records for the specified
-# label by the provided content"
-#
-# Author: aattww (https://github.com/aattww/)
-#
-# Report bugs to https://github.com/acmesh-official/acme.sh/issues/2840
-#
-# JOKER_USERNAME="xxxx"
-# JOKER_PASSWORD="xxxx"
+# shellcheck disable=SC2034
+dns_joker_info='Joker.com
+Site: Joker.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_joker
+Options:
+ JOKER_USERNAME Username
+ JOKER_PASSWORD Password
+Issues: github.com/acmesh-official/acme.sh/issues/2840
+Author: @aattww
+'
JOKER_API="https://svc.joker.com/nic/replace"
@@ -93,7 +80,7 @@ _get_root() {
fulldomain=$1
i=1
while true; do
- h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
diff --git a/dnsapi/dns_kappernet.sh b/dnsapi/dns_kappernet.sh
index 83a7e5f8..762ba8b3 100644
--- a/dnsapi/dns_kappernet.sh
+++ b/dnsapi/dns_kappernet.sh
@@ -1,13 +1,13 @@
#!/usr/bin/env sh
-
-# kapper.net domain api
-# for further questions please contact: support@kapper.net
-# please report issues here: https://github.com/acmesh-official/acme.sh/issues/2977
-
-#KAPPERNETDNS_Key="yourKAPPERNETapikey"
-#KAPPERNETDNS_Secret="yourKAPPERNETapisecret"
-
-KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
+# shellcheck disable=SC2034
+dns_kappernet_info='kapper.net
+Site: kapper.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_kappernet
+Options:
+ KAPPERNETDNS_Key API Key
+ KAPPERNETDNS_Secret API Secret
+Issues: github.com/acmesh-official/acme.sh/issues/2977
+'
###############################################################################
# called with
@@ -19,10 +19,9 @@ dns_kappernet_add() {
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
+ KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
- KAPPERNETDNS_Key=""
- KAPPERNETDNS_Secret=""
_err "Please specify your kapper.net api key and secret."
_err "If you have not received yours - send your mail to"
_err "support@kapper.net to get your key and secret."
@@ -41,12 +40,12 @@ dns_kappernet_add() {
_debug _domain "DOMAIN: $_domain"
_info "Trying to add TXT DNS Record"
- data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
+ data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%22300%22%2C%22prio%22%3A%22%22%7D"
if _kappernet_api GET "action=new&subject=$_domain&data=$data"; then
if _contains "$response" "{\"OK\":true"; then
- _info "Waiting 120 seconds for DNS to spread the new record"
- _sleep 120
+ _info "Waiting 1 second for DNS to spread the new record"
+ _sleep 1
return 0
else
_err "Error creating a TXT DNS Record: $fullhostname TXT $txtvalue"
@@ -66,10 +65,9 @@ dns_kappernet_rm() {
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
+ KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
- KAPPERNETDNS_Key=""
- KAPPERNETDNS_Secret=""
_err "Please specify your kapper.net api key and secret."
_err "If you have not received yours - send your mail to"
_err "support@kapper.net to get your key and secret."
@@ -81,7 +79,7 @@ dns_kappernet_rm() {
_saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret"
_info "Trying to remove the TXT Record: $fullhostname containing $txtvalue"
- data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
+ data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%22300%22%2C%22prio%22%3A%22%22%7D"
if _kappernet_api GET "action=del&subject=$fullhostname&data=$data"; then
if _contains "$response" "{\"OK\":true"; then
return 0
@@ -104,7 +102,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -115,7 +113,7 @@ _get_root() {
if _contains "$response" '"OK":false'; then
_debug "$h not found"
else
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
@@ -141,7 +139,7 @@ _kappernet_api() {
if [ "$method" = "GET" ]; then
response="$(_get "$url")"
else
- _err "Unsupported method"
+ _err "Unsupported method or missing Secret/Key"
return 1
fi
diff --git a/dnsapi/dns_kas.sh b/dnsapi/dns_kas.sh
index 053abd21..2164a8e8 100755
--- a/dnsapi/dns_kas.sh
+++ b/dnsapi/dns_kas.sh
@@ -1,19 +1,16 @@
#!/usr/bin/env sh
-########################################################################
-# All-inkl Kasserver hook script for acme.sh
-#
-# Environment variables:
-#
-# - $KAS_Login (Kasserver API login name)
-# - $KAS_Authtype (Kasserver API auth type. Default: plain)
-# - $KAS_Authdata (Kasserver API auth data.)
-#
-# Last update: squared GmbH
-# Credits:
-# - dns_he.sh. Thanks a lot man!
-# - Martin Kammerlander, Phlegx Systems OG
-# - Marc-Oliver Lange
-# - https://github.com/o1oo11oo/kasapi.sh
+# shellcheck disable=SC2034
+dns_kas_info='All-inkl Kas Server
+Site: kas.all-inkl.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_kas
+Options:
+ KAS_Login API login name
+ KAS_Authtype API auth type. Default: "plain"
+ KAS_Authdata API auth data
+Issues: github.com/acmesh-official/acme.sh/issues/2715
+Author: squared GmbH , Martin Kammerlander , Marc-Oliver Lange
+'
+
########################################################################
KAS_Api_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasApi.wsdl")"
KAS_Api="$(echo "$KAS_Api_GET" | tr -d ' ' | grep -i "//g")"
@@ -215,7 +212,7 @@ _get_record_id() {
return 1
fi
- _record_id="$(echo "$response" | tr -d '\n\r' | sed "s/
- /\n/g" | grep -i "$_record_name" | grep -i ">TXT<" | sed "s/
- record_id<\/key>/=>/g" | sed "s/<\/value><\/item>/\n/g" | grep "=>" | sed "s/=>//g")"
+ _record_id="$(echo "$response" | tr -d '\n\r' | sed "s/
- /\n/g" | grep -i "$_record_name" | grep -i ">TXT<" | sed "s/
- record_id<\/key>/=>/g" | grep -i "$_txtvalue" | sed "s/<\/value><\/item>/\n/g" | grep "=>" | sed "s/=>//g")"
_debug "[KAS] -> Record Id: " "$_record_id"
return 0
}
diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh
index f640242f..0496008e 100644
--- a/dnsapi/dns_kinghost.sh
+++ b/dnsapi/dns_kinghost.sh
@@ -1,16 +1,17 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_kinghost_info='King.host
+Domains: KingHost.net KingHost.com.br
+Site: King.host
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_kinghost
+Options:
+ KINGHOST_Username Username
+ KINGHOST_Password Password
+Author: Felipe Keller Braz
+'
-############################################################
# KingHost API support #
# https://api.kinghost.net/doc/ #
-# #
-# Author: Felipe Keller Braz #
-# Report Bugs here: https://github.com/kinghost/acme.sh #
-# #
-# Values to export: #
-# export KINGHOST_Username="email@provider.com" #
-# export KINGHOST_Password="xxxxxxxxxx" #
-############################################################
KING_Api="https://api.kinghost.net/acme"
diff --git a/dnsapi/dns_knot.sh b/dnsapi/dns_knot.sh
index 729a89cb..5636804a 100644
--- a/dnsapi/dns_knot.sh
+++ b/dnsapi/dns_knot.sh
@@ -1,4 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_knot_info='Knot Server knsupdate
+Site: www.knot-dns.cz/docs/2.5/html/man_knsupdate.html
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_knot
+Options:
+ KNOT_SERVER Server hostname. Default: "localhost".
+ KNOT_KEY File path to TSIG key
+'
+
+# See also dns_nsupdate.sh
######## Public functions #####################
diff --git a/dnsapi/dns_la.sh b/dnsapi/dns_la.sh
index 674df410..f19333c4 100644
--- a/dnsapi/dns_la.sh
+++ b/dnsapi/dns_la.sh
@@ -1,7 +1,13 @@
#!/usr/bin/env sh
-
-#LA_Id="test123"
-#LA_Key="d1j2fdo4dee3948"
+# shellcheck disable=SC2034
+dns_la_info='dns.la
+Site: dns.la
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_la
+Options:
+ LA_Id API ID
+ LA_Key API key
+Issues: github.com/acmesh-official/acme.sh/issues/4257
+'
LA_Api="https://api.dns.la/api"
@@ -107,7 +113,7 @@ _get_root() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -120,7 +126,7 @@ _get_root() {
if _contains "$response" '"domainid":'; then
_domain_id=$(printf "%s" "$response" | grep '"domainid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh
index 63f81869..66b1f61f 100644
--- a/dnsapi/dns_leaseweb.sh
+++ b/dnsapi/dns_leaseweb.sh
@@ -1,12 +1,18 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_leaseweb_info='Leaseweb.com
+Site: Leaseweb.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_leaseweb
+Options:
+ LSW_Key API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2558
+Author: Rolph Haspers
+'
-#Author: Rolph Haspers
-#Utilize leaseweb.com API to finish dns-01 verifications.
-#Requires a Leaseweb API Key (export LSW_Key="Your Key")
#See https://developer.leaseweb.com for more information.
######## Public functions #####################
-LSW_API="https://api.leaseweb.com/hosting/v2/domains/"
+LSW_API="https://api.leaseweb.com/hosting/v2/domains"
#Usage: dns_leaseweb_add _acme-challenge.www.domain.com
dns_leaseweb_add() {
diff --git a/dnsapi/dns_lexicon.sh b/dnsapi/dns_lexicon.sh
index 19702343..a4b2a801 100755
--- a/dnsapi/dns_lexicon.sh
+++ b/dnsapi/dns_lexicon.sh
@@ -1,8 +1,12 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_lexicon_info='Lexicon DNS client
+Site: github.com/AnalogJ/lexicon
+Docs: github.com/acmesh-official/acme.sh/wiki/How-to-use-lexicon-DNS-API
+Options:
+ PROVIDER Provider
+'
-# dns api wrapper of lexicon for acme.sh
-
-# https://github.com/AnalogJ/lexicon
lexicon_cmd="lexicon"
wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-lexicon-dns-api"
diff --git a/dnsapi/dns_limacity.sh b/dnsapi/dns_limacity.sh
new file mode 100644
index 00000000..5734be9e
--- /dev/null
+++ b/dnsapi/dns_limacity.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_limacity_info='lima-city.de
+Site: www.lima-city.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_limacity
+Options:
+ LIMACITY_APIKEY API Key. Note: The API Key must have following roles: dns.admin, domains.reader
+Issues: github.com/acmesh-official/acme.sh/issues/4758
+Author: @Laraveluser
+'
+
+######## Public functions #####################
+
+LIMACITY_APIKEY="${LIMACITY_APIKEY:-$(_readaccountconf_mutable LIMACITY_APIKEY)}"
+AUTH=$(printf "%s" "api:$LIMACITY_APIKEY" | _base64 -w 0)
+export _H1="Authorization: Basic $AUTH"
+export _H2="Content-Type: application/json"
+APIBASE=https://www.lima-city.de/usercp
+
+#Usage: dns_limacity_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_limacity_add() {
+ _debug LIMACITY_APIKEY "$LIMACITY_APIKEY"
+ if [ "$LIMACITY_APIKEY" = "" ]; then
+ _err "No Credentials given"
+ return 1
+ fi
+
+ # save the dns server and key to the account conf file.
+ _saveaccountconf_mutable LIMACITY_APIKEY "${LIMACITY_APIKEY}"
+
+ fulldomain=$1
+ txtvalue=$2
+ if ! _lima_get_domain_id "$fulldomain"; then return 1; fi
+
+ msg=$(_post "{\"nameserver_record\":{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\",\"ttl\":60}}" "${APIBASE}/domains/${LIMACITY_DOMAINID}/records.json" "" "POST")
+ _debug "$msg"
+
+ if [ "$(echo "$msg" | _egrep_o "\"status\":\"ok\"")" = "" ]; then
+ _err "$msg"
+ return 1
+ fi
+
+ return 0
+}
+
+#Usage: dns_limacity_rm _acme-challenge.www.domain.com
+dns_limacity_rm() {
+
+ fulldomain=$1
+ txtvalue=$2
+ if ! _lima_get_domain_id "$fulldomain"; then return 1; fi
+
+ for recordId in $(_get "${APIBASE}/domains/${LIMACITY_DOMAINID}/records.json" | _egrep_o "{\"id\":[0-9]*[^}]*,\"name\":\"${fulldomain}\"" | _egrep_o "[0-9]*"); do
+ _post "" "${APIBASE}/domains/${LIMACITY_DOMAINID}/records/${recordId}" "" "DELETE"
+ done
+
+ return 0
+}
+
+#################### Private functions below ##################################
+
+_lima_get_domain_id() {
+ domain="$1"
+ _debug "$domain"
+ i=2
+ p=1
+
+ domains=$(_get "${APIBASE}/domains.json")
+ if [ "$(echo "$domains" | _egrep_o "\{.*""domains""")" ]; then
+ response="$(echo "$domains" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | _egrep_o "\{.*""unicode_fqdn""[^,]+""$h"".*\}")"
+ if [ "$hostedzone" ]; then
+ LIMACITY_DOMAINID=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$LIMACITY_DOMAINID" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh
index ead5b164..d74d1fc8 100755
--- a/dnsapi/dns_linode.sh
+++ b/dnsapi/dns_linode.sh
@@ -1,6 +1,12 @@
#!/usr/bin/env sh
-
-#Author: Philipp Grosswiler
+# shellcheck disable=SC2034
+dns_linode_info='Linode.com (Old)
+ Deprecated. Use dns_linode_v4
+Site: Linode.com
+Options:
+ LINODE_API_KEY API Key
+Author: Philipp Grosswiler
+'
LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
@@ -130,7 +136,7 @@ _get_root() {
if _rest GET "domain.list"; then
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -141,7 +147,7 @@ _get_root() {
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_linode_v4.sh b/dnsapi/dns_linode_v4.sh
index 9504afbf..3c6997a0 100755
--- a/dnsapi/dns_linode_v4.sh
+++ b/dnsapi/dns_linode_v4.sh
@@ -1,7 +1,12 @@
#!/usr/bin/env sh
-
-#Original Author: Philipp Grosswiler
-#v4 Update Author: Aaron W. Swenson
+# shellcheck disable=SC2034
+dns_linode_v4_info='Linode.com
+Site: Linode.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_linode_v4
+Options:
+ LINODE_V4_API_KEY API Key
+Author: Philipp Grosswiler , Aaron W. Swenson
+'
LINODE_V4_API_URL="https://api.linode.com/v4/domains"
@@ -71,7 +76,7 @@ dns_linode_v4_rm() {
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
+ if _H4="X-Filter: { \"type\": \"TXT\", \"name\": \"$_sub_domain\" }" _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
resource="$(echo "$response" | _egrep_o "\{.*\"name\": *\"$_sub_domain\".*}")"
@@ -126,34 +131,42 @@ _Linode_API() {
# _domain=domain.com
# _domain_id=12345
_get_root() {
- domain=$1
+ full_host_str="$1"
+
i=2
p=1
+ while true; do
+ # loop through the received string (e.g. _acme-challenge.sub3.sub2.sub1.domain.tld),
+ # starting from the lowest subdomain, and check if it's a hosted domain
+ tst_hosted_domain=$(printf "%s" "$full_host_str" | cut -d . -f "$i"-100)
+ _debug tst_hosted_domain "$tst_hosted_domain"
+ if [ -z "$tst_hosted_domain" ]; then
+ #not valid
+ _err "Couldn't get domain from string '$full_host_str'."
+ return 1
+ fi
- if _rest GET; then
- response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
- while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
- _debug h "$h"
- if [ -z "$h" ]; then
- #not valid
- return 1
- fi
-
- hostedzone="$(echo "$response" | _egrep_o "\{.*\"domain\": *\"$h\".*}")"
+ _debug "Querying Linode APIv4 for hosted zone: $tst_hosted_domain"
+ if _H4="X-Filter: {\"domain\":\"$tst_hosted_domain\"}" _rest GET; then
+ _debug "Got response from API: $response"
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+ hostedzone="$(echo "$response" | _egrep_o "\{.*\"domain\": *\"$tst_hosted_domain\".*}")"
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ _debug "Found domain hosted on Linode DNS. Zone: $tst_hosted_domain, id: $_domain_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
- _domain=$h
+ _sub_domain=$(printf "%s" "$full_host_str" | cut -d . -f 1-"$p")
+ _domain=$tst_hosted_domain
return 0
fi
return 1
fi
+
p=$i
i=$(_math "$i" + 1)
- done
- fi
+ fi
+ done
+
return 1
}
diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh
index 399c7867..98a4f3ab 100644
--- a/dnsapi/dns_loopia.sh
+++ b/dnsapi/dns_loopia.sh
@@ -1,11 +1,13 @@
#!/usr/bin/env sh
-
-#
-#LOOPIA_User="username"
-#
-#LOOPIA_Password="password"
-#
-#LOOPIA_Api="https://api.loopia./RPCSERV"
+# shellcheck disable=SC2034
+dns_loopia_info='Loopia.se
+Site: Loopia.se
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_loopia
+Options:
+ LOOPIA_Api API URL. E.g. "https://api.loopia./RPCSERV" where the is one of: com, no, rs, se. Default: "se".
+ LOOPIA_User Username
+ LOOPIA_Password Password
+'
LOOPIA_Api_Default="https://api.loopia.se/RPCSERV"
@@ -107,7 +109,7 @@ _loopia_load_config() {
fi
if _contains "$LOOPIA_Password" "'" || _contains "$LOOPIA_Password" '"'; then
- _err "Password contains quoute or double quoute and this is not supported by dns_loopia.sh"
+ _err "Password contains a quotation mark or double quotation marks and this is not supported by dns_loopia.sh"
return 1
fi
@@ -178,14 +180,14 @@ _get_root() {
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
while true; do
- h=$(echo "$domain" | cut -d . -f $i-100)
+ h=$(echo "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "$h"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh
index 30c15579..34cce6a1 100755
--- a/dnsapi/dns_lua.sh
+++ b/dnsapi/dns_lua.sh
@@ -1,11 +1,14 @@
#!/usr/bin/env sh
-
-# bug reports to dev@1e.ca
-
-#
-#LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#LUA_Email="user@luadns.net"
+# shellcheck disable=SC2034
+dns_lua_info='LuaDNS.com
+Domains: LuaDNS.net
+Site: LuaDNS.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_lua
+Options:
+ LUA_Key API key
+ LUA_Email Email
+Author:
+'
LUA_Api="https://api.luadns.com/v1"
@@ -107,7 +110,7 @@ _get_root() {
return 1
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -118,7 +121,7 @@ _get_root() {
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_maradns.sh b/dnsapi/dns_maradns.sh
index 4ff6ca2d..9eefb175 100755
--- a/dnsapi/dns_maradns.sh
+++ b/dnsapi/dns_maradns.sh
@@ -1,4 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_maradns_info='MaraDNS Server
+Site: MaraDNS.samiam.org
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_maradns
+Options:
+ MARA_ZONE_FILE Zone file path. E.g. "/etc/maradns/db.domain.com"
+ MARA_DUENDE_PID_PATH Duende PID Path. E.g. "/run/maradns/etc_maradns_mararc.pid"
+Issues: github.com/acmesh-official/acme.sh/issues/2072
+'
#Usage: dns_maradns_add _acme-challenge.www.domain.com "token"
dns_maradns_add() {
@@ -63,7 +72,7 @@ _reload_maradns() {
pidpath="$1"
kill -s HUP -- "$(cat "$pidpath")"
if [ $? -ne 0 ]; then
- _err "Unable to reload MaraDNS, kill returned $?"
+ _err "Unable to reload MaraDNS, kill returned"
return 1
fi
}
diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh
index 49007402..43c903cd 100644
--- a/dnsapi/dns_me.sh
+++ b/dnsapi/dns_me.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-# bug reports to dev@1e.ca
-
-# ME_Key=qmlkdjflmkqdjf
-# ME_Secret=qmsdlkqmlksdvnnpae
+# shellcheck disable=SC2034
+dns_me_info='DnsMadeEasy.com
+Site: DnsMadeEasy.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_me
+Options:
+ ME_Key API Key
+ ME_Secret API Secret
+Author:
+'
ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed
@@ -103,7 +107,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -116,7 +120,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | sed 's/^{//; s/}$//; s/{.*}//' | sed -r 's/^.*"id":([0-9]+).*$/\1/')
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh
index dad69bde..0824a4e7 100644
--- a/dnsapi/dns_miab.sh
+++ b/dnsapi/dns_miab.sh
@@ -1,24 +1,23 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_miab_info='Mail-in-a-Box
+Site: MailInaBox.email
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_miab
+Options:
+ MIAB_Username Admin username
+ MIAB_Password Admin password
+ MIAB_Server Server hostname. FQDN of your_MIAB Server
+Issues: github.com/acmesh-official/acme.sh/issues/2550
+Author: Darven Dissek, William Gertz
+'
-# Name: dns_miab.sh
-#
-# Authors:
-# Darven Dissek 2018
-# William Gertz 2019
-#
-# Thanks to Neil Pang and other developers here for code reused from acme.sh from DNS-01
-# used to communicate with the MailinaBox Custom DNS API
-# Report Bugs here:
-# https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh)
-# https://github.com/acmesh-official/acme.sh (for acme.sh)
-#
######## Public functions #####################
#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_miab_add() {
fulldomain=$1
txtvalue=$2
- _info "Using miab challange add"
+ _info "Using miab challenge add"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
@@ -27,7 +26,7 @@ dns_miab_add() {
return 1
fi
- #check domain and seperate into doamin and host
+ #check domain and seperate into domain and host
if ! _get_root "$fulldomain"; then
_err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
return 1
@@ -56,7 +55,7 @@ dns_miab_rm() {
fulldomain=$1
txtvalue=$2
- _info "Using miab challage delete"
+ _info "Using miab challenge delete"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
@@ -113,7 +112,7 @@ _get_root() {
#cycle through the passed domain seperating out a test domain discarding
# the subdomain by marching thorugh the dots
while true; do
- _test_domain=$(printf "%s" "$_passed_domain" | cut -d . -f ${_i}-100)
+ _test_domain=$(printf "%s" "$_passed_domain" | cut -d . -f "${_i}"-100)
_debug _test_domain "$_test_domain"
if [ -z "$_test_domain" ]; then
@@ -123,7 +122,7 @@ _get_root() {
#report found if the test domain is in the json response and
# report the subdomain
if _contains "$response" "\"$_test_domain\""; then
- _sub_domain=$(printf "%s" "$_passed_domain" | cut -d . -f 1-${_p})
+ _sub_domain=$(printf "%s" "$_passed_domain" | cut -d . -f 1-"${_p}")
_domain=${_test_domain}
return 0
fi
diff --git a/dnsapi/dns_mijnhost.sh b/dnsapi/dns_mijnhost.sh
new file mode 100644
index 00000000..9f5e7710
--- /dev/null
+++ b/dnsapi/dns_mijnhost.sh
@@ -0,0 +1,214 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_mijnhost_info='mijn.host
+Site: mijn.host
+Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_mijnhost
+Options:
+ MIJNHOST_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/6177
+Author: @peterv99
+'
+
+######## Public functions ######################
+MIJNHOST_API="https://mijn.host/api/v2"
+
+# Add TXT record for domain verification
+dns_mijnhost_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ MIJNHOST_API_KEY="${MIJNHOST_API_KEY:-$(_readaccountconf_mutable MIJNHOST_API_KEY)}"
+ if [ -z "$MIJNHOST_API_KEY" ]; then
+ MIJNHOST_API_KEY=""
+ _err "You haven't specified your mijn-host API key yet."
+ _err "Please add MIJNHOST_API_KEY to the env."
+ return 1
+ fi
+
+ # Save the API key for future use
+ _saveaccountconf_mutable MIJNHOST_API_KEY "$MIJNHOST_API_KEY"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _debug2 _sub_domain "$_sub_domain"
+ _debug2 _domain "$_domain"
+ _debug "Adding DNS record" "${fulldomain}."
+
+ # Construct the API URL
+ api_url="$MIJNHOST_API/domains/$_domain/dns"
+
+ # Getting previous records
+ _mijnhost_rest GET "$api_url" ""
+
+ if [ "$_code" != "200" ]; then
+ _err "Error getting current DNS enties ($_code)"
+ return 1
+ fi
+
+ records=$(echo "$response" | _egrep_o '"records":\[.*\]' | sed 's/"records"://')
+
+ _debug2 "Current records" "$records"
+
+ # Build the payload for the API
+ data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"ttl\":300}"
+
+ _debug2 "Record to add" "$data"
+
+ # Updating the records
+ updated_records=$(echo "$records" | sed -E "s/\]( *$)/,$data\]/")
+
+ _debug2 "Updated records" "$updated_records"
+
+ # data
+ data="{\"records\": $updated_records}"
+
+ _mijnhost_rest PUT "$api_url" "$data"
+
+ if [ "$_code" = "200" ]; then
+ _info "DNS record succesfully added."
+ return 0
+ else
+ _err "Error adding DNS record ($_code)."
+ return 1
+ fi
+}
+
+# Remove TXT record after verification
+dns_mijnhost_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ MIJNHOST_API_KEY="${MIJNHOST_API_KEY:-$(_readaccountconf_mutable MIJNHOST_API_KEY)}"
+ if [ -z "$MIJNHOST_API_KEY" ]; then
+ MIJNHOST_API_KEY=""
+ _err "You haven't specified your mijn-host API key yet."
+ _err "Please add MIJNHOST_API_KEY to the env."
+ return 1
+ fi
+
+ _debug "Detecting root zone for" "${fulldomain}."
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _debug "Removing DNS record for TXT value" "${txtvalue}."
+
+ # Construct the API URL
+ api_url="$MIJNHOST_API/domains/$_domain/dns"
+
+ # Get current records
+ _mijnhost_rest GET "$api_url" ""
+
+ if [ "$_code" != "200" ]; then
+ _err "Error getting current DNS enties ($_code)"
+ return 1
+ fi
+
+ _debug2 "Get current records response:" "$response"
+
+ records=$(echo "$response" | _egrep_o '"records":\[.*\]' | sed 's/"records"://')
+
+ _debug2 "Current records:" "$records"
+
+ updated_records=$(echo "$records" | sed -E "s/\{[^}]*\"value\":\"$txtvalue\"[^}]*\},?//g" | sed 's/,]/]/g')
+
+ _debug2 "Updated records:" "$updated_records"
+
+ # Build the new payload
+ data="{\"records\": $updated_records}"
+
+ # Use the _put method to update the records
+ _mijnhost_rest PUT "$api_url" "$data"
+
+ if [ "$_code" = "200" ]; then
+ _info "DNS record removed successfully."
+ return 0
+ else
+ _err "Error removing DNS record ($_code)."
+ return 1
+ fi
+}
+
+# Helper function to detect the root zone
+_get_root() {
+ domain=$1
+
+ # Get current records
+ _debug "Getting current domains"
+ _mijnhost_rest GET "$MIJNHOST_API/domains" ""
+
+ if [ "$_code" != "200" ]; then
+ _err "error getting current domains ($_code)"
+ return 1
+ fi
+
+ # Extract root domains from response
+ rootDomains=$(echo "$response" | _egrep_o '"domain":"[^"]*"' | sed -E 's/"domain":"([^"]*)"/\1/')
+ _debug "Root domains:" "$rootDomains"
+
+ for rootDomain in $rootDomains; do
+ if _contains "$domain" "$rootDomain"; then
+ _domain="$rootDomain"
+ _sub_domain=$(echo "$domain" | sed "s/.$rootDomain//g")
+ _debug "Found root domain" "$_domain" "and subdomain" "$_sub_domain" "for" "$domain"
+ return 0
+ fi
+ done
+ return 1
+}
+
+# Helper function for rest calls
+_mijnhost_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+
+ MAX_REQUEST_RETRY_TIMES=15
+ _request_retry_times=0
+ _retry_sleep=5 #Initial sleep time in seconds.
+
+ while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
+ _debug2 _request_retry_times "$_request_retry_times"
+ export _H1="API-Key: $MIJNHOST_API_KEY"
+ export _H2="Content-Type: application/json"
+ # clear headers from previous request to avoid getting wrong http code on timeouts
+ : >"$HTTP_HEADER"
+ _debug "$ep"
+ if [ "$m" != "GET" ]; then
+ _debug2 "data $data"
+ response="$(_post "$data" "$ep" "" "$m")"
+ else
+ response="$(_get "$ep")"
+ fi
+ _ret="$?"
+ _debug2 "response $response"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ _debug "http response code $_code"
+ if [ "$_code" = "401" ]; then
+ # we have an invalid API token, maybe it is expired?
+ _err "Access denied. Invalid API token."
+ return 1
+ fi
+
+ if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "400" ] || _contains "$response" "DNS records not managed by mijn.host"; then #Sometimes API errors out
+ _request_retry_times="$(_math "$_request_retry_times" + 1)"
+ _info "REST call error $_code retrying $ep in ${_retry_sleep}s"
+ _sleep "$_retry_sleep"
+ _retry_sleep="$(_math "$_retry_sleep" \* 2)"
+ continue
+ fi
+ break
+ done
+ if [ "$_request_retry_times" = "$MAX_REQUEST_RETRY_TIMES" ]; then
+ _err "Error mijn.host API call was retried $MAX_REQUEST_RETRY_TIMES times."
+ _err "Calling $ep failed."
+ return 1
+ fi
+ response="$(echo "$response" | _normalizeJson)"
+ return 0
+}
diff --git a/dnsapi/dns_misaka.sh b/dnsapi/dns_misaka.sh
index 36ba5cfd..50ed4360 100755
--- a/dnsapi/dns_misaka.sh
+++ b/dnsapi/dns_misaka.sh
@@ -1,11 +1,12 @@
#!/usr/bin/env sh
-
-# bug reports to support+acmesh@misaka.io
-# based on dns_nsone.sh by dev@1e.ca
-
-#
-#Misaka_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
+# shellcheck disable=SC2034
+dns_misaka_info='Misaka.io
+Site: Misaka.io
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_misaka
+Options:
+ Misaka_Key API Key
+Author:
+'
Misaka_Api="https://dnsapi.misaka.io/dns"
@@ -115,7 +116,7 @@ _get_root() {
return 1
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -123,7 +124,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\""; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_myapi.sh b/dnsapi/dns_myapi.sh
index 7f3c5a86..101854d5 100755
--- a/dnsapi/dns_myapi.sh
+++ b/dnsapi/dns_myapi.sh
@@ -1,14 +1,23 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_myapi_info='Custom API Example
+ A sample custom DNS API script description.
+Domains: example.com example.net
+Site: github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_myapi
+Options:
+ MYAPI_Token API Token. Get API Token from https://example.com/api/
+ MYAPI_Variable2 Option 2. Default "default value".
+ MYAPI_Variable2 Option 3. Optional.
+Issues: github.com/acmesh-official/acme.sh
+Author: Neil Pang
+'
-#Here is a sample custom api script.
#This file name is "dns_myapi.sh"
#So, here must be a method dns_myapi_add()
#Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error.
-#
-#Author: Neilpang
-#Report Bugs here: https://github.com/acmesh-official/acme.sh
-#
+
######## Public functions #####################
# Please Read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
diff --git a/dnsapi/dns_mydevil.sh b/dnsapi/dns_mydevil.sh
index 953290af..e9b3d3c8 100755
--- a/dnsapi/dns_mydevil.sh
+++ b/dnsapi/dns_mydevil.sh
@@ -1,15 +1,16 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_mydevil_info='MyDevil.net
+ MyDevil.net already supports automatic Lets Encrypt certificates,
+ except for wildcard domains.
+ This script depends on devil command that MyDevil.net provides,
+ which means that it works only on server side.
+Site: MyDevil.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mydevil
+Issues: github.com/acmesh-official/acme.sh/issues/2079
+Author: Marcin Konicki
+'
-# MyDevil.net API (2019-02-03)
-#
-# MyDevil.net already supports automatic Let's Encrypt certificates,
-# except for wildcard domains.
-#
-# This script depends on `devil` command that MyDevil.net provides,
-# which means that it works only on server side.
-#
-# Author: Marcin Konicki
-#
######## Public functions #####################
#Usage: dns_mydevil_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
diff --git a/dnsapi/dns_mydnsjp.sh b/dnsapi/dns_mydnsjp.sh
index 13866f70..4dfffaaa 100755
--- a/dnsapi/dns_mydnsjp.sh
+++ b/dnsapi/dns_mydnsjp.sh
@@ -1,14 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_mydnsjp_info='MyDNS.JP
+Site: MyDNS.JP
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mydnsjp
+Options:
+ MYDNSJP_MasterID Master ID
+ MYDNSJP_Password Password
+Author: @tkmsst
+'
-#Here is a api script for MyDNS.JP.
-#This file name is "dns_mydnsjp.sh"
-#So, here must be a method dns_mydnsjp_add()
-#Which will be called by acme.sh to add the txt record to your api system.
-#returns 0 means success, otherwise error.
-#
-#Author: epgdatacapbon
-#Report Bugs here: https://github.com/epgdatacapbon/acme.sh
-#
######## Public functions #####################
# Export MyDNS.JP MasterID and Password in following variables...
@@ -126,7 +126,7 @@ _get_root() {
fi
while true; do
- _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+ _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100)
if [ -z "$_domain" ]; then
# not valid
@@ -134,7 +134,7 @@ _get_root() {
fi
if [ "$_domain" = "$_root_domain" ]; then
- _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$p")
return 0
fi
diff --git a/dnsapi/dns_mythic_beasts.sh b/dnsapi/dns_mythic_beasts.sh
index 294ae84c..1529e1e7 100755
--- a/dnsapi/dns_mythic_beasts.sh
+++ b/dnsapi/dns_mythic_beasts.sh
@@ -1,4 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_mythic_beasts_info='Mythic-Beasts.com
+Site: Mythic-Beasts.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_mythic_beasts
+Options:
+ MB_AK API Key
+ MB_AS API Secret
+Issues: github.com/acmesh-official/acme.sh/issues/3848
+'
# Mythic Beasts is a long-standing UK service provider using standards-based OAuth2 authentication
# To test: ./acme.sh --dns dns_mythic_beasts --test --debug 1 --output-insecure --issue --domain domain.com
# Cannot retest once cert is issued
@@ -98,7 +107,7 @@ _get_root() {
_debug "Detect the root zone"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
_err "Domain exhausted"
return 1
@@ -109,7 +118,7 @@ _get_root() {
_mb_rest GET "$h/records"
ret="$?"
if [ "$ret" -eq 0 ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh
index a5f667a9..5527b357 100755
--- a/dnsapi/dns_namecheap.sh
+++ b/dnsapi/dns_namecheap.sh
@@ -1,12 +1,17 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_namecheap_info='NameCheap.com
+Site: NameCheap.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namecheap
+Options:
+ NAMECHEAP_API_KEY API Key
+ NAMECHEAP_USERNAME Username
+ NAMECHEAP_SOURCEIP Source IP
+Issues: github.com/acmesh-official/acme.sh/issues/2107
+'
# Namecheap API
# https://www.namecheap.com/support/api/intro.aspx
-#
-# Requires Namecheap API key set in
-#NAMECHEAP_API_KEY,
-#NAMECHEAP_USERNAME,
-#NAMECHEAP_SOURCEIP
# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
######## Public functions #####################
@@ -104,7 +109,7 @@ _get_root_by_getList() {
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -118,7 +123,7 @@ _get_root_by_getList() {
if ! _contains "$response" "$h"; then
_debug "$h not found"
else
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
@@ -132,14 +137,14 @@ _get_root_by_getHosts() {
i=100
p=99
- while [ $p -ne 0 ]; do
+ while [ "$p" -ne 0 ]; do
- h=$(printf "%s" "$1" | cut -d . -f $i-100)
+ h=$(printf "%s" "$1" | cut -d . -f "$i"-100)
if [ -n "$h" ]; then
if _contains "$h" "\\."; then
_debug h "$h"
if _namecheap_set_tld_sld "$h"; then
- _sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$1" | cut -d . -f 1-"$p")
_domain="$h"
return 0
else
@@ -373,7 +378,7 @@ _namecheap_set_tld_sld() {
while true; do
- _tld=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _tld=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug tld "$_tld"
if [ -z "$_tld" ]; then
diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh
index 0d5dd2c4..1062c849 100755
--- a/dnsapi/dns_namecom.sh
+++ b/dnsapi/dns_namecom.sh
@@ -1,9 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_namecom_info='Name.com
+Site: Name.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namecom
+Options:
+ Namecom_Username Username
+ Namecom_Token API Token
+Author: @RaidenII
+'
-#Author: RaidenII
-#Created 06/28/2017
-#Updated 03/01/2018, rewrote to support name.com API v4
-#Utilize name.com API to finish dns-01 verifications.
######## Public functions #####################
Namecom_API="https://api.name.com/v4"
@@ -154,15 +159,15 @@ _namecom_get_root() {
# Need to exclude the last field (tld)
numfields=$(echo "$domain" | _egrep_o "\." | wc -l)
- while [ $i -le "$numfields" ]; do
- host=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ while [ "$i" -le "$numfields" ]; do
+ host=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug host "$host"
if [ -z "$host" ]; then
return 1
fi
if _contains "$response" "$host"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$host"
return 0
fi
diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh
index f961d0bd..5d47a59a 100755
--- a/dnsapi/dns_namesilo.sh
+++ b/dnsapi/dns_namesilo.sh
@@ -1,8 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_namesilo_info='NameSilo.com
+Site: NameSilo.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namesilo
+Options:
+ Namesilo_Key API Key
+Author: @meowthink
+'
-#Author: meowthink
-#Created 01/14/2017
-#Utilize namesilo.com API to finish dns-01 verifications.
+#Utilize API to finish dns-01 verifications.
Namesilo_API="https://www.namesilo.com/api"
@@ -103,15 +109,15 @@ _get_root() {
# Need to exclude the last field (tld)
numfields=$(echo "$domain" | _egrep_o "\." | wc -l)
- while [ $i -le "$numfields" ]; do
- host=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ while [ "$i" -le "$numfields" ]; do
+ host=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug host "$host"
if [ -z "$host" ]; then
return 1
fi
if _contains "$response" ">$host"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$host"
return 0
fi
diff --git a/dnsapi/dns_nanelo.sh b/dnsapi/dns_nanelo.sh
new file mode 100644
index 00000000..1ab47a89
--- /dev/null
+++ b/dnsapi/dns_nanelo.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_nanelo_info='Nanelo.com
+Site: Nanelo.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_nanelo
+Options:
+ NANELO_TOKEN API Token
+Issues: github.com/acmesh-official/acme.sh/issues/4519
+'
+
+NANELO_API="https://api.nanelo.com/v1/"
+
+######## Public functions #####################
+
+# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nanelo_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
+ if [ -z "$NANELO_TOKEN" ]; then
+ NANELO_TOKEN=""
+ _err "You didn't configure a Nanelo API Key yet."
+ _err "Please set NANELO_TOKEN and try again."
+ _err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
+ return 1
+ fi
+ _saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
+
+ _info "Adding TXT record to ${fulldomain}"
+ response="$(_get "$NANELO_API$NANELO_TOKEN/dns/addrecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
+ if _contains "${response}" 'success'; then
+ return 0
+ fi
+ _err "Could not create resource record, please check the logs"
+ _err "${response}"
+ return 1
+}
+
+dns_nanelo_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
+ if [ -z "$NANELO_TOKEN" ]; then
+ NANELO_TOKEN=""
+ _err "You didn't configure a Nanelo API Key yet."
+ _err "Please set NANELO_TOKEN and try again."
+ _err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
+ return 1
+ fi
+ _saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
+
+ _info "Deleting resource record $fulldomain"
+ response="$(_get "$NANELO_API$NANELO_TOKEN/dns/deleterecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
+ if _contains "${response}" 'success'; then
+ return 0
+ fi
+ _err "Could not delete resource record, please check the logs"
+ _err "${response}"
+ return 1
+}
diff --git a/dnsapi/dns_nederhost.sh b/dnsapi/dns_nederhost.sh
index abaae42b..b16c36ec 100755
--- a/dnsapi/dns_nederhost.sh
+++ b/dnsapi/dns_nederhost.sh
@@ -1,6 +1,12 @@
#!/usr/bin/env sh
-
-#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghj"
+# shellcheck disable=SC2034
+dns_nederhost_info='NederHost.nl
+Site: NederHost.nl
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_nederhost
+Options:
+ NederHost_Key API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2089
+'
NederHost_Api="https://api.nederhost.nl/dns/v1"
@@ -82,8 +88,8 @@ _get_root() {
i=2
p=1
while true; do
- _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_debug _domain "$_domain"
if [ -z "$_domain" ]; then
#not valid
diff --git a/dnsapi/dns_neodigit.sh b/dnsapi/dns_neodigit.sh
index 64ea8786..a31f8c9b 100644
--- a/dnsapi/dns_neodigit.sh
+++ b/dnsapi/dns_neodigit.sh
@@ -1,13 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_neodigit_info='Neodigit.net
+Site: Neodigit.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_neodigit
+Options:
+ NEODIGIT_API_TOKEN API Token
+Author: Adrian Almenar
+'
-#
-# NEODIGIT_API_TOKEN="jasdfhklsjadhflnhsausdfas"
-
-# This is Neodigit.net api wrapper for acme.sh
-#
-# Author: Adrian Almenar
-# Report Bugs here: https://github.com/tecnocratica/acme.sh
-#
NEODIGIT_API_URL="https://api.neodigit.net/v1"
#
######## Public functions #####################
@@ -126,7 +126,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -142,7 +142,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh
index 776fa02d..8609adf6 100644
--- a/dnsapi/dns_netcup.sh
+++ b/dnsapi/dns_netcup.sh
@@ -1,5 +1,15 @@
#!/usr/bin/env sh
-#developed by linux-insideDE
+# shellcheck disable=SC2034
+dns_netcup_info='netcup.eu
+Domains: netcup.de netcup.net
+Site: netcup.eu/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_netcup
+Options:
+ NC_Apikey API Key
+ NC_Apipw API Password
+ NC_CID Customer Number
+Author: linux-insideDE
+'
NC_Apikey="${NC_Apikey:-$(_readaccountconf_mutable NC_Apikey)}"
NC_Apipw="${NC_Apipw:-$(_readaccountconf_mutable NC_Apipw)}"
@@ -9,7 +19,7 @@ client=""
dns_netcup_add() {
_debug NC_Apikey "$NC_Apikey"
- login
+ _login
if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then
_err "No Credentials given"
return 1
@@ -51,7 +61,7 @@ dns_netcup_add() {
}
dns_netcup_rm() {
- login
+ _login
fulldomain=$1
txtvalue=$2
@@ -115,7 +125,7 @@ dns_netcup_rm() {
logout
}
-login() {
+_login() {
tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4)
_debug "$tmp"
diff --git a/dnsapi/dns_netlify.sh b/dnsapi/dns_netlify.sh
index 0e5dc327..322f10ad 100644
--- a/dnsapi/dns_netlify.sh
+++ b/dnsapi/dns_netlify.sh
@@ -1,6 +1,12 @@
#!/usr/bin/env sh
-
-#NETLIFY_ACCESS_TOKEN="xxxx"
+# shellcheck disable=SC2034
+dns_netlify_info='Netlify.com
+Site: Netlify.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_netlify
+Options:
+ NETLIFY_ACCESS_TOKEN API Token
+Issues: github.com/acmesh-official/acme.sh/issues/3088
+'
NETLIFY_HOST="api.netlify.com/api/v1/"
NETLIFY_URL="https://$NETLIFY_HOST"
@@ -49,8 +55,6 @@ dns_netlify_add() {
return 1
fi
- _err "Not fully implemented!"
- return 1
}
#Usage: dns_myapi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -89,7 +93,6 @@ dns_netlify_rm() {
_err "error removing validation value ($_code)"
return 1
fi
- return 0
fi
return 1
}
@@ -105,7 +108,7 @@ _get_root() {
_netlify_rest GET "dns_zones" "" "$accesstoken"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then
#not valid
@@ -120,7 +123,7 @@ _get_root() {
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
_sub_domain="@"
else
- _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
fi
_domain=$h
return 0
diff --git a/dnsapi/dns_nic.sh b/dnsapi/dns_nic.sh
index 56170f87..5f3e7d5d 100644
--- a/dnsapi/dns_nic.sh
+++ b/dnsapi/dns_nic.sh
@@ -1,10 +1,15 @@
#!/usr/bin/env sh
-
-#
-#NIC_ClientID='0dc0xxxxxxxxxxxxxxxxxxxxxxxxce88'
-#NIC_ClientSecret='3LTtxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnuW8'
-#NIC_Username="000000/NIC-D"
-#NIC_Password="xxxxxxx"
+# shellcheck disable=SC2034
+dns_nic_info='nic.ru
+Site: nic.ru
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_nic
+Options:
+ NIC_ClientID Client ID
+ NIC_ClientSecret Client Secret
+ NIC_Username Username
+ NIC_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2547
+'
NIC_Api="https://api.nic.ru"
@@ -164,7 +169,7 @@ _get_root() {
fi
if _contains "$_all_domains" "^$h$"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
_service=$(printf "%s" "$response" | grep -m 1 "idn-name=\"$_domain\"" | sed -r "s/.*service=\"(.*)\".*$/\1/")
return 0
diff --git a/dnsapi/dns_njalla.sh b/dnsapi/dns_njalla.sh
index e9243288..c410447d 100644
--- a/dnsapi/dns_njalla.sh
+++ b/dnsapi/dns_njalla.sh
@@ -1,7 +1,12 @@
#!/usr/bin/env sh
-
-#
-#NJALLA_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
+# shellcheck disable=SC2034
+dns_njalla_info='Njalla
+Site: Njal.la
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_njalla
+Options:
+ NJALLA_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/2913
+'
NJALLA_Api="https://njal.la/api/1/"
@@ -121,7 +126,7 @@ _get_root() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -135,7 +140,7 @@ _get_root() {
if _contains "$response" "\"$h\""; then
_domain_returned=$(echo "$response" | _egrep_o "\{\"name\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
if [ "$_domain_returned" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_nm.sh b/dnsapi/dns_nm.sh
index 4dfcc777..1f818a29 100644
--- a/dnsapi/dns_nm.sh
+++ b/dnsapi/dns_nm.sh
@@ -1,15 +1,13 @@
#!/usr/bin/env sh
-
-########################################################################
-# https://namemaster.de hook script for acme.sh
-#
-# Environment variables:
-#
-# - $NM_user (your namemaster.de API username)
-# - $NM_sha256 (your namemaster.de API password_as_sha256hash)
-#
-# Author: Thilo Gass
-# Git repo: https://github.com/ThiloGa/acme.sh
+# shellcheck disable=SC2034
+dns_nm_info='NameMaster.de
+Site: NameMaster.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_nm
+Options:
+ NM_user API Username
+ NM_sha256 API Password as SHA256 hash
+Author: Thilo Gass
+'
#-- dns_nm_add() - Add TXT record --------------------------------------
# Usage: dns_nm_add _acme-challenge.subdomain.domain.com "XyZ123..."
diff --git a/dnsapi/dns_nsd.sh b/dnsapi/dns_nsd.sh
index 0d29a485..3ddaa98c 100644
--- a/dnsapi/dns_nsd.sh
+++ b/dnsapi/dns_nsd.sh
@@ -1,7 +1,13 @@
#!/usr/bin/env sh
-
-#Nsd_ZoneFile="/etc/nsd/zones/example.com.zone"
-#Nsd_Command="sudo nsd-control reload"
+# shellcheck disable=SC2034
+dns_nsd_info='NLnetLabs NSD Server
+Site: github.com/NLnetLabs/nsd
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#nsd
+Options:
+ Nsd_ZoneFile Zone File path. E.g. "/etc/nsd/zones/example.com.zone"
+ Nsd_Command Command. E.g. "sudo nsd-control reload"
+Issues: github.com/acmesh-official/acme.sh/issues/2245
+'
# args: fulldomain txtvalue
dns_nsd_add() {
diff --git a/dnsapi/dns_nsone.sh b/dnsapi/dns_nsone.sh
index 9a998341..e1bfa531 100644
--- a/dnsapi/dns_nsone.sh
+++ b/dnsapi/dns_nsone.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env sh
-
-# bug reports to dev@1e.ca
-
-#
-#NS1_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
+# shellcheck disable=SC2034
+dns_nsone_info='ns1.com
+Domains: ns1.net
+Site: ns1.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_nsone
+Options:
+ NS1_Key API Key
+Author:
+'
NS1_Api="https://api.nsone.net/v1"
@@ -116,7 +119,7 @@ _get_root() {
return 1
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -124,7 +127,7 @@ _get_root() {
fi
if _contains "$response" "\"zone\":\"$h\""; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_nsupdate.sh b/dnsapi/dns_nsupdate.sh
index cd4b7140..d5dbbcbc 100755
--- a/dnsapi/dns_nsupdate.sh
+++ b/dnsapi/dns_nsupdate.sh
@@ -1,4 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_nsupdate_info='nsupdate RFC 2136 DynDNS client
+Site: bind9.readthedocs.io/en/v9.18.19/manpages.html#nsupdate-dynamic-dns-update-utility
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_nsupdate
+Options:
+ NSUPDATE_SERVER Server hostname. Default: "localhost".
+ NSUPDATE_SERVER_PORT Server port. Default: "53".
+ NSUPDATE_KEY File path to TSIG key.
+ NSUPDATE_ZONE Domain zone to update. Optional.
+'
######## Public functions #####################
@@ -10,6 +20,7 @@ dns_nsupdate_add() {
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
+ NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
_checkKeyFile || return 1
@@ -18,21 +29,25 @@ dns_nsupdate_add() {
_saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
_saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}"
_saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}"
+ _saveaccountconf_mutable NSUPDATE_OPT "${NSUPDATE_OPT}"
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
+ [ -n "${NSUPDATE_OPT}" ] || NSUPDATE_OPT=""
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then
- nsupdate -k "${NSUPDATE_KEY}" $nsdebug <
+'
+
# Endpoints:
# - https://portal.nexcess.net (default)
# - https://core.thermo.io
@@ -22,8 +21,6 @@
# - https://portal.nexcess.net/api-token
# - https://core.thermo.io/api-token
# - https://my.futurehosting.com/api-token
-#
-# Author: Frank Laszlo
NW_API_VERSION="0"
@@ -157,7 +154,7 @@ _get_root() {
_debug response "${response}"
while true; do
- h=$(printf "%s" "${domain}" | cut -d . -f $i-100)
+ h=$(printf "%s" "${domain}" | cut -d . -f "$i"-100)
_debug h "${h}"
if [ -z "${h}" ]; then
#not valid
@@ -168,7 +165,7 @@ _get_root() {
if [ "${hostedzone}" ]; then
_zone_id=$(printf "%s\n" "${hostedzone}" | _egrep_o "\"zone_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "${_zone_id}" ]; then
- _sub_domain=$(printf "%s" "${domain}" | cut -d . -f 1-${p})
+ _sub_domain=$(printf "%s" "${domain}" | cut -d . -f 1-"${p}")
_domain="${h}"
return 0
fi
diff --git a/dnsapi/dns_oci.sh b/dnsapi/dns_oci.sh
index 18d74410..c76a4565 100644
--- a/dnsapi/dns_oci.sh
+++ b/dnsapi/dns_oci.sh
@@ -1,6 +1,19 @@
#!/usr/bin/env sh
-#
-# Acme.sh DNS API plugin for Oracle Cloud Infrastructure
+# shellcheck disable=SC2034
+dns_oci_info='Oracle Cloud Infrastructure (OCI)
+ If OCI CLI configuration file ~/.oci/config has a DEFAULT profile then it will be used.
+Site: Cloud.Oracle.com
+Docs: github.com/acmesh-official/acme.sh/wiki/How-to-use-Oracle-Cloud-Infrastructure-DNS
+Options:
+ OCI_CLI_TENANCY OCID of tenancy that contains the target DNS zone. Optional.
+ OCI_CLI_USER OCID of user with permission to add/remove records from zones. Optional.
+ OCI_CLI_REGION Should point to the tenancy home region. Optional.
+ OCI_CLI_KEY_FILE Path to private API signing key file in PEM format. Optional.
+ OCI_CLI_KEY The private API signing key in PEM format. Optional.
+Issues: github.com/acmesh-official/acme.sh/issues/3540
+Author: Avi Miller
+'
+
# Copyright (c) 2021, Oracle and/or its affiliates
#
# The plugin will automatically use the default profile from an OCI SDK and CLI
@@ -177,7 +190,7 @@ _get_zone() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
# not valid
@@ -186,7 +199,7 @@ _get_zone() {
_domain_id=$(_signed_request "GET" "/20180115/zones/$h" "" "id")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
_debug _domain_id "$_domain_id"
@@ -265,6 +278,7 @@ _signed_request() {
_response="$(_get "https://${_sig_host}${_sig_target}")"
elif [ "$_curl_method" = "PATCH" ]; then
export _H1="$_date_header"
+ # shellcheck disable=SC2090
export _H2="$_sig_body_sha256"
export _H3="$_sig_body_type"
export _H4="$_sig_body_length"
diff --git a/dnsapi/dns_omglol.sh b/dnsapi/dns_omglol.sh
new file mode 100644
index 00000000..df080bcf
--- /dev/null
+++ b/dnsapi/dns_omglol.sh
@@ -0,0 +1,391 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_omglol_info='omg.lol
+Site: omg.lol
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_omglol
+Options:
+ OMG_ApiKey API Key. This is accessible from the bottom of the account page at https://home.omg.lol/account
+ OMG_Address Address. This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard
+Issues: github.com/acmesh-official/acme.sh/issues/5299
+Author: @Kholin
+'
+
+# See API Docs https://api.omg.lol/
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_omglol_add() {
+ fulldomain=$1
+ txtvalue=$2
+ OMG_ApiKey="${OMG_ApiKey:-$(_readaccountconf_mutable OMG_ApiKey)}"
+ OMG_Address="${OMG_Address:-$(_readaccountconf_mutable OMG_Address)}"
+
+ # As omg.lol includes a leading @ for their addresses, pre-strip this before save
+ OMG_Address="$(echo "$OMG_Address" | tr -d '@')"
+
+ _saveaccountconf_mutable OMG_ApiKey "$OMG_ApiKey"
+ _saveaccountconf_mutable OMG_Address "$OMG_Address"
+
+ _info "Using omg.lol."
+ _debug "Function" "dns_omglol_add()"
+ _debug "Full Domain Name" "$fulldomain"
+ _debug "txt Record Value" "$txtvalue"
+ _secure_debug "omg.lol API key" "$OMG_ApiKey"
+ _debug "omg.lol Address" "$OMG_Address"
+
+ omg_validate "$OMG_ApiKey" "$OMG_Address" "$fulldomain"
+ if [ ! $? ]; then
+ return 1
+ fi
+
+ dnsName=$(_getDnsRecordName "$fulldomain" "$OMG_Address")
+ authHeader="$(_createAuthHeader "$OMG_ApiKey")"
+
+ _debug2 "dns_omglol_add(): Address" "$dnsName"
+
+ omg_add "$OMG_Address" "$authHeader" "$dnsName" "$txtvalue"
+
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_omglol_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ OMG_ApiKey="${OMG_ApiKey:-$(_readaccountconf_mutable OMG_ApiKey)}"
+ OMG_Address="${OMG_Address:-$(_readaccountconf_mutable OMG_Address)}"
+
+ # As omg.lol includes a leading @ for their addresses, strip this in case provided
+ OMG_Address="$(echo "$OMG_Address" | tr -d '@')"
+
+ _info "Using omg.lol"
+ _debug "Function" "dns_omglol_rm()"
+ _debug "Full Domain Name" "$fulldomain"
+ _debug "txt Record Value" "$txtvalue"
+ _secure_debug "omg.lol API key" "$OMG_ApiKey"
+ _debug "omg.lol Address" "$OMG_Address"
+
+ omg_validate "$OMG_ApiKey" "$OMG_Address" "$fulldomain"
+ if [ ! $? ]; then
+ return 1
+ fi
+
+ dnsName=$(_getDnsRecordName "$fulldomain" "$OMG_Address")
+ authHeader="$(_createAuthHeader "$OMG_ApiKey")"
+
+ omg_delete "$OMG_Address" "$authHeader" "$dnsName" "$txtvalue"
+}
+
+#################### Private functions below ##################################
+# Check that the minimum requirements are present. Close ungracefully if not
+omg_validate() {
+ omg_apikey=$1
+ omg_address=$2
+ fulldomain=$3
+
+ _debug2 "Function" "dns_validate()"
+ _secure_debug2 "omg.lol API key" "$omg_apikey"
+ _debug2 "omg.lol Address" "$omg_address"
+ _debug2 "Full Domain Name" "$fulldomain"
+
+ if [ "" = "$omg_address" ]; then
+ _err "omg.lol base address not provided. Exiting"
+ return 1
+ fi
+
+ if [ "" = "$omg_apikey" ]; then
+ _err "omg.lol API key not provided. Exiting"
+ return 1
+ fi
+
+ _endswith "$fulldomain" "omg.lol"
+ if [ ! $? ]; then
+ _err "Domain name requested is not under omg.lol"
+ return 1
+ fi
+
+ _endswith "$fulldomain" "$omg_address.omg.lol"
+ if [ ! $? ]; then
+ _err "Domain name is not a subdomain of provided omg.lol address $omg_address"
+ return 1
+ fi
+
+ _debug "Required environment parameters are all present"
+}
+
+# Add (or modify) an entry for a new ACME query
+omg_add() {
+ address=$1
+ authHeader=$2
+ dnsName=$3
+ txtvalue=$4
+
+ _info "Creating DNS entry for $dnsName"
+ _debug2 "omg_add()"
+ _debug2 "omg.lol Address: " "$address"
+ _secure_debug2 "omg.lol authorization header: " "$authHeader"
+ _debug2 "Full Domain name:" "$dnsName.$address.omg.lol"
+ _debug2 "TXT value to set:" "$txtvalue"
+
+ export _H1="$authHeader"
+
+ endpoint="https://api.omg.lol/address/$address/dns"
+ _debug2 "Endpoint" "$endpoint"
+
+ payload='{"type": "TXT", "name":"'"$dnsName"'", "data":"'"$txtvalue"'", "ttl":30}'
+ _debug2 "Payload" "$payload"
+
+ response=$(_post "$payload" "$endpoint" "" "POST" "application/json")
+
+ omg_validate_add "$response" "$dnsName.$address" "$txtvalue"
+}
+
+omg_validate_add() {
+ response=$1
+ name=$2
+ content=$3
+
+ _debug "Validating DNS record addition"
+ _debug2 "omg_validate_add()"
+ _debug2 "Response" "$response"
+ _debug2 "DNS Name" "$name"
+ _debug2 "DNS value" "$content"
+
+ _jsonResponseCheck "$response" "success" "true"
+ if [ "1" = "$?" ]; then
+ _err "Response did not report success"
+ return 1
+ fi
+
+ _jsonResponseCheck "$response" "message" "Your DNS record was created successfully."
+ if [ "1" = "$?" ]; then
+ _err "Response message did not indicate DNS record was successfully created"
+ return 1
+ fi
+
+ _jsonResponseCheck "$response" "name" "$name"
+ if [ "1" = "$?" ]; then
+ _err "Response DNS Name did not match the response received"
+ return 1
+ fi
+
+ _jsonResponseCheck "$response" "content" "$content"
+ if [ "1" = "$?" ]; then
+ _err "Response DNS Name did not match the response received"
+ return 1
+ fi
+
+ _info "Record Created successfully"
+ return 0
+}
+
+omg_getRecords() {
+ address=$1
+ authHeader=$2
+ dnsName=$3
+ txtValue=$4
+
+ _debug2 "omg_getRecords()"
+ _debug2 "omg.lol Address: " "$address"
+ _secure_debug2 "omg.lol Auth Header: " "$authHeader"
+ _debug2 "omg.lol DNS name:" "$dnsName"
+ _debug2 "txt Value" "$txtValue"
+
+ export _H1="$authHeader"
+
+ endpoint="https://api.omg.lol/address/$address/dns"
+ _debug2 "Endpoint" "$endpoint"
+
+ payload=$(_get "$endpoint")
+
+ _debug2 "Received Payload:" "$payload"
+
+ # Reformat the JSON to be more parseable
+ recordID=$(echo "$payload" | _stripWhitespace)
+ recordID=$(echo "$recordID" | _exposeJsonArray)
+
+ # Now find the one with the right value, and caputre its ID
+ recordID=$(echo "$recordID" | grep -- "$txtValue" | grep -i -- "$dnsName.$address")
+ _getJsonElement "$recordID" "id"
+}
+
+omg_delete() {
+ address=$1
+ authHeader=$2
+ dnsName=$3
+ txtValue=$4
+
+ _info "Deleting DNS entry for $dnsName with value $txtValue"
+ _debug2 "omg_delete()"
+ _debug2 "omg.lol Address: " "$address"
+ _secure_debug2 "omg.lol Auth Header: " "$authHeader"
+ _debug2 "Full Domain name:" "$dnsName.$address.omg.lol"
+ _debug2 "txt Value" "$txtValue"
+
+ record=$(omg_getRecords "$address" "$authHeader" "$dnsName" "$txtvalue")
+ if [ "" = "$record" ]; then
+ _err "DNS record $address not found!"
+ return 1
+ fi
+
+ endpoint="https://api.omg.lol/address/$address/dns/$record"
+ _debug2 "Endpoint" "$endpoint"
+
+ export _H1="$authHeader"
+ output=$(_post "" "$endpoint" "" "DELETE")
+
+ _debug2 "Response" "$output"
+
+ omg_validate_delete "$output"
+}
+
+# Validate the response on request to delete.
+# Confirm status is success and message indicates deletion was successful.
+# Input: Response - HTTP response received from delete request
+omg_validate_delete() {
+ response=$1
+
+ _info "Validating DNS record deletion"
+ _debug2 "omg_validate_delete()"
+ _debug2 "Response" "$response"
+
+ _jsonResponseCheck "$output" "success" "true"
+ if [ "1" = "$?" ]; then
+ _err "Response did not report success"
+ return 1
+ fi
+
+ _jsonResponseCheck "$output" "message" "OK, your DNS record has been deleted."
+ if [ "1" = "$?" ]; then
+ _err "Response message did not indicate DNS record was successfully deleted"
+ return 1
+ fi
+
+ _info "Record deleted successfully"
+ return 0
+}
+
+########## Utility Functions #####################################
+# All utility functions only log at debug3
+_jsonResponseCheck() {
+ response=$1
+ field=$2
+ correct=$3
+
+ correct=$(echo "$correct" | _lower_case)
+
+ _debug3 "jsonResponseCheck()"
+ _debug3 "Response to parse" "$response"
+ _debug3 "Field to get response from" "$field"
+ _debug3 "What is the correct response" "$correct"
+
+ responseValue=$(_jsonGetLastResponse "$response" "$field")
+
+ if [ "$responseValue" != "$correct" ]; then
+ _debug3 "Expected: $correct"
+ _debug3 "Actual: $responseValue"
+ return 1
+ else
+ _debug3 "Matched: $responseValue"
+ fi
+ return 0
+}
+
+_jsonGetLastResponse() {
+ response=$1
+ field=$2
+
+ _debug3 "jsonGetLastResponse()"
+ _debug3 "Response provided" "$response"
+ _debug3 "Field to get responses for" "$field"
+
+ responseValue=$(echo "$response" | grep -- "\"$field\"" | cut -f2 -d":")
+
+ _debug3 "Response lines found:" "$responseValue"
+
+ responseValue=$(echo "$responseValue" | sed 's/^ //g' | sed 's/^"//g' | sed 's/\\"//g')
+ responseValue=$(echo "$responseValue" | sed 's/,$//g' | sed 's/"$//g')
+ responseValue=$(echo "$responseValue" | _lower_case)
+
+ _debug3 "Responses found" "$responseValue"
+ _debug3 "Response Selected" "$(echo "$responseValue" | tail -1)"
+
+ echo "$responseValue" | tail -1
+}
+
+_stripWhitespace() {
+ tr -d '\n' | tr -d '\r' | tr -d '\t' | sed -r 's/ +/ /g' | sed 's/\\"//g'
+}
+
+_exposeJsonArray() {
+ sed -r 's/.*\[//g' | tr '}' '|' | tr '{' '|' | sed 's/|, |/|/g' | tr '|' '\n'
+}
+
+_getJsonElement() {
+ content=$1
+ field=$2
+
+ _debug3 "_getJsonElement()"
+ _debug3 "Input JSON element" "$content"
+ _debug3 "JSON element to isolate" "$field"
+
+ # With a single JSON entry to parse, convert commas to newlines puts each element on
+ # its own line - which then allows us to just grep teh name, remove the key, and
+ # isolate the value
+ output=$(echo "$content" | tr ',' '\n' | grep -- "\"$field\":" | sed 's/.*: //g')
+
+ _debug3 "String before unquoting: $output"
+
+ _unquoteString "$output"
+}
+
+_createAuthHeader() {
+ apikey=$1
+
+ _debug3 "_createAuthHeader()"
+ _secure_debug3 "Provided API Key" "$apikey"
+
+ authheader="Authorization: Bearer $apikey"
+ _secure_debug3 "Authorization Header" "$authheader"
+ echo "$authheader"
+}
+
+_getDnsRecordName() {
+ fqdn=$1
+ address=$2
+
+ _debug3 "_getDnsRecordName()"
+ _debug3 "FQDN" "$fqdn"
+ _debug3 "omg.lol Address" "$address"
+
+ echo "$fqdn" | sed 's/\.omg\.lol//g' | sed 's/\.'"$address"'$//g'
+}
+
+_unquoteString() {
+ output=$1
+ quotes=0
+
+ _debug3 "_unquoteString()"
+ _debug3 "Possibly quoted string" "$output"
+
+ _startswith "$output" "\""
+ if [ $? ]; then
+ quotes=$((quotes + 1))
+ fi
+
+ _endswith "$output" "\""
+ if [ $? ]; then
+ quotes=$((quotes + 1))
+ fi
+
+ _debug3 "Original String: $output"
+ _debug3 "Quotes found: $quotes"
+
+ if [ $((quotes)) -gt 1 ]; then
+ output=$(echo "$output" | sed 's/^"//g' | sed 's/"$//g')
+ _debug3 "Quotes removed: $output"
+ fi
+
+ echo "$output"
+}
diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh
index 1565b767..d258ecc1 100644
--- a/dnsapi/dns_one.sh
+++ b/dnsapi/dns_one.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-# one.com ui wrapper for acme.sh
-
-#
-# export ONECOM_User="username"
-# export ONECOM_Password="password"
+# shellcheck disable=SC2034
+dns_one_info='one.com
+Site: one.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_one
+Options:
+ ONECOM_User Username
+ ONECOM_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2103
+'
dns_one_add() {
fulldomain=$1
@@ -90,7 +94,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
@@ -100,7 +104,7 @@ _get_root() {
response="$(_get "https://www.one.com/admin/api/domains/$h/dns/custom_records")"
if ! _contains "$response" "CRMRST_000302"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh
index 9158c268..7ec27d71 100755
--- a/dnsapi/dns_online.sh
+++ b/dnsapi/dns_online.sh
@@ -1,9 +1,16 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_online_info='online.net
+Domains: scaleway.com
+Site: online.net
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_online
+Options:
+ ONLINE_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2093
+'
# Online API
# https://console.online.net/en/api/
-#
-# Requires Online API key set in ONLINE_API_KEY
######## Public functions #####################
@@ -117,7 +124,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -126,7 +133,7 @@ _get_root() {
_online_rest GET "domain/$h/version/active"
if ! _contains "$response" "Domain not found" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
_real_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
return 0
diff --git a/dnsapi/dns_openprovider.sh b/dnsapi/dns_openprovider.sh
index 0a9e5ade..2dec9934 100755
--- a/dnsapi/dns_openprovider.sh
+++ b/dnsapi/dns_openprovider.sh
@@ -1,15 +1,15 @@
#!/usr/bin/env sh
-
-# This is the OpenProvider API wrapper for acme.sh
-#
-# Author: Sylvia van Os
-# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2104
-#
-# export OPENPROVIDER_USER="username"
-# export OPENPROVIDER_PASSWORDHASH="hashed_password"
-#
-# Usage:
-# acme.sh --issue --dns dns_openprovider -d example.com
+# shellcheck disable=SC2034
+dns_openprovider_info='OpenProvider.eu
+Site: OpenProvider.eu
+Domains: OpenProvider.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_openprovider
+Options:
+ OPENPROVIDER_USER Username
+ OPENPROVIDER_PASSWORDHASH Password hash
+Issues: github.com/acmesh-official/acme.sh/issues/2104
+Author: Sylvia van Os
+'
OPENPROVIDER_API="https://api.openprovider.eu/"
#OPENPROVIDER_API="https://api.cte.openprovider.eu/" # Test API
@@ -69,7 +69,7 @@ dns_openprovider_add() {
new_item="$(echo "$item" | sed -n 's/.*
- .*\(\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/
- \2<\/name>\3\4\5\6<\/item>/p')"
fi
- if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
+ if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item"
continue
fi
@@ -153,7 +153,7 @@ dns_openprovider_rm() {
new_item="$(echo "$item" | sed -n 's/.*
- .*\(\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/
- \2<\/name>\3\4\5\6<\/item>/p')"
fi
- if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
+ if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item"
continue
fi
@@ -187,7 +187,7 @@ _get_root() {
results_retrieved=0
while true; do
- h=$(echo "$domain" | cut -d . -f $i-100)
+ h=$(echo "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
diff --git a/dnsapi/dns_openprovider_rest.sh b/dnsapi/dns_openprovider_rest.sh
new file mode 100644
index 00000000..210dc6fc
--- /dev/null
+++ b/dnsapi/dns_openprovider_rest.sh
@@ -0,0 +1,186 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_openprovider_rest_info='OpenProvider (REST)
+Domains: OpenProvider.com
+Site: OpenProvider.eu
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_openprovider_rest
+Options:
+ OPENPROVIDER_REST_USERNAME Openprovider Account Username
+ OPENPROVIDER_REST_PASSWORD Openprovider Account Password
+Issues: github.com/acmesh-official/acme.sh/issues/6122
+Author: Lambiek12
+'
+
+OPENPROVIDER_API_URL="https://api.openprovider.eu/v1beta"
+
+######## Public functions #####################
+
+# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+dns_openprovider_rest_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _openprovider_prepare_credentials || return 1
+
+ _debug "Try fetch OpenProvider DNS zone details"
+ if ! _get_dns_zone "$fulldomain"; then
+ _err "DNS zone not found within configured OpenProvider account."
+ return 1
+ fi
+
+ if [ -n "$_domain_id" ]; then
+ addzonerecordrequestparameters="dns/zones/$_domain_name"
+ addzonerecordrequestbody="{\"id\":$_domain_id,\"name\":\"$_domain_name\",\"records\":{\"add\":[{\"name\":\"$_sub_domain\",\"ttl\":900,\"type\":\"TXT\",\"value\":\"$txtvalue\"}]}}"
+
+ if _openprovider_rest PUT "$addzonerecordrequestparameters" "$addzonerecordrequestbody"; then
+ if _contains "$response" "\"success\":true"; then
+ return 0
+ elif _contains "$response" "\"Duplicate record\""; then
+ _debug "Record already existed"
+ return 0
+ else
+ _err "Adding TXT record failed due to errors."
+ return 1
+ fi
+ fi
+ fi
+
+ _err "Adding TXT record failed due to errors."
+ return 1
+}
+
+# Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to remove the txt record after validation
+dns_openprovider_rest_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _openprovider_prepare_credentials || return 1
+
+ _debug "Try fetch OpenProvider DNS zone details"
+ if ! _get_dns_zone "$fulldomain"; then
+ _err "DNS zone not found within configured OpenProvider account."
+ return 1
+ fi
+
+ if [ -n "$_domain_id" ]; then
+ removezonerecordrequestparameters="dns/zones/$_domain_name"
+ removezonerecordrequestbody="{\"id\":$_domain_id,\"name\":\"$_domain_name\",\"records\":{\"remove\":[{\"name\":\"$_sub_domain\",\"ttl\":900,\"type\":\"TXT\",\"value\":\"\\\"$txtvalue\\\"\"}]}}"
+
+ if _openprovider_rest PUT "$removezonerecordrequestparameters" "$removezonerecordrequestbody"; then
+ if _contains "$response" "\"success\":true"; then
+ return 0
+ else
+ _err "Removing TXT record failed due to errors."
+ return 1
+ fi
+ fi
+ fi
+
+ _err "Removing TXT record failed due to errors."
+ return 1
+}
+
+#################### OpenProvider API common functions ####################
+_openprovider_prepare_credentials() {
+ OPENPROVIDER_REST_USERNAME="${OPENPROVIDER_REST_USERNAME:-$(_readaccountconf_mutable OPENPROVIDER_REST_USERNAME)}"
+ OPENPROVIDER_REST_PASSWORD="${OPENPROVIDER_REST_PASSWORD:-$(_readaccountconf_mutable OPENPROVIDER_REST_PASSWORD)}"
+
+ if [ -z "$OPENPROVIDER_REST_USERNAME" ] || [ -z "$OPENPROVIDER_REST_PASSWORD" ]; then
+ OPENPROVIDER_REST_USERNAME=""
+ OPENPROVIDER_REST_PASSWORD=""
+ _err "You didn't specify the Openprovider username or password yet."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable OPENPROVIDER_REST_USERNAME "$OPENPROVIDER_REST_USERNAME"
+ _saveaccountconf_mutable OPENPROVIDER_REST_PASSWORD "$OPENPROVIDER_REST_PASSWORD"
+}
+
+_openprovider_rest() {
+ httpmethod=$1
+ queryparameters=$2
+ requestbody=$3
+
+ _openprovider_rest_login
+ if [ -z "$openproviderauthtoken" ]; then
+ _err "Unable to fetch authentication token from Openprovider API."
+ return 1
+ fi
+
+ export _H1="Content-Type: application/json"
+ export _H2="Accept: application/json"
+ export _H3="Authorization: Bearer $openproviderauthtoken"
+
+ if [ "$httpmethod" != "GET" ]; then
+ response="$(_post "$requestbody" "$OPENPROVIDER_API_URL/$queryparameters" "" "$httpmethod")"
+ else
+ response="$(_get "$OPENPROVIDER_API_URL/$queryparameters")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "No valid parameters supplied for Openprovider API: Error $queryparameters"
+ return 1
+ fi
+
+ _debug2 response "$response"
+
+ return 0
+}
+
+_openprovider_rest_login() {
+ export _H1="Content-Type: application/json"
+ export _H2="Accept: application/json"
+
+ loginrequesturl="$OPENPROVIDER_API_URL/auth/login"
+ loginrequestbody="{\"ip\":\"0.0.0.0\",\"password\":\"$OPENPROVIDER_REST_PASSWORD\",\"username\":\"$OPENPROVIDER_REST_USERNAME\"}"
+ loginresponse="$(_post "$loginrequestbody" "$loginrequesturl" "" "POST")"
+
+ openproviderauthtoken="$(printf "%s\n" "$loginresponse" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')"
+
+ export openproviderauthtoken
+}
+
+#################### Private functions ##################################
+
+# Usage: _get_dns_zone _acme-challenge.www.domain.com
+# Returns:
+# _domain_id=123456789
+# _domain_name=domain.com
+# _sub_domain=_acme-challenge.www
+_get_dns_zone() {
+ domain=$1
+ i=1
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ if [ -z "$h" ]; then
+ # Empty value not allowed
+ return 1
+ fi
+
+ if ! _openprovider_rest GET "dns/zones/$h" ""; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id="$(printf "%s\n" "$response" | _egrep_o '"id" *: *[^,]*' | _head_n 1 | sed 's#^"id" *: *##')"
+ _debug _domain_id "$_domain_id"
+
+ _domain_name="$h"
+ _debug _domain_name "$_domain_name"
+
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _debug _sub_domain "$_sub_domain"
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
diff --git a/dnsapi/dns_openstack.sh b/dnsapi/dns_openstack.sh
index 38619e6f..fa38bc0b 100755
--- a/dnsapi/dns_openstack.sh
+++ b/dnsapi/dns_openstack.sh
@@ -1,14 +1,21 @@
#!/usr/bin/env sh
-
-# OpenStack Designate API plugin
-#
-# This requires you to have OpenStackClient and python-desginateclient
-# installed.
-#
-# You will require Keystone V3 credentials loaded into your environment, which
-# could be either password or v3applicationcredential type.
-#
-# Author: Andy Botting
+# shellcheck disable=SC2034
+dns_openstack_info='OpenStack Designate API
+ Depends on OpenStackClient and python-desginateclient.
+ You will require Keystone V3 credentials loaded into your environment,
+ which could be either password or v3 application credential type.
+Site: docs.openstack.org/api-ref/dns/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_openstack
+Options:
+ OS_AUTH_URL Auth URL. E.g. "https://keystone.example.com:5000/"
+ OS_USERNAME Username
+ OS_PASSWORD Password
+ OS_PROJECT_NAME Project name
+ OS_PROJECT_DOMAIN_NAME Project domain name. E.g. "Default"
+ OS_USER_DOMAIN_NAME User domain name. E.g. "Default"
+Issues: github.com/acmesh-official/acme.sh/issues/3054
+Author: Andy Botting
+'
######## Public functions #####################
@@ -57,16 +64,16 @@ _dns_openstack_create_recordset() {
if [ -z "$_recordset_id" ]; then
_info "Creating a new recordset"
- if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record "$txtvalue" "$_zone_id" "$fulldomain."); then
+ if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record="$txtvalue" "$_zone_id" "$fulldomain."); then
_err "No recordset ID found after create"
return 1
fi
else
_info "Updating existing recordset"
- # Build new list of --record args for update
- _record_args="--record $txtvalue"
+ # Build new list of --record= args for update
+ _record_args="--record=$txtvalue"
for _rec in $_records; do
- _record_args="$_record_args --record $_rec"
+ _record_args="$_record_args --record=$_rec"
done
# shellcheck disable=SC2086
if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
@@ -107,13 +114,13 @@ _dns_openstack_delete_recordset() {
fi
else
_info "Found existing records, updating recordset"
- # Build new list of --record args for update
+ # Build new list of --record= args for update
_record_args=""
for _rec in $_records; do
if [ "$_rec" = "$txtvalue" ]; then
continue
fi
- _record_args="$_record_args --record $_rec"
+ _record_args="$_record_args --record=$_rec"
done
# shellcheck disable=SC2086
if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then
diff --git a/dnsapi/dns_opnsense.sh b/dnsapi/dns_opnsense.sh
index c2806a1b..d1e9c0ac 100755
--- a/dnsapi/dns_opnsense.sh
+++ b/dnsapi/dns_opnsense.sh
@@ -1,16 +1,16 @@
#!/usr/bin/env sh
-
-#OPNsense Bind API
-#https://docs.opnsense.org/development/api.html
-#
-#OPNs_Host="opnsense.example.com"
-#OPNs_Port="443"
-# optional, defaults to 443 if unset
-#OPNs_Key="qocfU9RSbt8vTIBcnW8bPqCrpfAHMDvj5OzadE7Str+rbjyCyk7u6yMrSCHtBXabgDDXx/dY0POUp7ZA"
-#OPNs_Token="pZEQ+3ce8dDlfBBdg3N8EpqpF5I1MhFqdxX06le6Gl8YzyQvYCfCzNaFX9O9+IOSyAs7X71fwdRiZ+Lv"
-#OPNs_Api_Insecure=0
-# optional, defaults to 0 if unset
-# Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
+# shellcheck disable=SC2034
+dns_opnsense_info='OPNsense Server
+Site: docs.opnsense.org/development/api.html
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_opnsense
+Options:
+ OPNs_Host Server Hostname. E.g. "opnsense.example.com"
+ OPNs_Port Port. Default: "443".
+ OPNs_Key API Key
+ OPNs_Token API Token
+ OPNs_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
+Issues: github.com/acmesh-official/acme.sh/issues/2480
+'
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
@@ -137,29 +137,29 @@ _get_root() {
domain=$1
i=2
p=1
- if _opns_rest "GET" "/domain/searchMasterDomain"; then
+ if _opns_rest "GET" "/domain/searchPrimaryDomain"; then
_domain_response="$response"
else
return 1
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_debug h "$h"
- id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"master\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
- _host=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _host=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="${h}"
_domainid="${id}"
return 0
fi
p=$i
- i=$(_math $i + 1)
+ i=$(_math "$i" + 1)
done
_debug "$domain not found"
diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh
index 5e35011b..24ad0904 100755
--- a/dnsapi/dns_ovh.sh
+++ b/dnsapi/dns_ovh.sh
@@ -1,19 +1,24 @@
#!/usr/bin/env sh
-
-#Application Key
-#OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#Application Secret
-#OVH_AS="sdfsafsdfsdfdsfsdfsa"
-#
-#Consumer Key
-#OVH_CK="sdfsdfsdfsdfsdfdsf"
+# shellcheck disable=SC2034
+dns_ovh_info='OVH.com
+Domains: kimsufi.com soyoustart.com
+Site: OVH.com
+Docs: github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api
+Options:
+ OVH_END_POINT Endpoint. "ovh-eu", "ovh-us", "ovh-ca", "kimsufi-eu", "kimsufi-ca", "soyoustart-eu", "soyoustart-ca" or raw URL. Default: "ovh-eu".
+ OVH_AK Application Key
+ OVH_AS Application Secret
+ OVH_CK Consumer Key
+'
#OVH_END_POINT=ovh-eu
#'ovh-eu'
OVH_EU='https://eu.api.ovh.com/1.0'
+#'ovh-us'
+OVH_US='https://api.us.ovhcloud.com/1.0'
+
#'ovh-ca':
OVH_CA='https://ca.api.ovh.com/1.0'
@@ -29,9 +34,6 @@ SYS_EU='https://eu.api.soyoustart.com/1.0'
#'soyoustart-ca'
SYS_CA='https://ca.api.soyoustart.com/1.0'
-#'runabove-ca'
-RAV_CA='https://api.runabove.com/1.0'
-
wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api"
ovh_success="https://github.com/acmesh-official/acme.sh/wiki/OVH-Success"
@@ -45,6 +47,10 @@ _ovh_get_api() {
printf "%s" $OVH_EU
return
;;
+ ovh-us | ovhus)
+ printf "%s" $OVH_US
+ return
+ ;;
ovh-ca | ovhca)
printf "%s" $OVH_CA
return
@@ -65,14 +71,15 @@ _ovh_get_api() {
printf "%s" $SYS_CA
return
;;
- runabove-ca | runaboveca)
- printf "%s" $RAV_CA
+ # raw API url starts with https://
+ https*)
+ printf "%s" "$1"
return
;;
*)
- _err "Unknown parameter : $1"
+ _err "Unknown endpoint : $1"
return 1
;;
esac
@@ -106,7 +113,7 @@ _initAuth() {
_saveaccountconf_mutable OVH_END_POINT "$OVH_END_POINT"
fi
- OVH_API="$(_ovh_get_api $OVH_END_POINT)"
+ OVH_API="$(_ovh_get_api "$OVH_END_POINT")"
_debug OVH_API "$OVH_API"
OVH_CK="${OVH_CK:-$(_readaccountconf_mutable OVH_CK)}"
@@ -253,7 +260,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -266,7 +273,7 @@ _get_root() {
if ! _contains "$response" "This service does not exist" >/dev/null &&
! _contains "$response" "This call has not been granted" >/dev/null &&
! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_pdns.sh b/dnsapi/dns_pdns.sh
index 6aa2e953..ec19ad25 100755
--- a/dnsapi/dns_pdns.sh
+++ b/dnsapi/dns_pdns.sh
@@ -1,12 +1,14 @@
#!/usr/bin/env sh
-
-#PowerDNS Embedded API
-#https://doc.powerdns.com/md/httpapi/api_spec/
-#
-#PDNS_Url="http://ns.example.com:8081"
-#PDNS_ServerId="localhost"
-#PDNS_Token="0123456789ABCDEF"
-#PDNS_Ttl=60
+# shellcheck disable=SC2034
+dns_pdns_info='PowerDNS Server API
+Site: PowerDNS.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_pdns
+Options:
+ PDNS_Url API URL. E.g. "http://ns.example.com:8081"
+ PDNS_ServerId Server ID. E.g. "localhost"
+ PDNS_Token API Token
+ PDNS_Ttl Domain TTL. Default: "60".
+'
DEFAULT_PDNS_TTL=60
@@ -18,6 +20,11 @@ dns_pdns_add() {
fulldomain=$1
txtvalue=$2
+ PDNS_Url="${PDNS_Url:-$(_readaccountconf_mutable PDNS_Url)}"
+ PDNS_ServerId="${PDNS_ServerId:-$(_readaccountconf_mutable PDNS_ServerId)}"
+ PDNS_Token="${PDNS_Token:-$(_readaccountconf_mutable PDNS_Token)}"
+ PDNS_Ttl="${PDNS_Ttl:-$(_readaccountconf_mutable PDNS_Ttl)}"
+
if [ -z "$PDNS_Url" ]; then
PDNS_Url=""
_err "You don't specify PowerDNS address."
@@ -44,12 +51,12 @@ dns_pdns_add() {
fi
#save the api addr and key to the account conf file.
- _saveaccountconf PDNS_Url "$PDNS_Url"
- _saveaccountconf PDNS_ServerId "$PDNS_ServerId"
- _saveaccountconf PDNS_Token "$PDNS_Token"
+ _saveaccountconf_mutable PDNS_Url "$PDNS_Url"
+ _saveaccountconf_mutable PDNS_ServerId "$PDNS_ServerId"
+ _saveaccountconf_mutable PDNS_Token "$PDNS_Token"
if [ "$PDNS_Ttl" != "$DEFAULT_PDNS_TTL" ]; then
- _saveaccountconf PDNS_Ttl "$PDNS_Ttl"
+ _saveaccountconf_mutable PDNS_Ttl "$PDNS_Ttl"
fi
_debug "Detect root zone"
@@ -71,6 +78,11 @@ dns_pdns_rm() {
fulldomain=$1
txtvalue=$2
+ PDNS_Url="${PDNS_Url:-$(_readaccountconf_mutable PDNS_Url)}"
+ PDNS_ServerId="${PDNS_ServerId:-$(_readaccountconf_mutable PDNS_ServerId)}"
+ PDNS_Token="${PDNS_Token:-$(_readaccountconf_mutable PDNS_Token)}"
+ PDNS_Ttl="${PDNS_Ttl:-$(_readaccountconf_mutable PDNS_Ttl)}"
+
if [ -z "$PDNS_Ttl" ]; then
PDNS_Ttl="$DEFAULT_PDNS_TTL"
fi
@@ -179,7 +191,7 @@ _get_root() {
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if _contains "$_zones_response" "\"name\":\"$h.\""; then
_domain="$h."
@@ -192,7 +204,7 @@ _get_root() {
if [ -z "$h" ]; then
return 1
fi
- i=$(_math $i + 1)
+ i=$(_math "$i" + 1)
done
_debug "$domain not found"
diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh
index f5986827..465bcc60 100644
--- a/dnsapi/dns_pleskxml.sh
+++ b/dnsapi/dns_pleskxml.sh
@@ -1,10 +1,17 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_pleskxml_info='Plesk Server API
+Site: Plesk.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_pleskxml
+Options:
+ pleskxml_uri Plesk server API URL. E.g. "https://your-plesk-server.net:8443/enterprise/control/agent.php"
+ pleskxml_user Username
+ pleskxml_pass Password
+Issues: github.com/acmesh-official/acme.sh/issues/2577
+Author: @Stilez, @romanlum
+'
-## Name: dns_pleskxml.sh
-## Created by Stilez.
-## Also uses some code from PR#1832 by @romanlum (https://github.com/acmesh-official/acme.sh/pull/1832/files)
-
-## This DNS-01 method uses the Plesk XML API described at:
+## Plesk XML API described at:
## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709
## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784
@@ -16,21 +23,6 @@
## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records.
## The user credentials (username+password) and URL/URI for the Plesk XML API must be set by the user
-## before this module is called (case sensitive):
-##
-## ```
-## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php"
-## (or probably something similar)
-## export pleskxml_user="my plesk username"
-## export pleskxml_pass="my plesk password"
-## ```
-
-## Ok, let's issue a cert now:
-## ```
-## acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com
-## ```
-##
-## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed.
#################### INTERNAL VARIABLES + NEWLINE + API TEMPLATES ##################################
@@ -41,11 +33,15 @@ pleskxml_init_checks_done=0
NEWLINE='\
'
-pleskxml_tplt_get_domains=""
+pleskxml_tplt_get_domains=""
# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh
# Also used to test credentials and URI.
# No params.
+pleskxml_tplt_get_additional_domains=""
+# Get a list of additional domains that PLESK can manage, so we can check root domain + host for acme.sh
+# No params.
+
pleskxml_tplt_get_dns_records="%s"
# Get all DNS records for a Plesk domain ID.
# PARAM = Plesk domain id to query
@@ -145,22 +141,25 @@ dns_pleskxml_rm() {
)"
if [ -z "$reclist" ]; then
- _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting."
+ _err "No TXT records found for root domain $fulldomain (Plesk domain ID ${root_domain_id}). Exiting."
return 1
fi
- _debug "Got list of DNS TXT records for root domain '$root_domain_name':"
+ _debug "Got list of DNS TXT records for root Plesk domain ID ${root_domain_id} of root domain $fulldomain:"
_debug "$reclist"
+ # Extracting the id of the TXT record for the full domain (NOT case-sensitive) and corresponding value
recid="$(
_value "$reclist" |
- grep "${fulldomain}." |
+ grep -i "${fulldomain}." |
grep "${txtvalue}" |
sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/'
)"
+ _debug "Got id from line: $recid"
+
if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then
- _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
+ _err "DNS records for root domain '${fulldomain}.' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
_err "Cannot delete TXT record. Exiting."
return 1
fi
@@ -251,9 +250,12 @@ _call_api() {
# Detect any that isn't "ok". None of the used calls should fail if the API is working correctly.
# Also detect if there simply aren't any status lines (null result?) and report that, as well.
+ # Remove structure from result string, since it might contain values that are related to the status of the domain and not to the API request
- statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *[^<]* *$')"
- statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *ok *$')"
+ statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | sed '//,/<\/data>/d' | grep -c '^ *[^<]* *$')"
+ statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | sed '//,/<\/data>/d' | grep -c '^ *ok *$')"
+ _debug "statuslines_count_total=$statuslines_count_total."
+ _debug "statuslines_count_okay=$statuslines_count_okay."
if [ -z "$statuslines_count_total" ]; then
@@ -369,16 +371,44 @@ _pleskxml_get_root_domain() {
return 1
fi
- # Generate a crude list of domains known to this Plesk account.
+ # Generate a crude list of domains known to this Plesk account based on subscriptions.
+ # We convert tags to so it'll flag on a hit with either or fields,
+ # for non-Western character sets.
+ # Output will be one line per known domain, containing 2 tages and a single tag
+ # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
+
+ output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+ debug_output="$(printf "%s" "$output" | sed -n 's:.*\(.*\).*:\1:p')"
+
+ _debug 'Domains managed by Plesk server are:'
+ _debug "$debug_output"
+
+ _debug "Querying Plesk server for list of additional managed domains..."
+
+ _call_api "$pleskxml_tplt_get_additional_domains"
+ if [ "$pleskxml_retcode" -ne 0 ]; then
+ return 1
+ fi
+
+ # Generate a crude list of additional domains known to this Plesk account based on sites.
# We convert tags to so it'll flag on a hit with either or fields,
# for non-Western character sets.
# Output will be one line per known domain, containing 2 tages and a single tag
# We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
- output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+ output_additional="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+ debug_additional="$(printf "%s" "$output_additional" | sed -n 's:.*\(.*\).*:\1:p')"
+
+ _debug 'Additional domains managed by Plesk server are:'
+ _debug "$debug_additional"
+
+ # Concate the two outputs together.
+
+ output="$(printf "%s" "$output $NEWLINE $output_additional")"
+ debug_output="$(printf "%s" "$output" | sed -n 's:.*\(.*\).*:\1:p')"
- _debug 'Domains managed by Plesk server are (ignore the hacked output):'
- _debug "$output"
+ _debug 'Domains (including additional) managed by Plesk server are:'
+ _debug "$debug_output"
# loop and test if domain, or any parent domain, is managed by Plesk
# Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain
diff --git a/dnsapi/dns_pointhq.sh b/dnsapi/dns_pointhq.sh
index 62313109..0abc087b 100644
--- a/dnsapi/dns_pointhq.sh
+++ b/dnsapi/dns_pointhq.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-#
-#PointHQ_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#PointHQ_Email="xxxx@sss.com"
+# shellcheck disable=SC2034
+dns_pointhq_info='pointhq.com PointDNS
+Site: pointhq.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_pointhq
+Options:
+ PointHQ_Key API Key
+ PointHQ_Email Email
+Issues: github.com/acmesh-official/acme.sh/issues/2060
+'
PointHQ_Api="https://api.pointhq.com"
@@ -114,7 +118,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -126,7 +130,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_porkbun.sh b/dnsapi/dns_porkbun.sh
index ad4455b6..1681ca9a 100644
--- a/dnsapi/dns_porkbun.sh
+++ b/dnsapi/dns_porkbun.sh
@@ -1,10 +1,15 @@
#!/usr/bin/env sh
-
-#
-#PORKBUN_API_KEY="pk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
-#PORKBUN_SECRET_API_KEY="sk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
-
-PORKBUN_Api="https://porkbun.com/api/json/v3"
+# shellcheck disable=SC2034
+dns_porkbun_info='Porkbun.com
+Site: Porkbun.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_porkbun
+Options:
+ PORKBUN_API_KEY API Key
+ PORKBUN_SECRET_API_KEY API Secret
+Issues: github.com/acmesh-official/acme.sh/issues/3450
+'
+
+PORKBUN_Api="https://api.porkbun.com/api/json/v3"
######## Public functions #####################
@@ -88,7 +93,7 @@ dns_porkbun_rm() {
_err "Delete record error."
return 1
fi
- echo "$response" | tr -d " " | grep '\"status\":"SUCCESS"' >/dev/null
+ echo "$response" | tr -d " " | grep '"status":"SUCCESS"' >/dev/null
fi
}
@@ -102,7 +107,7 @@ _get_root() {
domain=$1
i=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
@@ -134,7 +139,7 @@ _porkbun_rest() {
api_key_trimmed=$(echo "$PORKBUN_API_KEY" | tr -d '"')
secret_api_key_trimmed=$(echo "$PORKBUN_SECRET_API_KEY" | tr -d '"')
- test -z "$data" && data="{" || data="$(echo $data | cut -d'}' -f1),"
+ test -z "$data" && data="{" || data="$(echo "$data" | cut -d'}' -f1),"
data="$data\"apikey\":\"$api_key_trimmed\",\"secretapikey\":\"$secret_api_key_trimmed\"}"
export _H1="Content-Type: application/json"
diff --git a/dnsapi/dns_rackcorp.sh b/dnsapi/dns_rackcorp.sh
index 6aabfddc..b8fc73ab 100644
--- a/dnsapi/dns_rackcorp.sh
+++ b/dnsapi/dns_rackcorp.sh
@@ -1,16 +1,14 @@
#!/usr/bin/env sh
-
-# Provider: RackCorp (www.rackcorp.com)
-# Author: Stephen Dendtler (sdendtler@rackcorp.com)
-# Report Bugs here: https://github.com/senjoo/acme.sh
-# Alternate email contact: support@rackcorp.com
-#
-# You'll need an API key (Portal: ADMINISTRATION -> API)
-# Set the environment variables as below:
-#
-# export RACKCORP_APIUUID="UUIDHERE"
-# export RACKCORP_APISECRET="SECRETHERE"
-#
+# shellcheck disable=SC2034
+dns_rackcorp_info='RackCorp.com
+Site: RackCorp.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_rackcorp
+Options:
+ RACKCORP_APIUUID API UUID. See Portal: ADMINISTRATION -> API
+ RACKCORP_APISECRET API Secret
+Issues: github.com/acmesh-official/acme.sh/issues/3351
+Author: Stephen Dendtler
+'
RACKCORP_API_ENDPOINT="https://api.rackcorp.net/api/rest/v2.4/json.php"
@@ -85,7 +83,7 @@ _get_root() {
return 1
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug searchhost "$h"
if [ -z "$h" ]; then
_err "Could not find domain for record $domain in RackCorp using the provided credentials"
@@ -97,7 +95,7 @@ _get_root() {
if _contains "$response" "\"matches\":1"; then
if _contains "$response" "\"name\":\"$h\""; then
- _lookup=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _lookup=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_rackspace.sh b/dnsapi/dns_rackspace.sh
index b50d9168..05ec14a6 100644
--- a/dnsapi/dns_rackspace.sh
+++ b/dnsapi/dns_rackspace.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-#
-#
-#RACKSPACE_Username=""
-#
-#RACKSPACE_Apikey=""
+# shellcheck disable=SC2034
+dns_rackspace_info='RackSpace.com
+Site: RackSpace.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_rackspace
+Options:
+ RACKSPACE_Apikey API Key
+ RACKSPACE_Username Username
+Issues: github.com/acmesh-official/acme.sh/issues/2091
+'
RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0"
@@ -68,7 +72,7 @@ _get_root_zone() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -84,7 +88,7 @@ _get_root_zone() {
_domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\"\([^,]*\)\",\"accountId\":\"[0-9]*\",\"name\":\"$h\",.*/\1/p")
_debug2 domain_id "$_domain_id"
if [ -n "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_rage4.sh b/dnsapi/dns_rage4.sh
index 4af4541d..ad312759 100755
--- a/dnsapi/dns_rage4.sh
+++ b/dnsapi/dns_rage4.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-#
-#RAGE4_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#RAGE4_USERNAME="xxxx@sss.com"
+# shellcheck disable=SC2034
+dns_rage4_info='rage4.com
+Site: rage4.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_rage4
+Options:
+ RAGE4_TOKEN API Key
+ RAGE4_USERNAME Username
+Issues: github.com/acmesh-official/acme.sh/issues/4306
+'
RAGE4_Api="https://rage4.com/rapi/"
diff --git a/dnsapi/dns_rcode0.sh b/dnsapi/dns_rcode0.sh
index d3f7f219..4ffdf572 100755
--- a/dnsapi/dns_rcode0.sh
+++ b/dnsapi/dns_rcode0.sh
@@ -1,14 +1,20 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_rcode0_info='Rcode0 rcodezero.at
+Site: rcodezero.at
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_rcode0
+Options:
+ RCODE0_URL API URL. E.g. "https://my.rcodezero.at"
+ RCODE0_API_TOKEN API Token
+ RCODE0_TTL TTL. Default: "60".
+Issues: github.com/acmesh-official/acme.sh/issues/2490
+'
#Rcode0 API Integration
#https://my.rcodezero.at/api-doc
#
# log into https://my.rcodezero.at/enableapi and get your ACME API Token (the ACME API token has limited
# access to the REST calls needed for acme.sh only)
-#
-#RCODE0_URL="https://my.rcodezero.at"
-#RCODE0_API_TOKEN="0123456789ABCDEF"
-#RCODE0_TTL=60
DEFAULT_RCODE0_URL="https://my.rcodezero.at"
DEFAULT_RCODE0_TTL=60
@@ -165,7 +171,7 @@ _get_root() {
i=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug "try to find: $h"
if _rcode0_rest "GET" "/api/v1/acme/zones/$h"; then
@@ -183,7 +189,7 @@ _get_root() {
if [ -z "$h" ]; then
return 1
fi
- i=$(_math $i + 1)
+ i=$(_math "$i" + 1)
done
_debug "no matching domain for $domain found"
diff --git a/dnsapi/dns_regru.sh b/dnsapi/dns_regru.sh
index 8ff380f0..be5ae117 100644
--- a/dnsapi/dns_regru.sh
+++ b/dnsapi/dns_regru.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env sh
-
-#
-# REGRU_API_Username="test"
-#
-# REGRU_API_Password="test"
-#
+# shellcheck disable=SC2034
+dns_regru_info='reg.ru
+Site: reg.ru
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_regru
+Options:
+ REGRU_API_Username Username
+ REGRU_API_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/2336
+'
REGRU_API_URL="https://api.reg.ru/api/regru2"
diff --git a/dnsapi/dns_scaleway.sh b/dnsapi/dns_scaleway.sh
index a0a0f318..4cbf68d2 100755
--- a/dnsapi/dns_scaleway.sh
+++ b/dnsapi/dns_scaleway.sh
@@ -1,9 +1,15 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_scaleway_info='ScaleWay.com
+Site: ScaleWay.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_scaleway
+Options:
+ SCALEWAY_API_TOKEN API Token
+Issues: github.com/acmesh-official/acme.sh/issues/3295
+'
# Scaleway API
# https://developers.scaleway.com/en/products/domain/dns/api/
-#
-# Requires Scaleway API token set in SCALEWAY_API_TOKEN
######## Public functions #####################
@@ -35,9 +41,7 @@ dns_scaleway_add() {
_err error "$response"
return 1
fi
- _info "Record added."
- return 0
}
dns_scaleway_rm() {
@@ -65,9 +69,7 @@ dns_scaleway_rm() {
_err error "$response"
return 1
fi
- _info "Record deleted."
- return 0
}
#################### Private functions below ##################################
@@ -98,7 +100,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -107,7 +109,7 @@ _get_root() {
_scaleway_rest GET "dns-zones/$h/records"
if ! _contains "$response" "subdomain not found" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh
index 399c50e0..21930110 100644
--- a/dnsapi/dns_schlundtech.sh
+++ b/dnsapi/dns_schlundtech.sh
@@ -1,16 +1,14 @@
#!/usr/bin/env sh
-# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
-
-# Schlundtech DNS API
-# Author: mod242
-# Created: 2019-40-29
-# Completly based on the autoDNS xml api wrapper by auerswald@gmail.com
-#
-# export SCHLUNDTECH_USER="username"
-# export SCHLUNDTECH_PASSWORD="password"
-#
-# Usage:
-# acme.sh --issue --dns dns_schlundtech -d example.com
+# shellcheck disable=SC2034
+dns_schlundtech_info='SchlundTech.de
+Site: SchlundTech.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_schlundtech
+Options:
+ SCHLUNDTECH_USER Username
+ SCHLUNDTECH_PASSWORD Password
+Issues: github.com/acmesh-official/acme.sh/issues/2246
+Author: @mod242
+'
SCHLUNDTECH_API="https://gateway.schlundtech.de"
@@ -108,7 +106,7 @@ _get_autodns_zone() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
@@ -126,7 +124,7 @@ _get_autodns_zone() {
if _contains "$autodns_response" "1" >/dev/null; then
_zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
return 0
fi
diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh
index 1b09882d..565f541b 100644
--- a/dnsapi/dns_selectel.sh
+++ b/dnsapi/dns_selectel.sh
@@ -1,10 +1,25 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_selectel_info='Selectel.com
+Domains: Selectel.ru
+Site: Selectel.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel
+Options: For old API version v1 (deprecated)
+ SL_Ver API version. Use "v1".
+ SL_Key API Key
+OptionsAlt: For the current API version v2
+ SL_Ver API version. Use "v2".
+ SL_Login_ID Account ID
+ SL_Project_Name Project name
+ SL_Login_Name Service user name
+ SL_Pswd Service user password
+ SL_Expire Token lifetime. In minutes (0-1440). Default "1400"
+Issues: github.com/acmesh-official/acme.sh/issues/5126
+'
-#
-#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-
-SL_Api="https://api.selectel.ru/domains/v1"
+SL_Api="https://api.selectel.ru/domains"
+auth_uri="https://cloud.api.selcloud.ru/identity/v3/auth/tokens"
+_sl_sep='#'
######## Public functions #####################
@@ -13,17 +28,14 @@ dns_selectel_add() {
fulldomain=$1
txtvalue=$2
- SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
-
- if [ -z "$SL_Key" ]; then
- SL_Key=""
- _err "You don't specify selectel.ru api key yet."
- _err "Please create you key and try again."
+ if ! _sl_init_vars; then
return 1
fi
-
- #save the api key to the account conf file.
- _saveaccountconf_mutable SL_Key "$SL_Key"
+ _debug2 SL_Ver "$SL_Ver"
+ _debug2 SL_Expire "$SL_Expire"
+ _debug2 SL_Login_Name "$SL_Login_Name"
+ _debug2 SL_Login_ID "$SL_Login_ID"
+ _debug2 SL_Project_Name "$SL_Project_Name"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -35,11 +47,63 @@ dns_selectel_add() {
_debug _domain "$_domain"
_info "Adding record"
- if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then
- if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then
+ if [ "$SL_Ver" = "v2" ]; then
+ _ext_srv1="/zones/"
+ _ext_srv2="/rrset/"
+ _text_tmp=$(echo "$txtvalue" | sed -En "s/[\"]*([^\"]*)/\1/p")
+ _text_tmp='\"'$_text_tmp'\"'
+ _data="{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"${fulldomain}.\", \"records\": [{\"content\":\"$_text_tmp\"}]}"
+ elif [ "$SL_Ver" = "v1" ]; then
+ _ext_srv1="/"
+ _ext_srv2="/records/"
+ _data="{\"type\":\"TXT\",\"ttl\":60,\"name\":\"$fulldomain\",\"content\":\"$txtvalue\"}"
+ else
+ _err "Error. Unsupported version API $SL_Ver"
+ return 1
+ fi
+ _ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}"
+ _debug _ext_uri "$_ext_uri"
+ _debug _data "$_data"
+
+ if _sl_rest POST "$_ext_uri" "$_data"; then
+ if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
fi
+ if _contains "$response" "already_exists"; then
+ # record TXT with $fulldomain already exists
+ if [ "$SL_Ver" = "v2" ]; then
+ # It is necessary to add one more content to the comments
+ # read all records rrset
+ _debug "Getting txt records"
+ _sl_rest GET "${_ext_uri}"
+ # There is already a $txtvalue value, no need to add it
+ if _contains "$response" "$txtvalue"; then
+ _info "Added, OK"
+ _info "Txt record ${fulldomain} with value ${txtvalue} already exists"
+ return 0
+ fi
+ # group \1 - full record rrset; group \2 - records attribute value, exactly {"content":"\"value1\""},{"content":"\"value2\""}",...
+ _record_seg="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\1/p")"
+ _record_array="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\2/p")"
+ # record id
+ _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")"
+ # preparing _data
+ _tmp_str="${_record_array},{\"content\":\"${_text_tmp}\"}"
+ _data="{\"ttl\": 60, \"records\": [${_tmp_str}]}"
+ _debug2 _record_seg "$_record_seg"
+ _debug2 _record_array "$_record_array"
+ _debug2 _record_array "$_record_id"
+ _debug "New data for record" "$_data"
+ if _sl_rest PATCH "${_ext_uri}${_record_id}" "$_data"; then
+ _info "Added, OK"
+ return 0
+ fi
+ elif [ "$SL_Ver" = "v1" ]; then
+ _info "Added, OK"
+ return 0
+ fi
+ fi
fi
_err "Add txt record error."
return 1
@@ -50,15 +114,15 @@ dns_selectel_rm() {
fulldomain=$1
txtvalue=$2
- SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
-
- if [ -z "$SL_Key" ]; then
- SL_Key=""
- _err "You don't specify slectel api key yet."
- _err "Please create you key and try again."
+ if ! _sl_init_vars "nosave"; then
return 1
fi
-
+ _debug2 SL_Ver "$SL_Ver"
+ _debug2 SL_Expire "$SL_Expire"
+ _debug2 SL_Login_Name "$SL_Login_Name"
+ _debug2 SL_Login_ID "$SL_Login_ID"
+ _debug2 SL_Project_Name "$SL_Project_Name"
+ #
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@@ -67,91 +131,195 @@ dns_selectel_rm() {
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
-
+ #
+ if [ "$SL_Ver" = "v2" ]; then
+ _ext_srv1="/zones/"
+ _ext_srv2="/rrset/"
+ elif [ "$SL_Ver" = "v1" ]; then
+ _ext_srv1="/"
+ _ext_srv2="/records/"
+ else
+ _err "Error. Unsupported version API $SL_Ver"
+ return 1
+ fi
+ #
_debug "Getting txt records"
- _sl_rest GET "/${_domain_id}/records/"
-
+ _ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}"
+ _debug _ext_uri "$_ext_uri"
+ _sl_rest GET "${_ext_uri}"
+ #
if ! _contains "$response" "$txtvalue"; then
_err "Txt record not found"
return 1
fi
-
- _record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
+ #
+ if [ "$SL_Ver" = "v2" ]; then
+ _record_seg="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\1/gp")"
+ _record_arr="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/p")"
+ elif [ "$SL_Ver" = "v1" ]; then
+ _record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
+ else
+ _err "Error. Unsupported version API $SL_Ver"
+ return 1
+ fi
_debug2 "_record_seg" "$_record_seg"
if [ -z "$_record_seg" ]; then
_err "can not find _record_seg"
return 1
fi
-
- _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)"
- _debug2 "_record_id" "$_record_id"
+ # record id
+ # the following lines change the algorithm for deleting records with the value $txtvalue
+ # if you use the 1st line, then all such records are deleted at once
+ # if you use the 2nd line, then only the first entry from them is deleted
+ #_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")"
+ _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"" | sed '1!d')"
if [ -z "$_record_id" ]; then
_err "can not find _record_id"
return 1
fi
-
- if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then
- _err "Delete record error."
- return 1
+ _debug2 "_record_id" "$_record_id"
+ # delete all record type TXT with text $txtvalue
+ if [ "$SL_Ver" = "v2" ]; then
+ # actual
+ _new_arr="$(echo "$_record_seg" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/gp" | sed -En "s/(\},\{)/}\n{/gp" | sed "/${txtvalue}/d" | sed ":a;N;s/\n/,/;ta")"
+ # uri record for DEL or PATCH
+ _del_uri="${_ext_uri}${_record_id}"
+ _debug _del_uri "$_del_uri"
+ if [ -z "$_new_arr" ]; then
+ # remove record
+ if ! _sl_rest DELETE "${_del_uri}"; then
+ _err "Delete record error: ${_del_uri}."
+ else
+ info "Delete record success: ${_del_uri}."
+ fi
+ else
+ # update a record by removing one element in content
+ _data="{\"ttl\": 60, \"records\": [${_new_arr}]}"
+ _debug2 _data "$_data"
+ # REST API PATCH call
+ if _sl_rest PATCH "${_del_uri}" "$_data"; then
+ _info "Patched, OK: ${_del_uri}"
+ else
+ _err "Patched record error: ${_del_uri}."
+ fi
+ fi
+ else
+ # legacy
+ for _one_id in $_record_id; do
+ _del_uri="${_ext_uri}${_one_id}"
+ _debug _del_uri "$_del_uri"
+ if ! _sl_rest DELETE "${_del_uri}"; then
+ _err "Delete record error: ${_del_uri}."
+ else
+ info "Delete record success: ${_del_uri}."
+ fi
+ done
fi
return 0
}
#################### Private functions below ##################################
-#_acme-challenge.www.domain.com
-#returns
-# _sub_domain=_acme-challenge.www
-# _domain=domain.com
-# _domain_id=sdjkglgdfewsdfg
+
_get_root() {
domain=$1
- if ! _sl_rest GET "/"; then
- return 1
- fi
-
- i=2
- p=1
- while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
- _debug h "$h"
- if [ -z "$h" ]; then
- #not valid
+ if [ "$SL_Ver" = 'v1' ]; then
+ # version API 1
+ if ! _sl_rest GET "/"; then
return 1
fi
-
- if _contains "$response" "\"name\" *: *\"$h\","; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
- _domain=$h
- _debug "Getting domain id for $h"
- if ! _sl_rest GET "/$h"; then
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
return 1
fi
- _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
- return 0
+ if _contains "$response" "\"name\" *: *\"$h\","; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain=$h
+ _debug "Getting domain id for $h"
+ if ! _sl_rest GET "/$h"; then
+ _err "Error read records of all domains $SL_Ver"
+ return 1
+ fi
+ _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ _err "Error read records of all domains $SL_Ver"
+ return 1
+ elif [ "$SL_Ver" = "v2" ]; then
+ # version API 2
+ _ext_uri='/zones/'
+ domain="${domain}."
+ _debug "domain:: " "$domain"
+ # read records of all domains
+ if ! _sl_rest GET "$_ext_uri"; then
+ _err "Error read records of all domains $SL_Ver"
+ return 1
fi
- p=$i
- i=$(_math "$i" + 1)
- done
- return 1
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ _err "The domain was not found among the registered ones"
+ return 1
+ fi
+ _domain_record=$(echo "$response" | sed -En "s/.*(\{[^}]*id[^}]*\"name\" *: *\"$h\"[^}]*}).*/\1/p")
+ _debug "_domain_record:: " "$_domain_record"
+ if [ -n "$_domain_record" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain=$h
+ _debug "Getting domain id for $h"
+ _domain_id=$(echo "$_domain_record" | sed -En "s/\{[^}]*\"id\" *: *\"([^\"]*)\"[^}]*\}/\1/p")
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ _err "Error read records of all domains $SL_Ver"
+ return 1
+ else
+ _err "Error. Unsupported version API $SL_Ver"
+ return 1
+ fi
}
+#################################################################
+# use: method add_url body
_sl_rest() {
m=$1
ep="$2"
data="$3"
- _debug "$ep"
- export _H1="X-Token: $SL_Key"
+ _token=$(_get_auth_token)
+ if [ -z "$_token" ]; then
+ _err "BAD key or token $ep"
+ return 1
+ fi
+ if [ "$SL_Ver" = v2 ]; then
+ _h1_name="X-Auth-Token"
+ else
+ _h1_name='X-Token'
+ fi
+ export _H1="${_h1_name}: ${_token}"
export _H2="Content-Type: application/json"
-
+ _debug2 "Full URI: " "$SL_Api/${SL_Ver}${ep}"
+ _debug2 "_H1:" "$_H1"
+ _debug2 "_H2:" "$_H2"
if [ "$m" != "GET" ]; then
_debug data "$data"
- response="$(_post "$data" "$SL_Api/$ep" "" "$m")"
+ response="$(_post "$data" "$SL_Api/${SL_Ver}${ep}" "" "$m")"
else
- response="$(_get "$SL_Api/$ep")"
+ response="$(_get "$SL_Api/${SL_Ver}${ep}")"
fi
-
+ # shellcheck disable=SC2181
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
@@ -159,3 +327,152 @@ _sl_rest() {
_debug2 response "$response"
return 0
}
+
+_get_auth_token() {
+ if [ "$SL_Ver" = 'v1' ]; then
+ # token for v1
+ _debug "Token v1"
+ _token_keystone=$SL_Key
+ elif [ "$SL_Ver" = 'v2' ]; then
+ # token for v2. Get a token for calling the API
+ _debug "Keystone Token v2"
+ token_v2=$(_readaccountconf_mutable SL_Token_V2)
+ if [ -n "$token_v2" ]; then
+ # The structure with the token was considered. Let's check its validity
+ # field 1 - SL_Login_Name
+ # field 2 - token keystone
+ # field 3 - SL_Login_ID
+ # field 4 - SL_Project_Name
+ # field 5 - Receipt time
+ # separator - '$_sl_sep'
+ _login_name=$(_getfield "$token_v2" 1 "$_sl_sep")
+ _token_keystone=$(_getfield "$token_v2" 2 "$_sl_sep")
+ _project_name=$(_getfield "$token_v2" 4 "$_sl_sep")
+ _receipt_time=$(_getfield "$token_v2" 5 "$_sl_sep")
+ _login_id=$(_getfield "$token_v2" 3 "$_sl_sep")
+ _debug2 _login_name "$_login_name"
+ _debug2 _login_id "$_login_id"
+ _debug2 _project_name "$_project_name"
+ # check the validity of the token for the user and the project and its lifetime
+ _dt_diff_minute=$((($(date +%s) - _receipt_time) / 60))
+ _debug2 _dt_diff_minute "$_dt_diff_minute"
+ [ "$_dt_diff_minute" -gt "$SL_Expire" ] && unset _token_keystone
+ if [ "$_project_name" != "$SL_Project_Name" ] || [ "$_login_name" != "$SL_Login_Name" ] || [ "$_login_id" != "$SL_Login_ID" ]; then
+ unset _token_keystone
+ fi
+ _debug "Get exists token"
+ fi
+ if [ -z "$_token_keystone" ]; then
+ # the previous token is incorrect or was not received, get a new one
+ _debug "Update (get new) token"
+ _data_auth="{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"name\":\"${SL_Login_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"},\"password\":\"${SL_Pswd}\"}}},\"scope\":{\"project\":{\"name\":\"${SL_Project_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"}}}}}"
+ export _H1="Content-Type: application/json"
+ _result=$(_post "$_data_auth" "$auth_uri")
+ _token_keystone=$(grep 'x-subject-token' "$HTTP_HEADER" | sed -nE "s/[[:space:]]*x-subject-token:[[:space:]]*([[:print:]]*)(\r*)/\1/p")
+ _dt_curr=$(date +%s)
+ SL_Token_V2="${SL_Login_Name}${_sl_sep}${_token_keystone}${_sl_sep}${SL_Login_ID}${_sl_sep}${SL_Project_Name}${_sl_sep}${_dt_curr}"
+ _saveaccountconf_mutable SL_Token_V2 "$SL_Token_V2"
+ fi
+ else
+ # token set empty for unsupported version API
+ _token_keystone=""
+ fi
+ printf -- "%s" "$_token_keystone"
+}
+
+#################################################################
+# use: [non_save]
+_sl_init_vars() {
+ _non_save="${1}"
+ _debug2 _non_save "$_non_save"
+
+ _debug "First init variables"
+ # version API
+ SL_Ver="${SL_Ver:-$(_readaccountconf_mutable SL_Ver)}"
+ if [ -z "$SL_Ver" ]; then
+ SL_Ver="v1"
+ fi
+ if ! [ "$SL_Ver" = "v1" ] && ! [ "$SL_Ver" = "v2" ]; then
+ _err "You don't specify selectel.ru API version."
+ _err "Please define specify API version."
+ fi
+ _debug2 SL_Ver "$SL_Ver"
+ if [ "$SL_Ver" = "v1" ]; then
+ # token
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify selectel.ru api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+ #save the api key to the account conf file.
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Key "$SL_Key"
+ fi
+ elif [ "$SL_Ver" = "v2" ]; then
+ # time expire token
+ SL_Expire="${SL_Expire:-$(_readaccountconf_mutable SL_Expire)}"
+ if [ -z "$SL_Expire" ]; then
+ SL_Expire=1400 # 23h 20 min
+ fi
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Expire "$SL_Expire"
+ fi
+ # login service user
+ SL_Login_Name="${SL_Login_Name:-$(_readaccountconf_mutable SL_Login_Name)}"
+ if [ -z "$SL_Login_Name" ]; then
+ SL_Login_Name=''
+ _err "You did not specify the selectel.ru API service user name."
+ _err "Please provide a service user name and try again."
+ return 1
+ fi
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Login_Name "$SL_Login_Name"
+ fi
+ # user ID
+ SL_Login_ID="${SL_Login_ID:-$(_readaccountconf_mutable SL_Login_ID)}"
+ if [ -z "$SL_Login_ID" ]; then
+ SL_Login_ID=''
+ _err "You did not specify the selectel.ru API user ID."
+ _err "Please provide a user ID and try again."
+ return 1
+ fi
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Login_ID "$SL_Login_ID"
+ fi
+ # project name
+ SL_Project_Name="${SL_Project_Name:-$(_readaccountconf_mutable SL_Project_Name)}"
+ if [ -z "$SL_Project_Name" ]; then
+ SL_Project_Name=''
+ _err "You did not specify the project name."
+ _err "Please provide a project name and try again."
+ return 1
+ fi
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Project_Name "$SL_Project_Name"
+ fi
+ # service user password
+ SL_Pswd="${SL_Pswd:-$(_readaccountconf_mutable SL_Pswd)}"
+ if [ -z "$SL_Pswd" ]; then
+ SL_Pswd=''
+ _err "You did not specify the service user password."
+ _err "Please provide a service user password and try again."
+ return 1
+ fi
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Pswd "$SL_Pswd" "12345678"
+ fi
+ else
+ SL_Ver=""
+ _err "You also specified the wrong version of the selectel.ru API."
+ _err "Please provide the correct API version and try again."
+ return 1
+ fi
+ if [ -z "$_non_save" ]; then
+ _saveaccountconf_mutable SL_Ver "$SL_Ver"
+ fi
+
+ return 0
+}
diff --git a/dnsapi/dns_selfhost.sh b/dnsapi/dns_selfhost.sh
index a6ef1f94..4912dfdf 100644
--- a/dnsapi/dns_selfhost.sh
+++ b/dnsapi/dns_selfhost.sh
@@ -1,8 +1,15 @@
#!/usr/bin/env sh
-#
-# Author: Marvin Edeler
-# Report Bugs here: https://github.com/Marvo2011/acme.sh/issues/1
-# Last Edit: 17.02.2022
+# shellcheck disable=SC2034
+dns_selfhost_info='SelfHost.de
+Site: SelfHost.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_selfhost
+Options:
+ SELFHOSTDNS_USERNAME Username
+ SELFHOSTDNS_PASSWORD Password
+ SELFHOSTDNS_MAP Subdomain name
+Issues: github.com/acmesh-official/acme.sh/issues/4291
+Author: Marvin Edeler
+'
dns_selfhost_add() {
fulldomain=$1
diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh
index f70a2294..d6994681 100755
--- a/dnsapi/dns_servercow.sh
+++ b/dnsapi/dns_servercow.sh
@@ -1,19 +1,14 @@
#!/usr/bin/env sh
-
-##########
-# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
-#
-# Usage:
-# export SERVERCOW_API_Username=username
-# export SERVERCOW_API_Password=password
-# acme.sh --issue -d example.com --dns dns_servercow
-#
-# Issues:
-# Any issues / questions / suggestions can be posted here:
-# https://github.com/jhartlep/servercow-dns-api/issues
-#
-# Author: Jens Hartlep
-##########
+# shellcheck disable=SC2034
+dns_servercow_info='ServerCow.de
+Site: ServerCow.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_servercow
+Options:
+ SERVERCOW_API_Username Username
+ SERVERCOW_API_Password Password
+Issues: github.com/jhartlep/servercow-dns-api/issues
+Author: Jens Hartlep
+'
SERVERCOW_API="https://api.servercow.de/dns/v1/domains"
@@ -53,7 +48,7 @@ dns_servercow_add() {
if printf -- "%s" "$response" | grep "{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\"" >/dev/null; then
_info "A txt record with the same name already exists."
# trim the string on the left
- txtvalue_old=${response#*{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
+ txtvalue_old=${response#*{\"name\":\""$_sub_domain"\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
# trim the string on the right
txtvalue_old=${txtvalue_old%%\"*}
@@ -86,7 +81,6 @@ dns_servercow_add() {
return 1
fi
- return 1
}
# Usage fulldomain txtvalue
@@ -142,7 +136,7 @@ _get_root() {
p=1
while true; do
- _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+ _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100)
_debug _domain "$_domain"
if [ -z "$_domain" ]; then
@@ -155,7 +149,7 @@ _get_root() {
fi
if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then
- _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$p")
if [ -z "$_sub_domain" ]; then
# not valid
return 1
diff --git a/dnsapi/dns_simply.sh b/dnsapi/dns_simply.sh
index 6a8d0e18..e0ad16e2 100644
--- a/dnsapi/dns_simply.sh
+++ b/dnsapi/dns_simply.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_simply_info='Simply.com
+Site: Simply.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_simply
+Options:
+ SIMPLY_AccountName Account name
+ SIMPLY_ApiKey API Key
+'
-# API-integration for Simply.com (https://www.simply.com)
-
-#SIMPLY_AccountName="accountname"
-#SIMPLY_ApiKey="apikey"
-#
#SIMPLY_Api="https://api.simply.com/2/"
SIMPLY_Api_Default="https://api.simply.com/2"
@@ -163,7 +166,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -176,7 +179,7 @@ _get_root() {
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
_debug "$h not found"
else
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh
new file mode 100644
index 00000000..8fff4037
--- /dev/null
+++ b/dnsapi/dns_spaceship.sh
@@ -0,0 +1,212 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_spaceship_info='Spaceship.com
+Site: Spaceship.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_spaceship
+Options:
+ SPACESHIP_API_KEY API Key
+ SPACESHIP_API_SECRET API Secret
+ SPACESHIP_ROOT_DOMAIN Root domain. Manually specify the root domain if auto-detection fails. Optional.
+Issues: github.com/acmesh-official/acme.sh/issues/6304
+Author: Meow <@Meo597>
+'
+
+# Spaceship API
+# https://docs.spaceship.dev/
+
+######## Public functions #####################
+
+SPACESHIP_API_BASE="https://spaceship.dev/api/v1"
+
+# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+dns_spaceship_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Adding TXT record for $fulldomain with value $txtvalue"
+
+ # Initialize API credentials and headers
+ if ! _spaceship_init; then
+ return 1
+ fi
+
+ # Detect root zone
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ # Extract subdomain part relative to root domain
+ subdomain=$(echo "$fulldomain" | sed "s/\.$_domain$//")
+ if [ "$subdomain" = "$fulldomain" ]; then
+ _err "Failed to extract subdomain from $fulldomain relative to root domain $_domain"
+ return 1
+ fi
+ _debug "Extracted subdomain: $subdomain for root domain: $_domain"
+
+ # Escape txtvalue to prevent JSON injection (e.g., quotes in txtvalue)
+ escaped_txtvalue=$(echo "$txtvalue" | sed 's/"/\\"/g')
+
+ # Prepare payload and URL for adding TXT record
+ # Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API
+ payload="{\"force\": true, \"items\": [{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\", \"ttl\": 600}]}"
+ url="$SPACESHIP_API_BASE/dns/records/$_domain"
+
+ # Send API request
+ if _spaceship_api_request "PUT" "$url" "$payload"; then
+ _info "Successfully added TXT record for $fulldomain"
+ return 0
+ else
+ _err "Failed to add TXT record. If the domain $_domain is incorrect, set SPACESHIP_ROOT_DOMAIN to the correct root domain."
+ return 1
+ fi
+}
+
+# Usage: fulldomain txtvalue
+# Used to remove the txt record after validation
+dns_spaceship_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Removing TXT record for $fulldomain with value $txtvalue"
+
+ # Initialize API credentials and headers
+ if ! _spaceship_init; then
+ return 1
+ fi
+
+ # Detect root zone
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ # Extract subdomain part relative to root domain
+ subdomain=$(echo "$fulldomain" | sed "s/\.$_domain$//")
+ if [ "$subdomain" = "$fulldomain" ]; then
+ _err "Failed to extract subdomain from $fulldomain relative to root domain $_domain"
+ return 1
+ fi
+ _debug "Extracted subdomain: $subdomain for root domain: $_domain"
+
+ # Escape txtvalue to prevent JSON injection
+ escaped_txtvalue=$(echo "$txtvalue" | sed 's/"/\\"/g')
+
+ # Prepare payload and URL for deleting TXT record
+ # Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API
+ payload="[{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\"}]"
+ url="$SPACESHIP_API_BASE/dns/records/$_domain"
+
+ # Send API request
+ if _spaceship_api_request "DELETE" "$url" "$payload"; then
+ _info "Successfully deleted TXT record for $fulldomain"
+ return 0
+ else
+ _err "Failed to delete TXT record. If the domain $_domain is incorrect, set SPACESHIP_ROOT_DOMAIN to the correct root domain."
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
+
+_spaceship_init() {
+ SPACESHIP_API_KEY="${SPACESHIP_API_KEY:-$(_readaccountconf_mutable SPACESHIP_API_KEY)}"
+ SPACESHIP_API_SECRET="${SPACESHIP_API_SECRET:-$(_readaccountconf_mutable SPACESHIP_API_SECRET)}"
+
+ if [ -z "$SPACESHIP_API_KEY" ] || [ -z "$SPACESHIP_API_SECRET" ]; then
+ _err "Spaceship API credentials are not set. Please set SPACESHIP_API_KEY and SPACESHIP_API_SECRET."
+ _err "Ensure \"$LE_CONFIG_HOME\" directory has restricted permissions (chmod 700 \"$LE_CONFIG_HOME\") to protect credentials."
+ return 1
+ fi
+
+ # Save credentials to account config for future renewals
+ _saveaccountconf_mutable SPACESHIP_API_KEY "$SPACESHIP_API_KEY"
+ _saveaccountconf_mutable SPACESHIP_API_SECRET "$SPACESHIP_API_SECRET"
+
+ # Set common headers for API requests
+ export _H1="X-API-Key: $SPACESHIP_API_KEY"
+ export _H2="X-API-Secret: $SPACESHIP_API_SECRET"
+ export _H3="Content-Type: application/json"
+ return 0
+}
+
+_get_root() {
+ domain="$1"
+
+ # Check manual override
+ SPACESHIP_ROOT_DOMAIN="${SPACESHIP_ROOT_DOMAIN:-$(_readdomainconf SPACESHIP_ROOT_DOMAIN)}"
+ if [ -n "$SPACESHIP_ROOT_DOMAIN" ]; then
+ _domain="$SPACESHIP_ROOT_DOMAIN"
+ _debug "Using manually specified or saved root domain: $_domain"
+ _savedomainconf SPACESHIP_ROOT_DOMAIN "$SPACESHIP_ROOT_DOMAIN"
+ return 0
+ fi
+
+ _debug "Detecting root zone for '$domain'"
+
+ i=1
+ p=1
+ while true; do
+ _cutdomain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+
+ _debug "Attempt i=$i: Checking if '$_cutdomain' is root zone (cut ret=$?)"
+
+ if [ -z "$_cutdomain" ]; then
+ _debug "Cut resulted in empty string, root zone not found."
+ break
+ fi
+
+ # Call the API to check if this _cutdomain is a manageable zone
+ if _spaceship_api_request "GET" "$SPACESHIP_API_BASE/dns/records/$_cutdomain?take=1&skip=0"; then
+ # API call succeeded (HTTP 200 OK for GET /dns/records)
+ _domain="$_cutdomain"
+ _debug "Root zone found: '$_domain'"
+
+ # Save the detected root domain
+ _savedomainconf SPACESHIP_ROOT_DOMAIN "$_domain"
+ _info "Root domain '$_domain' saved to configuration for future use."
+
+ return 0
+ fi
+
+ _debug "API check failed for '$_cutdomain'. Continuing search."
+
+ p=$i
+ i=$((i + 1))
+ done
+
+ _err "Could not detect root zone for '$domain'. Please set SPACESHIP_ROOT_DOMAIN manually."
+ return 1
+}
+
+_spaceship_api_request() {
+ method="$1"
+ url="$2"
+ payload="$3"
+
+ _debug2 "Sending $method request to $url with payload $payload"
+ if [ "$method" = "GET" ]; then
+ response="$(_get "$url")"
+ else
+ response="$(_post "$payload" "$url" "" "$method")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "API request failed. Response: $response"
+ return 1
+ fi
+
+ _debug2 "API response body: $response"
+
+ if [ "$method" = "GET" ]; then
+ if _contains "$(_head_n 1 <"$HTTP_HEADER")" '200'; then
+ return 0
+ fi
+ else
+ if _contains "$(_head_n 1 <"$HTTP_HEADER")" '204'; then
+ return 0
+ fi
+ fi
+
+ _debug2 "API response header: $HTTP_HEADER"
+ return 1
+}
diff --git a/dnsapi/dns_technitium.sh b/dnsapi/dns_technitium.sh
new file mode 100755
index 00000000..7bc0dd48
--- /dev/null
+++ b/dnsapi/dns_technitium.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_technitium_info='Technitium DNS Server
+Site: Technitium.com/dns/
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_technitium
+Options:
+ Technitium_Server Server Address
+ Technitium_Token API Token
+Issues: github.com/acmesh-official/acme.sh/issues/6116
+Author: Henning Reich
+'
+
+dns_technitium_add() {
+ _info "add txt Record using Technitium"
+ _Technitium_account
+ fulldomain=$1
+ txtvalue=$2
+ response="$(_get "$Technitium_Server/api/zones/records/add?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}")"
+ if _contains "$response" '"status":"ok"'; then
+ return 0
+ fi
+ _err "Could not add txt record."
+ return 1
+}
+
+dns_technitium_rm() {
+ _info "remove txt record using Technitium"
+ _Technitium_account
+ fulldomain=$1
+ txtvalue=$2
+ response="$(_get "$Technitium_Server/api/zones/records/delete?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}")"
+ if _contains "$response" '"status":"ok"'; then
+ return 0
+ fi
+ _err "Could not remove txt record"
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_Technitium_account() {
+ Technitium_Server="${Technitium_Server:-$(_readaccountconf_mutable Technitium_Server)}"
+ Technitium_Token="${Technitium_Token:-$(_readaccountconf_mutable Technitium_Token)}"
+ if [ -z "$Technitium_Server" ] || [ -z "$Technitium_Token" ]; then
+ Technitium_Server=""
+ Technitium_Token=""
+ _err "You don't specify Technitium Server and Token yet."
+ _err "Please create your Token and add server address and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable Technitium_Server "$Technitium_Server"
+ _saveaccountconf_mutable Technitium_Token "$Technitium_Token"
+}
diff --git a/dnsapi/dns_tele3.sh b/dnsapi/dns_tele3.sh
index 76c90913..3a3ccf8c 100644
--- a/dnsapi/dns_tele3.sh
+++ b/dnsapi/dns_tele3.sh
@@ -1,14 +1,13 @@
#!/usr/bin/env sh
-#
-# tele3.cz DNS API
-#
-# Author: Roman Blizik
-# Report Bugs here: https://github.com/par-pa/acme.sh
-#
-# --
-# export TELE3_Key="MS2I4uPPaI..."
-# export TELE3_Secret="kjhOIHGJKHg"
-# --
+# shellcheck disable=SC2034
+dns_tele3_info='tele3.cz
+Site: tele3.cz
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#tele3
+Options:
+ TELE3_Key API Key
+ TELE3_Secret API Secret
+Author: Roman Blizik <@par-pa>
+'
TELE3_API="https://www.tele3.cz/acme/"
diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
new file mode 100644
index 00000000..b148adc3
--- /dev/null
+++ b/dnsapi/dns_tencent.sh
@@ -0,0 +1,217 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_tencent_info='Tencent.com
+Site: cloud.Tencent.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_tencent
+Options:
+ Tencent_SecretId Secret ID
+ Tencent_SecretKey Secret Key
+Issues: github.com/acmesh-official/acme.sh/issues/4781
+'
+Tencent_API="https://dnspod.tencentcloudapi.com"
+
+#Usage: dns_tencent_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_tencent_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+ Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+ if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then
+ Tencent_SecretId=""
+ Tencent_SecretKey=""
+ _err "You don't specify tencent api SecretId and SecretKey yet."
+ return 1
+ fi
+
+ #save the api SecretId and SecretKey to the account conf file.
+ _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId"
+ _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ _debug "Add record"
+ _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord"
+}
+
+dns_tencent_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+ Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ _debug "Get record list"
+ attempt=1
+ max_attempts=5
+ while [ -z "$record_id" ] && [ "$attempt" -le $max_attempts ]; do
+ _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
+ record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+ _debug2 record_id "$record_id"
+ if [ -z "$record_id" ]; then
+ _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying"
+ _sleep 10
+ attempt=$(_math "$attempt + 1")
+ fi
+ done
+
+ record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+ _debug2 record_id "$record_id"
+
+ if [ -z "$record_id" ]; then
+ _debug "record not found after $max_attempts attempts, skip"
+ else
+ _debug "Delete record"
+ _delete_record_query "$record_id" && _tencent_rest "DeleteRecord"
+ fi
+}
+
+#################### Private functions below ##################################
+
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _describe_records_query "$h" "@"
+ if ! _tencent_rest "DescribeRecordList" "ignore"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"TotalCount\":"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _debug _sub_domain "$_sub_domain"
+ _domain="$h"
+ _debug _domain "$_domain"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_tencent_rest() {
+ action=$1
+ service="dnspod"
+ payload="${query}"
+ timestamp=$(date -u +%s)
+
+ token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp")
+ version="2021-03-23"
+
+ if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then
+ _err "Error <$1>"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ if [ -z "$2" ]; then
+ message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+ if [ "$message" ]; then
+ _err "$message"
+ return 1
+ fi
+ fi
+}
+
+_add_record_query() {
+ query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}"
+}
+
+_describe_records_query() {
+ query="{\"Domain\":\"$1\",\"Limit\":3000}"
+}
+
+_delete_record_query() {
+ query="{\"Domain\":\"$_domain\",\"RecordId\":$1}"
+}
+
+_check_exist_query() {
+ _domain="$1"
+ _subdomain="$2"
+ _value="$3"
+ query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}"
+}
+
+# shell client for tencent cloud api v3 | @author: rehiy
+
+tencent_sha256() {
+ printf %b "$@" | _digest sha256 hex
+}
+
+tencent_hmac_sha256() {
+ k=$1
+ shift
+ hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ')
+ printf %b "$@" | _hmac sha256 "$hex_key" hex
+}
+
+tencent_hmac_sha256_hexkey() {
+ k=$1
+ shift
+ printf %b "$@" | _hmac sha256 "$k" hex
+}
+
+tencent_signature_v3() {
+ service=$1
+ action=$(echo "$2" | _lower_case)
+ payload=${3:-'{}'}
+ timestamp=${4:-$(date +%s)}
+
+ domain="$service.tencentcloudapi.com"
+ secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'}
+ secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'}
+
+ algorithm='TC3-HMAC-SHA256'
+ date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null)
+ [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d)
+
+ canonicalUri='/'
+ canonicalQuery=''
+ canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n"
+
+ signedHeaders='content-type;host;x-tc-action'
+ canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")"
+
+ credentialScope="$date/$service/tc3_request"
+ stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
+
+ secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
+ secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")
+ secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request')
+ signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign")
+
+ echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
+}
+
+tencent_api_request() {
+ service=$1
+ version=$2
+ action=$3
+ payload=${4:-'{}'}
+ timestamp=${5:-$(date +%s)}
+
+ token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp")
+
+ _H1="Content-Type: application/json"
+ _H2="Authorization: $token"
+ _H3="X-TC-Version: $version"
+ _H4="X-TC-Timestamp: $timestamp"
+ _H5="X-TC-Action: $action"
+
+ _post "$payload" "$Tencent_API" "" "POST" "application/json"
+}
diff --git a/dnsapi/dns_timeweb.sh b/dnsapi/dns_timeweb.sh
new file mode 100644
index 00000000..7040ac9a
--- /dev/null
+++ b/dnsapi/dns_timeweb.sh
@@ -0,0 +1,403 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_timeweb_info='Timeweb.Cloud
+Site: Timeweb.Cloud
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_timeweb
+Options:
+ TW_Token API JWT token. Get it from the control panel at https://timeweb.cloud/my/api-keys
+Issues: github.com/acmesh-official/acme.sh/issues/5140
+Author: Nikolay Pronchev <@nikolaypronchev>
+'
+
+TW_Api="https://api.timeweb.cloud/api/v1"
+
+################ Public functions ################
+
+# Adds an ACME DNS-01 challenge DNS TXT record via the Timeweb Cloud API.
+#
+# Param1: The ACME DNS-01 challenge FQDN.
+# Param2: The value of the ACME DNS-01 challenge TXT record.
+#
+# Example: dns_timeweb_add "_acme-challenge.sub.domain.com" "D-52Wm...4uYM"
+dns_timeweb_add() {
+ _debug "$(__green "Timeweb DNS API"): \"dns_timeweb_add\" started."
+
+ _timeweb_set_acme_fqdn "$1" || return 1
+ _timeweb_set_acme_txt "$2" || return 1
+ _timeweb_check_token || return 1
+ _timeweb_split_acme_fqdn || return 1
+ _timeweb_dns_txt_add || return 1
+
+ _debug "$(__green "Timeweb DNS API"): \"dns_timeweb_add\" finished."
+}
+
+# Removes a DNS TXT record via the Timeweb Cloud API.
+#
+# Param1: The ACME DNS-01 challenge FQDN.
+# Param2: The value of the ACME DNS-01 challenge TXT record.
+#
+# Example: dns_timeweb_rm "_acme-challenge.sub.domain.com" "D-52Wm...4uYM"
+dns_timeweb_rm() {
+ _debug "$(__green "Timeweb DNS API"): \"dns_timeweb_rm\" started."
+
+ _timeweb_set_acme_fqdn "$1" || return 1
+ _timeweb_set_acme_txt "$2" || return 1
+ _timeweb_check_token || return 1
+ _timeweb_split_acme_fqdn || return 1
+ _timeweb_get_dns_txt || return 1
+ _timeweb_dns_txt_remove || return 1
+
+ _debug "$(__green "Timeweb DNS API"): \"dns_timeweb_rm\" finished."
+}
+
+################ Private functions ################
+
+# Checks and sets the ACME DNS-01 challenge FQDN.
+#
+# Param1: The ACME DNS-01 challenge FQDN.
+#
+# Example: _timeweb_set_acme_fqdn "_acme-challenge.sub.domain.com"
+#
+# Sets the "Acme_Fqdn" variable (_acme-challenge.sub.domain.com)
+_timeweb_set_acme_fqdn() {
+ Acme_Fqdn=$1
+ _debug "Setting ACME DNS-01 challenge FQDN \"$Acme_Fqdn\"."
+ [ -z "$Acme_Fqdn" ] && {
+ _err "ACME DNS-01 challenge FQDN is empty."
+ return 1
+ }
+ return 0
+}
+
+# Checks and sets the value of the ACME DNS-01 challenge TXT record.
+#
+# Param1: Value of the ACME DNS-01 challenge TXT record.
+#
+# Example: _timeweb_set_acme_txt "D-52Wm...4uYM"
+#
+# Sets the "Acme_Txt" variable to the provided value (D-52Wm...4uYM)
+_timeweb_set_acme_txt() {
+ Acme_Txt=$1
+ _debug "Setting the value of the ACME DNS-01 challenge TXT record to \"$Acme_Txt\"."
+ [ -z "$Acme_Txt" ] && {
+ _err "ACME DNS-01 challenge TXT record value is empty."
+ return 1
+ }
+ return 0
+}
+
+# Checks if the Timeweb Cloud API JWT token is present (refer to the script description).
+# Adds or updates the token in the acme.sh account configuration.
+_timeweb_check_token() {
+ _debug "Checking for the presence of the Timeweb Cloud API JWT token."
+
+ TW_Token="${TW_Token:-$(_readaccountconf_mutable TW_Token)}"
+
+ [ -z "$TW_Token" ] && {
+ _err "Timeweb Cloud API JWT token was not found."
+ return 1
+ }
+
+ _saveaccountconf_mutable TW_Token "$TW_Token"
+}
+
+# Divides the ACME DNS-01 challenge FQDN into its main domain and subdomain components.
+_timeweb_split_acme_fqdn() {
+ _debug "Trying to divide \"$Acme_Fqdn\" into its main domain and subdomain components."
+
+ TW_Page_Limit=100
+ TW_Page_Offset=0
+ TW_Domains_Returned=""
+
+ while [ -z "$TW_Domains_Returned" ] || [ "$TW_Domains_Returned" -ge "$TW_Page_Limit" ]; do
+
+ _timeweb_list_domains "$TW_Page_Limit" "$TW_Page_Offset" || return 1
+
+ # Remove the 'subdomains' subarray to prevent confusion with FQDNs.
+
+ TW_Domains=$(
+ echo "$TW_Domains" |
+ sed 's/"subdomains":\[[^]]*]//g'
+ )
+
+ [ -z "$TW_Domains" ] && {
+ _err "Failed to parse the list of domains."
+ return 1
+ }
+
+ while
+ TW_Domain=$(
+ echo "$TW_Domains" |
+ sed -n 's/.*{[^{]*"fqdn":"\([^"]*\)"[^}]*}.*/\1/p'
+ )
+
+ [ -n "$TW_Domain" ] && {
+ _timeweb_is_main_domain "$TW_Domain" && return 0
+
+ TW_Domains=$(
+ echo "$TW_Domains" |
+ sed 's/{\([^{]*"fqdn":"'"$TW_Domain"'"[^}]*\)}//'
+ )
+ continue
+ }
+ do :; done
+
+ TW_Page_Offset=$(_math "$TW_Page_Offset" + "$TW_Page_Limit")
+ done
+
+ _err "Failed to divide \"$Acme_Fqdn\" into its main domain and subdomain components."
+ return 1
+}
+
+# Searches for a previously added DNS TXT record.
+#
+# Sets the "TW_Dns_Txt_Id" variable.
+_timeweb_get_dns_txt() {
+ _debug "Trying to locate a DNS TXT record with the value \"$Acme_Txt\"."
+
+ TW_Page_Limit=100
+ TW_Page_Offset=0
+ TW_Dns_Records_Returned=""
+
+ while [ -z "$TW_Dns_Records_Returned" ] || [ "$TW_Dns_Records_Returned" -ge "$TW_Page_Limit" ]; do
+
+ _timeweb_list_dns_records "$TW_Page_Limit" "$TW_Page_Offset" || return 1
+
+ while
+ Dns_Record=$(
+ echo "$TW_Dns_Records" |
+ sed -n 's/.*{\([^{]*{[^{]*'"$Acme_Txt"'[^}]*}[^}]*\)}.*/\1/p'
+ )
+
+ [ -n "$Dns_Record" ] && {
+ _timeweb_is_added_txt "$Dns_Record" && return 0
+
+ TW_Dns_Records=$(
+ echo "$TW_Dns_Records" |
+ sed 's/{\([^{]*{[^{]*'"$Acme_Txt"'[^}]*}[^}]*\)}//'
+ )
+ continue
+ }
+ do :; done
+
+ TW_Page_Offset=$(_math "$TW_Page_Offset" + "$TW_Page_Limit")
+ done
+
+ _err "DNS TXT record was not found."
+ return 1
+}
+
+# Lists domains via the Timeweb Cloud API.
+#
+# Param 1: Limit for listed domains.
+# Param 2: Offset for domains list.
+#
+# Sets the "TW_Domains" variable.
+# Sets the "TW_Domains_Returned" variable.
+_timeweb_list_domains() {
+ _debug "Listing domains via Timeweb Cloud API. Limit: $1, offset: $2."
+
+ export _H1="Authorization: Bearer $TW_Token"
+
+ if ! TW_Domains=$(_get "$TW_Api/domains?limit=$1&offset=$2"); then
+ _err "The request to the Timeweb Cloud API failed."
+ return 1
+ fi
+
+ [ -z "$TW_Domains" ] && {
+ _err "Empty response from the Timeweb Cloud API."
+ return 1
+ }
+
+ TW_Domains_Returned=$(
+ echo "$TW_Domains" |
+ sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/'
+ )
+
+ [ -z "$TW_Domains_Returned" ] && {
+ _err "Failed to extract the total count of domains."
+ return 1
+ }
+
+ [ "$TW_Domains_Returned" -eq "0" ] && {
+ _err "Domains are missing."
+ return 1
+ }
+
+ _debug "Domains returned by Timeweb Cloud API: $TW_Domains_Returned."
+}
+
+# Lists domain DNS records via the Timeweb Cloud API.
+#
+# Param 1: Limit for listed DNS records.
+# Param 2: Offset for DNS records list.
+#
+# Sets the "TW_Dns_Records" variable.
+# Sets the "TW_Dns_Records_Returned" variable.
+_timeweb_list_dns_records() {
+ _debug "Listing domain DNS records via the Timeweb Cloud API. Limit: $1, offset: $2."
+
+ export _H1="Authorization: Bearer $TW_Token"
+
+ if ! TW_Dns_Records=$(_get "$TW_Api/domains/$TW_Main_Domain/dns-records?limit=$1&offset=$2"); then
+ _err "The request to the Timeweb Cloud API failed."
+ return 1
+ fi
+
+ [ -z "$TW_Dns_Records" ] && {
+ _err "Empty response from the Timeweb Cloud API."
+ return 1
+ }
+
+ TW_Dns_Records_Returned=$(
+ echo "$TW_Dns_Records" |
+ sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/'
+ )
+
+ [ -z "$TW_Dns_Records_Returned" ] && {
+ _err "Failed to extract the total count of DNS records."
+ return 1
+ }
+
+ [ "$TW_Dns_Records_Returned" -eq "0" ] && {
+ _err "DNS records are missing."
+ return 1
+ }
+
+ _debug "DNS records returned by Timeweb Cloud API: $TW_Dns_Records_Returned."
+}
+
+# Verifies whether the domain is the primary domain for the ACME DNS-01 challenge FQDN.
+# The requirement is that the provided domain is the top-level domain
+# for the ACME DNS-01 challenge FQDN.
+#
+# Param 1: Domain object returned by Timeweb Cloud API.
+#
+# Sets the "TW_Main_Domain" variable (e.g. "_acme-challenge.s1.domain.co.uk" → "domain.co.uk").
+# Sets the "TW_Subdomains" variable (e.g. "_acme-challenge.s1.domain.co.uk" → "_acme-challenge.s1").
+_timeweb_is_main_domain() {
+ _debug "Checking if \"$1\" is the main domain of the ACME DNS-01 challenge FQDN."
+
+ [ -z "$1" ] && {
+ _debug "Failed to extract FQDN. Skipping domain."
+ return 1
+ }
+
+ ! echo ".$Acme_Fqdn" | grep -qi "\.$1$" && {
+ _debug "Domain does not match the ACME DNS-01 challenge FQDN. Skipping domain."
+ return 1
+ }
+
+ TW_Main_Domain=$1
+ TW_Subdomains=$(
+ echo "$Acme_Fqdn" |
+ sed "s/\.*.\{${#1}\}$//"
+ )
+
+ _debug "Matched domain. ACME DNS-01 challenge FQDN split as [$TW_Subdomains].[$TW_Main_Domain]."
+ return 0
+}
+
+# Verifies whether a DNS record was previously added based on the following criteria:
+# - The value matches the ACME DNS-01 challenge TXT record value;
+# - The record type is TXT;
+# - The subdomain matches the ACME DNS-01 challenge FQDN.
+#
+# Param 1: DNS record object returned by Timeweb Cloud API.
+#
+# Sets the "TW_Dns_Txt_Id" variable.
+_timeweb_is_added_txt() {
+ _debug "Checking if \"$1\" is a previously added DNS TXT record."
+
+ echo "$1" | grep -qv '"type":"TXT"' && {
+ _debug "Not a TXT record. Skipping the record."
+ return 1
+ }
+
+ if [ -n "$TW_Subdomains" ]; then
+ echo "$1" | grep -qvi "\"subdomain\":\"$TW_Subdomains\"" && {
+ _debug "Subdomains do not match. Skipping the record."
+ return 1
+ }
+ else
+ echo "$1" | grep -q '"subdomain\":"..*"' && {
+ _debug "Subdomains do not match. Skipping the record."
+ return 1
+ }
+ fi
+
+ TW_Dns_Txt_Id=$(
+ echo "$1" |
+ sed 's/.*"id":\([0-9]*\)[^0-9].*/\1/'
+ )
+
+ [ -z "$TW_Dns_Txt_Id" ] && {
+ _debug "Failed to extract the DNS record ID. Skipping the record."
+ return 1
+ }
+
+ _debug "Matching DNS TXT record ID is \"$TW_Dns_Txt_Id\"."
+ return 0
+}
+
+# Adds a DNS TXT record via the Timeweb Cloud API.
+_timeweb_dns_txt_add() {
+ _debug "Adding a new DNS TXT record via the Timeweb Cloud API."
+
+ export _H1="Authorization: Bearer $TW_Token"
+ export _H2="Content-Type: application/json"
+
+ if ! TW_Response=$(
+ _post "{
+ \"subdomain\":\"$TW_Subdomains\",
+ \"type\":\"TXT\",
+ \"value\":\"$Acme_Txt\"
+ }" \
+ "$TW_Api/domains/$TW_Main_Domain/dns-records"
+ ); then
+ _err "The request to the Timeweb Cloud API failed."
+ return 1
+ fi
+
+ [ -z "$TW_Response" ] && {
+ _err "An unexpected empty response was received from the Timeweb Cloud API."
+ return 1
+ }
+
+ TW_Dns_Txt_Id=$(
+ echo "$TW_Response" |
+ sed 's/.*"id":\([0-9]*\)[^0-9].*/\1/'
+ )
+
+ [ -z "$TW_Dns_Txt_Id" ] && {
+ _err "Failed to extract the DNS TXT Record ID."
+ return 1
+ }
+
+ _debug "DNS TXT record has been added. ID: \"$TW_Dns_Txt_Id\"."
+}
+
+# Removes a DNS record via the Timeweb Cloud API.
+_timeweb_dns_txt_remove() {
+ _debug "Removing DNS record via the Timeweb Cloud API."
+
+ export _H1="Authorization: Bearer $TW_Token"
+
+ if ! TW_Response=$(
+ _post \
+ "" \
+ "$TW_Api/domains/$TW_Main_Domain/dns-records/$TW_Dns_Txt_Id" \
+ "" \
+ "DELETE"
+ ); then
+ _err "The request to the Timeweb Cloud API failed."
+ return 1
+ fi
+
+ [ -n "$TW_Response" ] && {
+ _err "Received an unexpected response body from the Timeweb Cloud API."
+ return 1
+ }
+
+ _debug "DNS TXT record with ID \"$TW_Dns_Txt_Id\" has been removed."
+}
diff --git a/dnsapi/dns_transip.sh b/dnsapi/dns_transip.sh
index 64a256ec..b3c5ed70 100644
--- a/dnsapi/dns_transip.sh
+++ b/dnsapi/dns_transip.sh
@@ -1,4 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_transip_info='TransIP.nl
+Site: TransIP.nl
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_transip
+Options:
+ TRANSIP_Username Username
+ TRANSIP_Key_File Private key file path
+Issues: github.com/acmesh-official/acme.sh/issues/2949
+'
+
TRANSIP_Api_Url="https://api.transip.nl/v6"
TRANSIP_Token_Read_Only="false"
TRANSIP_Token_Expiration="30 minutes"
@@ -14,7 +24,7 @@ dns_transip_add() {
_debug txtvalue="$txtvalue"
_transip_setup "$fulldomain" || return 1
_info "Creating TXT record."
- if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
+ if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":60}}"; then
_err "Could not add TXT record."
return 1
fi
@@ -28,7 +38,7 @@ dns_transip_rm() {
_debug txtvalue="$txtvalue"
_transip_setup "$fulldomain" || return 1
_info "Removing TXT record."
- if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
+ if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":60}}"; then
_err "Could not remove TXT record $_sub_domain for $domain"
return 1
fi
@@ -45,14 +55,14 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
if _transip_rest GET "domains/$h/dns" && _contains "$response" "dnsEntries"; then
diff --git a/dnsapi/dns_udr.sh b/dnsapi/dns_udr.sh
index caada826..656a0557 100644
--- a/dnsapi/dns_udr.sh
+++ b/dnsapi/dns_udr.sh
@@ -1,14 +1,14 @@
#!/usr/bin/env sh
-
-# united-domains Reselling (https://www.ud-reselling.com/) DNS API
-# Author: Andreas Scherer (https://github.com/andischerer)
-# Created: 2021-02-01
-#
-# Set the environment variables as below:
-#
-# export UDR_USER="your_username_goes_here"
-# export UDR_PASS="some_password_goes_here"
-#
+# shellcheck disable=SC2034
+dns_udr_info='united-domains Reselling
+Site: ud-reselling.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_udr
+Options:
+ UDR_USER Username
+ UDR_PASS Password
+Issues: github.com/acmesh-official/acme.sh/issues/3923
+Author: Andreas Scherer <@andischerer>
+'
UDR_API="https://api.domainreselling.de/api/call.cgi"
UDR_TTL="30"
@@ -115,7 +115,7 @@ _get_root() {
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
diff --git a/dnsapi/dns_ultra.sh b/dnsapi/dns_ultra.sh
index 0f26bd97..e8da431c 100644
--- a/dnsapi/dns_ultra.sh
+++ b/dnsapi/dns_ultra.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-#
-# ULTRA_USR="your_user_goes_here"
-#
-# ULTRA_PWD="some_password_goes_here"
+# shellcheck disable=SC2034
+dns_ultra_info='UltraDNS.com
+Site: UltraDNS.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ultra
+Options:
+ ULTRA_USR Username
+ ULTRA_PWD Password
+Issues: github.com/acmesh-official/acme.sh/issues/2118
+'
ULTRA_API="https://api.ultradns.com/v3/"
ULTRA_AUTH_API="https://api.ultradns.com/v2/"
@@ -111,7 +115,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
_debug response "$response"
if [ -z "$h" ]; then
@@ -124,7 +128,7 @@ _get_root() {
if _contains "${response}" "${h}." >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "${h}" | head -1)
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="${h}"
_debug sub_domain "${_sub_domain}"
_debug domain "${_domain}"
diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh
index 13ba8a00..ff70c8b6 100644
--- a/dnsapi/dns_unoeuro.sh
+++ b/dnsapi/dns_unoeuro.sh
@@ -1,9 +1,13 @@
#!/usr/bin/env sh
-
-#
-#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#
-#UNO_User="UExxxxxx"
+# shellcheck disable=SC2034
+dns_unoeuro_info='unoeuro.com
+ Deprecated. The unoeuro.com is now simply.com
+Site: unoeuro.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_unoeuro
+Options:
+ UNO_Key API Key
+ UNO_User Username
+'
Uno_Api="https://api.simply.com/1"
@@ -129,7 +133,7 @@ _get_root() {
i=2
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -143,7 +147,7 @@ _get_root() {
if _contains "$response" "\"status\": 200"; then
_domain_id=$h
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh
index a35b8f0f..fa38bbb6 100644
--- a/dnsapi/dns_variomedia.sh
+++ b/dnsapi/dns_variomedia.sh
@@ -1,7 +1,12 @@
#!/usr/bin/env sh
-
-#
-#VARIOMEDIA_API_TOKEN=000011112222333344445555666677778888
+# shellcheck disable=SC2034
+dns_variomedia_info='variomedia.de
+Site: variomedia.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_variomedia
+Options:
+ VARIOMEDIA_API_TOKEN API Token
+Issues: github.com/acmesh-official/acme.sh/issues/2564
+'
VARIOMEDIA_API="https://api.variomedia.de"
@@ -69,7 +74,7 @@ dns_variomedia_rm() {
return 1
fi
- _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
+ _record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge."
@@ -93,11 +98,11 @@ dns_variomedia_rm() {
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
- fulldomain=$1
+ domain=$1
i=1
+ p=1
while true; do
- h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
- _debug h "$h"
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
return 1
fi
@@ -106,17 +111,14 @@ _get_root() {
return 1
fi
- if _startswith "$response" "\{\"data\":"; then
- if _contains "$response" "\"id\":\"$h\""; then
- _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
- _domain=$h
- return 0
- fi
+ if _contains "$response" "\"id\":\"$h\""; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-"$p")
+ _domain="$h"
+ return 0
fi
+ p=$i
i=$(_math "$i" + 1)
done
-
- _debug "root domain not found"
return 1
}
diff --git a/dnsapi/dns_veesp.sh b/dnsapi/dns_veesp.sh
index b8a41d00..1afeeb30 100644
--- a/dnsapi/dns_veesp.sh
+++ b/dnsapi/dns_veesp.sh
@@ -1,10 +1,14 @@
#!/usr/bin/env sh
-
-# bug reports to stepan@plyask.in
-
-#
-# export VEESP_User="username"
-# export VEESP_Password="password"
+# shellcheck disable=SC2034
+dns_veesp_info='veesp.com
+Site: veesp.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_veesp
+Options:
+ VEESP_User Username
+ VEESP_Password Password
+Issues: github.com/acmesh-official/acme.sh/issues/3712
+Author:
+'
VEESP_Api="https://secure.veesp.com/api"
@@ -108,7 +112,7 @@ _get_root() {
return 1
fi
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -121,7 +125,7 @@ _get_root() {
_service_id=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$h\",\"service_id\":[^}]*" | cut -d : -f 3 | cut -d '"' -f 2)
_debug _service_id "$_service_id"
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
diff --git a/dnsapi/dns_vercel.sh b/dnsapi/dns_vercel.sh
index 7bf6b0e5..469f7670 100644
--- a/dnsapi/dns_vercel.sh
+++ b/dnsapi/dns_vercel.sh
@@ -1,11 +1,14 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_vercel_info='Vercel.com
+Site: Vercel.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_vercel
+Options:
+ VERCEL_TOKEN API Token
+'
-# Vercel DNS API
-#
# This is your API token which can be acquired on the account page.
# https://vercel.com/account/tokens
-#
-# VERCEL_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
VERCEL_API="https://api.vercel.com"
@@ -91,7 +94,7 @@ _get_root() {
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
@@ -102,7 +105,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh
index d717d6e2..faf3105d 100755
--- a/dnsapi/dns_vscale.sh
+++ b/dnsapi/dns_vscale.sh
@@ -1,11 +1,13 @@
#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_vscale_info='vscale.io
+Site: vscale.io
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_vscale
+Options:
+ VSCALE_API_KEY API Key
+Author: Alex Loban <@LAV45>
+'
-#This is the vscale.io api wrapper for acme.sh
-#
-#Author: Alex Loban
-#Report Bugs here: https://github.com/LAV45/acme.sh
-
-#VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
VSCALE_API_URL="https://api.vscale.io/v1"
######## Public functions #####################
@@ -95,7 +97,7 @@ _get_root() {
if _vscale_rest GET "domains/"; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -106,7 +108,7 @@ _get_root() {
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_vultr.sh b/dnsapi/dns_vultr.sh
index bd925fdb..4002e5de 100644
--- a/dnsapi/dns_vultr.sh
+++ b/dnsapi/dns_vultr.sh
@@ -1,7 +1,12 @@
#!/usr/bin/env sh
-
-#
-#VULTR_API_KEY=000011112222333344445555666677778888
+# shellcheck disable=SC2034
+dns_vultr_info='vultr.com
+Site: vultr.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_vultr
+Options:
+ VULTR_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/2374
+'
VULTR_Api="https://api.vultr.com/v2"
@@ -78,7 +83,7 @@ dns_vultr_rm() {
return 1
fi
- _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep -- "$txtvalue" | tr ',' '\n' | grep -i 'id' | cut -d : -f 2)"
+ _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep -- "$txtvalue" | tr ',' '\n' | grep -i 'id' | cut -d : -f 2 | tr -d '"')"
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge."
@@ -106,7 +111,7 @@ _get_root() {
domain=$1
i=1
while true; do
- _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _domain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$_domain"
if [ -z "$_domain" ]; then
return 1
@@ -116,7 +121,7 @@ _get_root() {
return 1
fi
- if printf "%s\n" "$response" | grep '^\{.*\}' >/dev/null; then
+ if printf "%s\n" "$response" | grep -E '^\{.*\}' >/dev/null; then
if _contains "$response" "\"domain\":\"$_domain\""; then
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
return 0
@@ -139,7 +144,7 @@ _vultr_rest() {
data="$3"
_debug "$ep"
- api_key_trimmed=$(echo $VULTR_API_KEY | tr -d '"')
+ api_key_trimmed=$(echo "$VULTR_API_KEY" | tr -d '"')
export _H1="Authorization: Bearer $api_key_trimmed"
export _H2='Content-Type: application/json'
diff --git a/dnsapi/dns_websupport.sh b/dnsapi/dns_websupport.sh
index e824c9c0..2374afc3 100644
--- a/dnsapi/dns_websupport.sh
+++ b/dnsapi/dns_websupport.sh
@@ -1,18 +1,16 @@
#!/usr/bin/env sh
-
-# Acme.sh DNS API wrapper for websupport.sk
-#
-# Original author: trgo.sk (https://github.com/trgosk)
-# Tweaks by: akulumbeg (https://github.com/akulumbeg)
-# Report Bugs here: https://github.com/akulumbeg/acme.sh
+# shellcheck disable=SC2034
+dns_websupport_info='Websupport.sk
+Site: Websupport.sk
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_websupport
+Options:
+ WS_ApiKey API Key. Called "Identifier" in the WS Admin
+ WS_ApiSecret API Secret. Called "Secret key" in the WS Admin
+Issues: github.com/acmesh-official/acme.sh/issues/3486
+Author: trgo.sk <@trgosk>, @akulumbeg
+'
# Requirements: API Key and Secret from https://admin.websupport.sk/en/auth/apiKey
-#
-# WS_ApiKey="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-# (called "Identifier" in the WS Admin)
-#
-# WS_ApiSecret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-# (called "Secret key" in the WS Admin)
WS_Api="https://rest.websupport.sk"
@@ -123,7 +121,7 @@ _get_root() {
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
@@ -137,7 +135,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *[^,]*" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
diff --git a/dnsapi/dns_west_cn.sh b/dnsapi/dns_west_cn.sh
new file mode 100644
index 00000000..b873bfc0
--- /dev/null
+++ b/dnsapi/dns_west_cn.sh
@@ -0,0 +1,109 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_west_cn_info='West.cn
+Site: West.cn
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_west_cn
+Options:
+ WEST_Username API username
+ WEST_Key API Key. Set at https://www.west.cn/manager/API/APIconfig.asp
+Issues: github.com/acmesh-official/acme.sh/issues/4894
+'
+
+REST_API="https://api.west.cn/API/v2"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_west_cn_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+ WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+ if [ -z "$WEST_Username" ] || [ -z "$WEST_Key" ]; then
+ WEST_Username=""
+ WEST_Key=""
+ _err "You don't specify west api key and username yet."
+ _err "Please set you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable WEST_Username "$WEST_Username"
+ _saveaccountconf_mutable WEST_Key "$WEST_Key"
+
+ add_record "$fulldomain" "$txtvalue"
+}
+
+#Usage: rm _acme-challenge.www.domain.com
+dns_west_cn_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+ WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+
+ if ! _rest POST "domain/dns/" "act=dnsrec.list&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT"; then
+ _err "dnsrec.list error."
+ return 1
+ fi
+
+ if _contains "$response" 'no records'; then
+ _info "Don't need to remove."
+ return 0
+ fi
+
+ record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"record_id"' | cut -d : -f 2 | cut -d ',' -f 1)
+ _debug record_id "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id."
+ return 1
+ fi
+
+ if ! _rest POST "domain/dns/" "act=dnsrec.remove&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_id=$record_id"; then
+ _err "dnsrec.remove error."
+ return 1
+ fi
+
+ _contains "$response" "success"
+}
+
+#add the txt record.
+#usage: add fulldomain txtvalue
+add_record() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Adding record"
+
+ if ! _rest POST "domain/dns/" "act=dnsrec.add&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT&record_value=$txtvalue"; then
+ return 1
+ fi
+
+ _contains "$response" "success"
+}
+
+#Usage: method URI data
+_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+ url="$REST_API/$ep"
+
+ _debug url "$url"
+
+ if [ "$m" = "GET" ]; then
+ response="$(_get "$url" | tr -d '\r')"
+ else
+ _debug2 data "$data"
+ response="$(_post "$data" "$url" | tr -d '\r')"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_world4you.sh b/dnsapi/dns_world4you.sh
index dfda4efd..dc295330 100644
--- a/dnsapi/dns_world4you.sh
+++ b/dnsapi/dns_world4you.sh
@@ -1,7 +1,14 @@
#!/usr/bin/env sh
-
-# World4You - www.world4you.com
-# Lorenz Stechauner, 2020 - https://www.github.com/NerLOR
+# shellcheck disable=SC2034
+dns_world4you_info='World4You.com
+Site: World4You.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_world4you
+Options:
+ WORLD4YOU_USERNAME Username
+ WORLD4YOU_PASSWORD Password
+Issues: github.com/acmesh-official/acme.sh/issues/3269
+Author: Lorenz Stechauner <@NerLOR>
+'
WORLD4YOU_API="https://my.world4you.com/en"
PAKETNR=''
@@ -108,7 +115,7 @@ dns_world4you_rm() {
_resethttp
export ACME_HTTP_NO_REDIRECTS=1
- body="DeleteDnsRecordForm[recordId]=$recordid&DeleteDnsRecordForm[uniqueFormIdDP]=$formiddp&DeleteDnsRecordForm[_token]=$form_token"
+ body="DeleteDnsRecordForm[id]=$recordid&DeleteDnsRecordForm[uniqueFormIdDP]=$formiddp&DeleteDnsRecordForm[_token]=$form_token"
_info "Removing record..."
ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns/record/delete" '' POST 'application/x-www-form-urlencoded')
_resethttp
@@ -195,7 +202,8 @@ _get_paketnr() {
fqdn="$1"
form="$2"
- domains=$(echo "$form" | grep '
|