PM Extra
3 years ago
34 changed files with 1531 additions and 565 deletions
-
2.github/FUNDING.yml
-
10.github/workflows/FreeBSD.yml
-
1.github/workflows/Linux.yml
-
10.github/workflows/MacOS.yml
-
10.github/workflows/Solaris.yml
-
10.github/workflows/Windows.yml
-
3Dockerfile
-
1README.md
-
312acme.sh
-
28deploy/mailcow.sh
-
108deploy/routeros.sh
-
7deploy/synology_dsm.sh
-
72deploy/truenas.sh
-
14dnsapi/dns_1984hosting.sh
-
2dnsapi/dns_aws.sh
-
12dnsapi/dns_cf.sh
-
159dnsapi/dns_curanet.sh
-
185dnsapi/dns_cx.sh
-
146dnsapi/dns_fornex.sh
-
177dnsapi/dns_gdnsdk.sh
-
232dnsapi/dns_geoscaling.sh
-
2dnsapi/dns_ispconfig.sh
-
84dnsapi/dns_loopia.sh
-
4dnsapi/dns_mydevil.sh
-
2dnsapi/dns_netlify.sh
-
3dnsapi/dns_opnsense.sh
-
2dnsapi/dns_ovh.sh
-
16dnsapi/dns_simply.sh
-
160dnsapi/dns_udr.sh
-
142dnsapi/dns_vercel.sh
-
30dnsapi/dns_world4you.sh
-
44notify/callmebotWhatsApp.sh
-
57notify/discord.sh
-
49notify/weixin_work.sh
@ -0,0 +1,159 @@ |
|||||
|
#!/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 <peter@r12.dk> |
||||
|
#Version 1.0 |
||||
|
|
||||
|
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" |
||||
|
CURANET_ACCESS_TOKEN="" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: dns_curanet_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_curanet_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_info "Using curanet" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}" |
||||
|
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}" |
||||
|
if [ -z "$CURANET_AUTHCLIENTID" ] || [ -z "$CURANET_AUTHSECRET" ]; then |
||||
|
CURANET_AUTHCLIENTID="" |
||||
|
CURANET_AUTHSECRET="" |
||||
|
_err "You don't specify curanet api client and secret." |
||||
|
_err "Please create your auth info and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the credentials to the account conf file. |
||||
|
_saveaccountconf_mutable CURANET_AUTHCLIENTID "$CURANET_AUTHCLIENTID" |
||||
|
_saveaccountconf_mutable CURANET_AUTHSECRET "$CURANET_AUTHSECRET" |
||||
|
|
||||
|
if ! _get_token; then |
||||
|
_err "Unable to get token" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
export _H1="Content-Type: application/json-patch+json" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN" |
||||
|
data="{\"name\": \"$fulldomain\",\"type\": \"TXT\",\"ttl\": 60,\"priority\": 0,\"data\": \"$txtvalue\"}" |
||||
|
response="$(_post "$data" "$CURANET_REST_URL/${_domain}/Records" "" "")" |
||||
|
|
||||
|
if _contains "$response" "$txtvalue"; then |
||||
|
_debug "TXT record added OK" |
||||
|
else |
||||
|
_err "Unable to add TXT record" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#Usage: fulldomain txtvalue |
||||
|
#Remove the txt record after validation. |
||||
|
dns_curanet_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_info "Using curanet" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}" |
||||
|
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}" |
||||
|
|
||||
|
if ! _get_token; then |
||||
|
_err "Unable to get token" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Getting current record list to identify TXT to delete" |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN" |
||||
|
|
||||
|
response="$(_get "$CURANET_REST_URL/${_domain}/Records" "" "")" |
||||
|
|
||||
|
if ! _contains "$response" "$txtvalue"; then |
||||
|
_err "Unable to delete record (does not contain $txtvalue )" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
recordid=$(echo "$response" | _egrep_o "{\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" | _egrep_o "id\":[0-9]+" | cut -c 5-) |
||||
|
|
||||
|
if [ -z "$recordid" ]; then |
||||
|
_err "Unable to get recordid" |
||||
|
_debug "regex {\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" |
||||
|
_debug "response $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Deleting recordID $recordid" |
||||
|
response="$(_post "" "$CURANET_REST_URL/${_domain}/Records/$recordid" "" "DELETE")" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_get_token() { |
||||
|
response="$(_post "grant_type=client_credentials&client_id=$CURANET_AUTHCLIENTID&client_secret=$CURANET_AUTHSECRET&scope=dns" "$CURANET_AUTH_URL" "" "")" |
||||
|
if ! _contains "$response" "access_token"; then |
||||
|
_err "Unable get access token" |
||||
|
return 1 |
||||
|
fi |
||||
|
CURANET_ACCESS_TOKEN=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]+" | cut -c 17-) |
||||
|
|
||||
|
if [ -z "$CURANET_ACCESS_TOKEN" ]; then |
||||
|
_err "Unable to get token" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _domain=domain.com |
||||
|
# _domain_id=sdjkglgdfewsdfg |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=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 |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Accept: application/json" |
||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN" |
||||
|
response="$(_get "$CURANET_REST_URL/$h/Records" "" "")" |
||||
|
|
||||
|
if [ ! "$(echo "$response" | _egrep_o "Entity not found")" ]; then |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
@ -1,185 +0,0 @@ |
|||||
#!/usr/bin/env sh |
|
||||
|
|
||||
# CloudXNS Domain api |
|
||||
# |
|
||||
#CX_Key="1234" |
|
||||
# |
|
||||
#CX_Secret="sADDsdasdgdsf" |
|
||||
|
|
||||
CX_Api="https://www.cloudxns.net/api2" |
|
||||
|
|
||||
#REST_API |
|
||||
######## Public functions ##################### |
|
||||
|
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|
||||
dns_cx_add() { |
|
||||
fulldomain=$1 |
|
||||
txtvalue=$2 |
|
||||
|
|
||||
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}" |
|
||||
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}" |
|
||||
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then |
|
||||
CX_Key="" |
|
||||
CX_Secret="" |
|
||||
_err "You don't specify cloudxns.net api key or secret yet." |
|
||||
_err "Please create you key and try again." |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
REST_API="$CX_Api" |
|
||||
|
|
||||
#save the api key and email to the account conf file. |
|
||||
_saveaccountconf_mutable CX_Key "$CX_Key" |
|
||||
_saveaccountconf_mutable CX_Secret "$CX_Secret" |
|
||||
|
|
||||
_debug "First detect the root zone" |
|
||||
if ! _get_root "$fulldomain"; then |
|
||||
_err "invalid domain" |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
add_record "$_domain" "$_sub_domain" "$txtvalue" |
|
||||
} |
|
||||
|
|
||||
#fulldomain txtvalue |
|
||||
dns_cx_rm() { |
|
||||
fulldomain=$1 |
|
||||
txtvalue=$2 |
|
||||
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}" |
|
||||
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}" |
|
||||
REST_API="$CX_Api" |
|
||||
if _get_root "$fulldomain"; then |
|
||||
record_id="" |
|
||||
existing_records "$_domain" "$_sub_domain" "$txtvalue" |
|
||||
if [ "$record_id" ]; then |
|
||||
_rest DELETE "record/$record_id/$_domain_id" "{}" |
|
||||
_info "Deleted record ${fulldomain}" |
|
||||
fi |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
#usage: root sub |
|
||||
#return if the sub record already exists. |
|
||||
#echos the existing records count. |
|
||||
# '0' means doesn't exist |
|
||||
existing_records() { |
|
||||
_debug "Getting txt records" |
|
||||
root=$1 |
|
||||
sub=$2 |
|
||||
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}') |
|
||||
_debug seg "$seg" |
|
||||
if [ -z "$seg" ]; then |
|
||||
return 0 |
|
||||
fi |
|
||||
|
|
||||
if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then |
|
||||
record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1) |
|
||||
_debug record_id "$record_id" |
|
||||
return 0 |
|
||||
fi |
|
||||
|
|
||||
} |
|
||||
|
|
||||
#add the txt record. |
|
||||
#usage: root sub txtvalue |
|
||||
add_record() { |
|
||||
root=$1 |
|
||||
sub=$2 |
|
||||
txtvalue=$3 |
|
||||
fulldomain="$sub.$root" |
|
||||
|
|
||||
_info "Adding record" |
|
||||
|
|
||||
if ! _rest POST "record" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then |
|
||||
return 1 |
|
||||
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 |
|
||||
i=2 |
|
||||
p=1 |
|
||||
|
|
||||
if ! _rest GET "domain"; then |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
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 _contains "$response" "$h."; then |
|
||||
seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}') |
|
||||
_debug seg "$seg" |
|
||||
_domain_id=$(printf "%s\n" "$seg" | _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) |
|
||||
_debug _sub_domain "$_sub_domain" |
|
||||
_domain="$h" |
|
||||
_debug _domain "$_domain" |
|
||||
return 0 |
|
||||
fi |
|
||||
return 1 |
|
||||
fi |
|
||||
p="$i" |
|
||||
i=$(_math "$i" + 1) |
|
||||
done |
|
||||
return 1 |
|
||||
} |
|
||||
|
|
||||
#Usage: method URI data |
|
||||
_rest() { |
|
||||
m=$1 |
|
||||
ep="$2" |
|
||||
_debug ep "$ep" |
|
||||
url="$REST_API/$ep" |
|
||||
_debug url "$url" |
|
||||
|
|
||||
cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC") |
|
||||
_debug cdate "$cdate" |
|
||||
|
|
||||
data="$3" |
|
||||
_debug data "$data" |
|
||||
|
|
||||
sec="$CX_Key$url$data$cdate$CX_Secret" |
|
||||
_debug sec "$sec" |
|
||||
hmac=$(printf "%s" "$sec" | _digest md5 hex) |
|
||||
_debug hmac "$hmac" |
|
||||
|
|
||||
export _H1="API-KEY: $CX_Key" |
|
||||
export _H2="API-REQUEST-DATE: $cdate" |
|
||||
export _H3="API-HMAC: $hmac" |
|
||||
export _H4="Content-Type: application/json" |
|
||||
|
|
||||
if [ "$data" ]; then |
|
||||
response="$(_post "$data" "$url" "" "$m")" |
|
||||
else |
|
||||
response="$(_get "$url")" |
|
||||
fi |
|
||||
|
|
||||
if [ "$?" != "0" ]; then |
|
||||
_err "error $ep" |
|
||||
return 1 |
|
||||
fi |
|
||||
_debug2 response "$response" |
|
||||
|
|
||||
_contains "$response" '"code":1' |
|
||||
|
|
||||
} |
|
@ -0,0 +1,146 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
#Author: Timur Umarov <inbox@tumarov.com> |
||||
|
|
||||
|
FORNEX_API_URL="https://fornex.com/api/dns/v0.1" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: dns_fornex_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_fornex_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _Fornex_API; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Unable to determine root domain" |
||||
|
return 1 |
||||
|
else |
||||
|
_debug _domain "$_domain" |
||||
|
fi |
||||
|
|
||||
|
_info "Adding record" |
||||
|
if _rest POST "$_domain/entry_set/add/" "host=$fulldomain&type=TXT&value=$txtvalue&apikey=$FORNEX_API_KEY"; then |
||||
|
_debug _response "$response" |
||||
|
if _contains "$response" '"ok": true' || _contains "$response" 'Такая запись уже существует.'; then |
||||
|
_info "Added, OK" |
||||
|
return 0 |
||||
|
fi |
||||
|
fi |
||||
|
_err "Add txt record error." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#Usage: dns_fornex_rm _acme-challenge.www.domain.com |
||||
|
dns_fornex_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _Fornex_API; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "Unable to determine root domain" |
||||
|
return 1 |
||||
|
else |
||||
|
_debug _domain "$_domain" |
||||
|
fi |
||||
|
|
||||
|
_debug "Getting txt records" |
||||
|
_rest GET "$_domain/entry_set.json?apikey=$FORNEX_API_KEY" |
||||
|
|
||||
|
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')" |
||||
|
_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 |
||||
|
_err "Delete record error." |
||||
|
return 1 |
||||
|
fi |
||||
|
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 |
||||
|
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 |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"$h\"" >/dev/null; then |
||||
|
_domain=$h |
||||
|
return 0 |
||||
|
else |
||||
|
_debug "$h not found" |
||||
|
fi |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
|
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_Fornex_API() { |
||||
|
FORNEX_API_KEY="${FORNEX_API_KEY:-$(_readaccountconf_mutable FORNEX_API_KEY)}" |
||||
|
if [ -z "$FORNEX_API_KEY" ]; then |
||||
|
FORNEX_API_KEY="" |
||||
|
|
||||
|
_err "You didn't specify the Fornex API key yet." |
||||
|
_err "Please create your key and try again." |
||||
|
|
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable FORNEX_API_KEY "$FORNEX_API_KEY" |
||||
|
} |
||||
|
|
||||
|
#method method action data |
||||
|
_rest() { |
||||
|
m=$1 |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
_debug "$ep" |
||||
|
|
||||
|
export _H1="Accept: application/json" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_debug data "$data" |
||||
|
response="$(_post "$data" "$FORNEX_API_URL/$ep" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$FORNEX_API_URL/$ep" | _normalizeJson)" |
||||
|
fi |
||||
|
|
||||
|
_ret="$?" |
||||
|
if [ "$_ret" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
@ -1,177 +0,0 @@ |
|||||
#!/usr/bin/env sh |
|
||||
#Author: Herman Sletteng |
|
||||
#Report Bugs here: https://github.com/loial/acme.sh |
|
||||
# |
|
||||
# |
|
||||
# Note, gratisdns requires a login first, so the script needs to handle |
|
||||
# temporary cookies. Since acme.sh _get/_post currently don't directly support |
|
||||
# cookies, I've defined wrapper functions _myget/_mypost to set the headers |
|
||||
|
|
||||
GDNSDK_API="https://admin.gratisdns.com" |
|
||||
######## Public functions ##################### |
|
||||
#Usage: dns_gdnsdk_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|
||||
dns_gdnsdk_add() { |
|
||||
fulldomain=$1 |
|
||||
txtvalue=$2 |
|
||||
_info "Using gratisdns.dk" |
|
||||
_debug fulldomain "$fulldomain" |
|
||||
_debug txtvalue "$txtvalue" |
|
||||
if ! _gratisdns_login; then |
|
||||
_err "Login failed!" |
|
||||
return 1 |
|
||||
fi |
|
||||
#finding domain zone |
|
||||
if ! _get_domain; then |
|
||||
_err "No matching root domain for $fulldomain found" |
|
||||
return 1 |
|
||||
fi |
|
||||
# adding entry |
|
||||
_info "Adding the entry" |
|
||||
_mypost "action=dns_primary_record_added_txt&user_domain=$_domain&name=$fulldomain&txtdata=$txtvalue&ttl=1" |
|
||||
if _successful_update; then return 0; fi |
|
||||
_err "Couldn't create entry!" |
|
||||
return 1 |
|
||||
} |
|
||||
|
|
||||
#Usage: fulldomain txtvalue |
|
||||
#Remove the txt record after validation. |
|
||||
dns_gdnsdk_rm() { |
|
||||
fulldomain=$1 |
|
||||
txtvalue=$2 |
|
||||
_info "Using gratisdns.dk" |
|
||||
_debug fulldomain "$fulldomain" |
|
||||
_debug txtvalue "$txtvalue" |
|
||||
if ! _gratisdns_login; then |
|
||||
_err "Login failed!" |
|
||||
return 1 |
|
||||
fi |
|
||||
if ! _get_domain; then |
|
||||
_err "No matching root domain for $fulldomain found" |
|
||||
return 1 |
|
||||
fi |
|
||||
_findentry "$fulldomain" "$txtvalue" |
|
||||
if [ -z "$_id" ]; then |
|
||||
_info "Entry doesn't exist, nothing to delete" |
|
||||
return 0 |
|
||||
fi |
|
||||
_debug "Deleting record..." |
|
||||
_mypost "action=dns_primary_delete_txt&user_domain=$_domain&id=$_id" |
|
||||
# removing entry |
|
||||
|
|
||||
if _successful_update; then return 0; fi |
|
||||
_err "Couldn't delete entry!" |
|
||||
return 1 |
|
||||
} |
|
||||
|
|
||||
#################### Private functions below ################################## |
|
||||
|
|
||||
_checkcredentials() { |
|
||||
GDNSDK_Username="${GDNSDK_Username:-$(_readaccountconf_mutable GDNSDK_Username)}" |
|
||||
GDNSDK_Password="${GDNSDK_Password:-$(_readaccountconf_mutable GDNSDK_Password)}" |
|
||||
|
|
||||
if [ -z "$GDNSDK_Username" ] || [ -z "$GDNSDK_Password" ]; then |
|
||||
GDNSDK_Username="" |
|
||||
GDNSDK_Password="" |
|
||||
_err "You haven't specified gratisdns.dk username and password yet." |
|
||||
_err "Please add credentials and try again." |
|
||||
return 1 |
|
||||
fi |
|
||||
#save the credentials to the account conf file. |
|
||||
_saveaccountconf_mutable GDNSDK_Username "$GDNSDK_Username" |
|
||||
_saveaccountconf_mutable GDNSDK_Password "$GDNSDK_Password" |
|
||||
return 0 |
|
||||
} |
|
||||
|
|
||||
_checkcookie() { |
|
||||
GDNSDK_Cookie="${GDNSDK_Cookie:-$(_readaccountconf_mutable GDNSDK_Cookie)}" |
|
||||
if [ -z "$GDNSDK_Cookie" ]; then |
|
||||
_debug "No cached cookie found" |
|
||||
return 1 |
|
||||
fi |
|
||||
_myget "action=" |
|
||||
if (echo "$_result" | grep -q "logmeout"); then |
|
||||
_debug "Cached cookie still valid" |
|
||||
return 0 |
|
||||
fi |
|
||||
_debug "Cached cookie no longer valid" |
|
||||
GDNSDK_Cookie="" |
|
||||
_saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie" |
|
||||
return 1 |
|
||||
} |
|
||||
|
|
||||
_gratisdns_login() { |
|
||||
if ! _checkcredentials; then return 1; fi |
|
||||
|
|
||||
if _checkcookie; then |
|
||||
_debug "Already logged in" |
|
||||
return 0 |
|
||||
fi |
|
||||
_debug "Logging into GratisDNS with user $GDNSDK_Username" |
|
||||
|
|
||||
if ! _mypost "login=$GDNSDK_Username&password=$GDNSDK_Password&action=logmein"; then |
|
||||
_err "GratisDNS login failed for user $GDNSDK_Username bad RC from _post" |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
GDNSDK_Cookie="$(grep -A 15 '302 Found' "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]*' | _head_n 1 | cut -d ' ' -f2)" |
|
||||
|
|
||||
if [ -z "$GDNSDK_Cookie" ]; then |
|
||||
_err "GratisDNS login failed for user $GDNSDK_Username. Check $HTTP_HEADER file" |
|
||||
return 1 |
|
||||
fi |
|
||||
export GDNSDK_Cookie |
|
||||
_saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie" |
|
||||
return 0 |
|
||||
} |
|
||||
|
|
||||
_myget() { |
|
||||
#Adds cookie to request |
|
||||
export _H1="Cookie: $GDNSDK_Cookie" |
|
||||
_result=$(_get "$GDNSDK_API?$1") |
|
||||
} |
|
||||
_mypost() { |
|
||||
#Adds cookie to request |
|
||||
export _H1="Cookie: $GDNSDK_Cookie" |
|
||||
_result=$(_post "$1" "$GDNSDK_API") |
|
||||
} |
|
||||
|
|
||||
_get_domain() { |
|
||||
_myget 'action=dns_primarydns' |
|
||||
_domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:]._-]+' | sed 's/^.*"//') |
|
||||
if [ -z "$_domains" ]; then |
|
||||
_err "Primary domain list not found!" |
|
||||
return 1 |
|
||||
fi |
|
||||
for _domain in $_domains; do |
|
||||
if (_endswith "$fulldomain" "$_domain"); then |
|
||||
_debug "Root domain: $_domain" |
|
||||
return 0 |
|
||||
fi |
|
||||
done |
|
||||
return 1 |
|
||||
} |
|
||||
|
|
||||
_successful_update() { |
|
||||
if (echo "$_result" | grep -q 'table-success'); then return 0; fi |
|
||||
return 1 |
|
||||
} |
|
||||
|
|
||||
_findentry() { |
|
||||
#args $1: fulldomain, $2: txtvalue |
|
||||
#returns id of dns entry, if it exists |
|
||||
_myget "action=dns_primary_changeDNSsetup&user_domain=$_domain" |
|
||||
_debug3 "_result: $_result" |
|
||||
|
|
||||
_tmp_result=$(echo "$_result" | tr -d '\n\r' | _egrep_o "<td>$1</td>\s*<td>$2</td>[^?]*[^&]*&id=[^&]*") |
|
||||
_debug _tmp_result "$_tmp_result" |
|
||||
if [ -z "${_tmp_result:-}" ]; then |
|
||||
_debug "The variable is _tmp_result is not supposed to be empty, there may be something wrong with the script" |
|
||||
fi |
|
||||
|
|
||||
_id=$(echo "$_tmp_result" | sed 's/^.*=//') |
|
||||
if [ -n "$_id" ]; then |
|
||||
_debug "Entry found with _id=$_id" |
|
||||
return 0 |
|
||||
fi |
|
||||
return 1 |
|
||||
} |
|
@ -0,0 +1,232 @@ |
|||||
|
#!/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) |
||||
|
|
||||
|
#-- dns_geoscaling_add() - Add TXT record -------------------------------------- |
||||
|
# Usage: dns_geoscaling_add _acme-challenge.subdomain.domain.com "XyZ123..." |
||||
|
|
||||
|
dns_geoscaling_add() { |
||||
|
full_domain=$1 |
||||
|
txt_value=$2 |
||||
|
_info "Using DNS-01 Geoscaling DNS2 hook" |
||||
|
|
||||
|
GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}" |
||||
|
GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}" |
||||
|
if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then |
||||
|
GEOSCALING_Username= |
||||
|
GEOSCALING_Password= |
||||
|
_err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables." |
||||
|
return 1 |
||||
|
fi |
||||
|
_saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}" |
||||
|
_saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}" |
||||
|
|
||||
|
# Fills in the $zone_id and $zone_name |
||||
|
find_zone "${full_domain}" || return 1 |
||||
|
_debug "Zone id '${zone_id}' will be used." |
||||
|
|
||||
|
# We're logged in here |
||||
|
|
||||
|
# we should add ${full_domain} minus the trailing ${zone_name} |
||||
|
|
||||
|
prefix=$(echo "${full_domain}" | sed "s|\\.${zone_name}\$||") |
||||
|
|
||||
|
body="id=${zone_id}&name=${prefix}&type=TXT&content=${txt_value}&ttl=300&prio=0" |
||||
|
|
||||
|
do_post "$body" "https://www.geoscaling.com/dns2/ajax/add_record.php" |
||||
|
exit_code="$?" |
||||
|
if [ "${exit_code}" -eq 0 ]; then |
||||
|
_info "TXT record added successfully." |
||||
|
else |
||||
|
_err "Couldn't add the TXT record." |
||||
|
fi |
||||
|
do_logout |
||||
|
return "${exit_code}" |
||||
|
} |
||||
|
|
||||
|
#-- dns_geoscaling_rm() - Remove TXT record ------------------------------------ |
||||
|
# Usage: dns_geoscaling_rm _acme-challenge.subdomain.domain.com "XyZ123..." |
||||
|
|
||||
|
dns_geoscaling_rm() { |
||||
|
full_domain=$1 |
||||
|
txt_value=$2 |
||||
|
_info "Cleaning up after DNS-01 Geoscaling DNS2 hook" |
||||
|
|
||||
|
GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}" |
||||
|
GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}" |
||||
|
if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then |
||||
|
GEOSCALING_Username= |
||||
|
GEOSCALING_Password= |
||||
|
_err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables." |
||||
|
return 1 |
||||
|
fi |
||||
|
_saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}" |
||||
|
_saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}" |
||||
|
|
||||
|
# fills in the $zone_id |
||||
|
find_zone "${full_domain}" || return 1 |
||||
|
_debug "Zone id '${zone_id}' will be used." |
||||
|
|
||||
|
# Here we're logged in |
||||
|
# Find the record id to clean |
||||
|
|
||||
|
# get the domain |
||||
|
response=$(do_get "https://www.geoscaling.com/dns2/index.php?module=domain&id=${zone_id}") |
||||
|
_debug2 "response" "$response" |
||||
|
|
||||
|
table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Basic Records</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')" |
||||
|
_debug2 table "${table}" |
||||
|
names=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|</td>||; s|.*>||') |
||||
|
ids=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|\.name">.*||; s|id="||') |
||||
|
types=$(echo "${table}" | _egrep_o 'id="[0-9]+\.type">[^<]*</td>' | sed 's|</td>||; s|.*>||') |
||||
|
values=$(echo "${table}" | _egrep_o 'id="[0-9]+\.content">[^<]*</td>' | sed 's|</td>||; s|.*>||') |
||||
|
|
||||
|
_debug2 names "${names}" |
||||
|
_debug2 ids "${ids}" |
||||
|
_debug2 types "${types}" |
||||
|
_debug2 values "${values}" |
||||
|
|
||||
|
# look for line whose name is ${full_domain}, whose type is TXT, and whose value is ${txt_value} |
||||
|
line_num="$(echo "${values}" | grep -F -n -- "${txt_value}" | _head_n 1 | cut -d ':' -f 1)" |
||||
|
_debug2 line_num "${line_num}" |
||||
|
found_id= |
||||
|
if [ -n "$line_num" ]; then |
||||
|
type=$(echo "${types}" | sed -n "${line_num}p") |
||||
|
name=$(echo "${names}" | sed -n "${line_num}p") |
||||
|
id=$(echo "${ids}" | sed -n "${line_num}p") |
||||
|
|
||||
|
_debug2 type "$type" |
||||
|
_debug2 name "$name" |
||||
|
_debug2 id "$id" |
||||
|
_debug2 full_domain "$full_domain" |
||||
|
|
||||
|
if [ "${type}" = "TXT" ] && [ "${name}" = "${full_domain}" ]; then |
||||
|
found_id=${id} |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
if [ "${found_id}" = "" ]; then |
||||
|
_err "Can not find record id." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
# Remove the record |
||||
|
body="id=${zone_id}&record_id=${found_id}" |
||||
|
response=$(do_post "$body" "https://www.geoscaling.com/dns2/ajax/delete_record.php") |
||||
|
exit_code="$?" |
||||
|
if [ "$exit_code" -eq 0 ]; then |
||||
|
_info "Record removed successfully." |
||||
|
else |
||||
|
_err "Could not clean (remove) up the record. Please go to Geoscaling administration interface and clean it by hand." |
||||
|
fi |
||||
|
do_logout |
||||
|
return "${exit_code}" |
||||
|
} |
||||
|
|
||||
|
########################## PRIVATE FUNCTIONS ########################### |
||||
|
|
||||
|
do_get() { |
||||
|
_url=$1 |
||||
|
export _H1="Cookie: $geoscaling_phpsessid_cookie" |
||||
|
_get "${_url}" |
||||
|
} |
||||
|
|
||||
|
do_post() { |
||||
|
_body=$1 |
||||
|
_url=$2 |
||||
|
export _H1="Cookie: $geoscaling_phpsessid_cookie" |
||||
|
_post "${_body}" "${_url}" |
||||
|
} |
||||
|
|
||||
|
do_login() { |
||||
|
|
||||
|
_info "Logging in..." |
||||
|
|
||||
|
username_encoded="$(printf "%s" "${GEOSCALING_Username}" | _url_encode)" |
||||
|
password_encoded="$(printf "%s" "${GEOSCALING_Password}" | _url_encode)" |
||||
|
body="username=${username_encoded}&password=${password_encoded}" |
||||
|
|
||||
|
response=$(_post "$body" "https://www.geoscaling.com/dns2/index.php?module=auth") |
||||
|
_debug2 response "${response}" |
||||
|
|
||||
|
#retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | _egrep_o '[0-9]+$') |
||||
|
retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | cut -d ' ' -f 2) |
||||
|
|
||||
|
if [ "$retcode" != "302" ]; then |
||||
|
_err "Geoscaling login failed for user ${GEOSCALING_Username}. Check ${HTTP_HEADER} file" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
geoscaling_phpsessid_cookie="$(grep -i '^set-cookie:' "${HTTP_HEADER}" | _egrep_o 'PHPSESSID=[^;]*;' | tr -d ';')" |
||||
|
return 0 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
do_logout() { |
||||
|
_info "Logging out." |
||||
|
response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=auth")" |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
find_zone() { |
||||
|
domain="$1" |
||||
|
|
||||
|
# do login |
||||
|
do_login || return 1 |
||||
|
|
||||
|
# get zones |
||||
|
response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=domains")" |
||||
|
|
||||
|
table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Your domains</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')" |
||||
|
_debug2 table "${table}" |
||||
|
zone_names="$(echo "${table}" | _egrep_o '<b>[^<]*</b>' | sed 's|<b>||;s|</b>||')" |
||||
|
_debug2 _matches "${zone_names}" |
||||
|
# Zone names and zone IDs are in same order |
||||
|
zone_ids=$(echo "${table}" | _egrep_o '<a href=.index\.php\?module=domain&id=[0-9]+. onclick="javascript:show_loader\(\);">' | sed 's|.*id=||;s|. .*||') |
||||
|
|
||||
|
_debug2 "These are the zones on this Geoscaling account:" |
||||
|
_debug2 "zone_names" "${zone_names}" |
||||
|
_debug2 "And these are their respective IDs:" |
||||
|
_debug2 "zone_ids" "${zone_ids}" |
||||
|
if [ -z "${zone_names}" ] || [ -z "${zone_ids}" ]; then |
||||
|
_err "Can not get zone names or IDs." |
||||
|
return 1 |
||||
|
fi |
||||
|
# Walk through all possible zone names |
||||
|
strip_counter=1 |
||||
|
while true; do |
||||
|
attempted_zone=$(echo "${domain}" | cut -d . -f ${strip_counter}-) |
||||
|
|
||||
|
# All possible zone names have been tried |
||||
|
if [ -z "${attempted_zone}" ]; then |
||||
|
_err "No zone for domain '${domain}' found." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "Looking for zone '${attempted_zone}'" |
||||
|
|
||||
|
line_num="$(echo "${zone_names}" | grep -n "^${attempted_zone}\$" | _head_n 1 | cut -d : -f 1)" |
||||
|
_debug2 line_num "${line_num}" |
||||
|
if [ "$line_num" ]; then |
||||
|
zone_id=$(echo "${zone_ids}" | sed -n "${line_num}p") |
||||
|
zone_name=$(echo "${zone_names}" | sed -n "${line_num}p") |
||||
|
if [ -z "${zone_id}" ]; then |
||||
|
_err "Can not find zone id." |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug "Found relevant zone '${attempted_zone}' with id '${zone_id}' - will be used for domain '${domain}'." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
_debug "Zone '${attempted_zone}' doesn't exist, let's try a less specific zone." |
||||
|
strip_counter=$(_math "${strip_counter}" + 1) |
||||
|
done |
||||
|
} |
||||
|
# vim: et:ts=2:sw=2: |
@ -0,0 +1,160 @@ |
|||||
|
#!/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" |
||||
|
# |
||||
|
|
||||
|
UDR_API="https://api.domainreselling.de/api/call.cgi" |
||||
|
UDR_TTL="30" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt" |
||||
|
dns_udr_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}" |
||||
|
UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}" |
||||
|
if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then |
||||
|
UDR_USER="" |
||||
|
UDR_PASS="" |
||||
|
_err "You didn't specify an UD-Reselling username and password yet" |
||||
|
return 1 |
||||
|
fi |
||||
|
# save the username and password to the account conf file. |
||||
|
_saveaccountconf_mutable UDR_USER "$UDR_USER" |
||||
|
_saveaccountconf_mutable UDR_PASS "$UDR_PASS" |
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _dnszone "${_dnszone}" |
||||
|
|
||||
|
_debug "Getting txt records" |
||||
|
if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}" |
||||
|
_debug resource_record "${rr}" |
||||
|
if _contains "$response" "$rr" >/dev/null; then |
||||
|
_err "Error, it would appear that this record already exists. Please review existing TXT records for this domain." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_info "Adding record" |
||||
|
if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&addrr0=${rr}"; then |
||||
|
_err "Adding the record did not succeed, please verify/check." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_info "Added, OK" |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
dns_udr_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}" |
||||
|
UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}" |
||||
|
if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then |
||||
|
UDR_USER="" |
||||
|
UDR_PASS="" |
||||
|
_err "You didn't specify an UD-Reselling username and password yet" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug _dnszone "${_dnszone}" |
||||
|
|
||||
|
_debug "Getting txt records" |
||||
|
if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}" |
||||
|
_debug resource_record "${rr}" |
||||
|
if _contains "$response" "$rr" >/dev/null; then |
||||
|
if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&delrr0=${rr}"; then |
||||
|
_err "Deleting the record did not succeed, please verify/check." |
||||
|
return 1 |
||||
|
fi |
||||
|
_info "Removed, OK" |
||||
|
return 0 |
||||
|
else |
||||
|
_info "Text record is not present, will not delete anything." |
||||
|
return 0 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=1 |
||||
|
|
||||
|
if ! _udr_rest "QueryDNSZoneList" ""; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
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 _contains "${response}" "${h}." >/dev/null; then |
||||
|
_dnszone=$(echo "$response" | _egrep_o "${h}") |
||||
|
if [ "$_dnszone" ]; then |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
i=$(_math "$i" + 1) |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_udr_rest() { |
||||
|
if [ -n "$2" ]; then |
||||
|
data="command=$1&$2" |
||||
|
else |
||||
|
data="command=$1" |
||||
|
fi |
||||
|
|
||||
|
_debug data "${data}" |
||||
|
response="$(_post "${data}" "${UDR_API}?s_login=${UDR_USER}&s_pw=${UDR_PASS}" "" "POST")" |
||||
|
|
||||
|
_code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') |
||||
|
_description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') |
||||
|
|
||||
|
_debug response_code "$_code" |
||||
|
_debug response_description "$_description" |
||||
|
|
||||
|
if [ ! "$_code" = "200" ]; then |
||||
|
_err "DNS-API-Error: $_description" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
@ -0,0 +1,142 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# 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" |
||||
|
|
||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_vercel_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
VERCEL_TOKEN="${VERCEL_TOKEN:-$(_readaccountconf_mutable VERCEL_TOKEN)}" |
||||
|
|
||||
|
if [ -z "$VERCEL_TOKEN" ]; then |
||||
|
VERCEL_TOKEN="" |
||||
|
_err "You have not set the Vercel API token yet." |
||||
|
_err "Please visit https://vercel.com/account/tokens to generate it." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_saveaccountconf_mutable VERCEL_TOKEN "$VERCEL_TOKEN" |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_info "Adding record" |
||||
|
if _vercel_rest POST "v2/domains/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"; then |
||||
|
if printf -- "%s" "$response" | grep "\"uid\":\"" >/dev/null; then |
||||
|
_info "Added" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Unexpected response while adding text record." |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
_err "Add txt record error." |
||||
|
} |
||||
|
|
||||
|
dns_vercel_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_vercel_rest GET "v2/domains/$_domain/records" |
||||
|
|
||||
|
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ") |
||||
|
|
||||
|
if [ "$count" = "0" ]; then |
||||
|
_info "Don't need to remove." |
||||
|
else |
||||
|
_record_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"slug\":\"[^,]*\",\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\",\"value\":\"$txtvalue\"" | cut -d: -f2 | cut -d, -f1 | tr -d '"') |
||||
|
|
||||
|
if [ "$_record_id" ]; then |
||||
|
echo "$_record_id" | while read -r item; do |
||||
|
if _vercel_rest DELETE "v2/domains/$_domain/records/$item"; then |
||||
|
_info "removed record" "$item" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "failed to remove record" "$item" |
||||
|
return 1 |
||||
|
fi |
||||
|
done |
||||
|
fi |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain="$1" |
||||
|
ep="$2" |
||||
|
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 |
||||
|
|
||||
|
if ! _vercel_rest GET "v4/domains/$h"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; 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 |
||||
|
} |
||||
|
|
||||
|
_vercel_rest() { |
||||
|
m="$1" |
||||
|
ep="$2" |
||||
|
data="$3" |
||||
|
|
||||
|
path="$VERCEL_API/$ep" |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
export _H2="Authorization: Bearer $VERCEL_TOKEN" |
||||
|
|
||||
|
if [ "$m" != "GET" ]; then |
||||
|
_secure_debug2 data "$data" |
||||
|
response="$(_post "$data" "$path" "" "$m")" |
||||
|
else |
||||
|
response="$(_get "$path")" |
||||
|
fi |
||||
|
_ret="$?" |
||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" |
||||
|
_debug "http response code $_code" |
||||
|
_secure_debug2 response "$response" |
||||
|
if [ "$_ret" != "0" ]; then |
||||
|
_err "error $ep" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
response="$(printf "%s" "$response" | _normalizeJson)" |
||||
|
return 0 |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
#Support CallMeBot Whatsapp webhooks |
||||
|
|
||||
|
#CALLMEBOT_YOUR_PHONE_NO="" |
||||
|
#CALLMEBOT_API_KEY="" |
||||
|
|
||||
|
callmebotWhatsApp_send() { |
||||
|
_subject="$1" |
||||
|
_content="$2" |
||||
|
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped |
||||
|
_debug "_statusCode" "$_statusCode" |
||||
|
|
||||
|
CALLMEBOT_YOUR_PHONE_NO="${CALLMEBOT_YOUR_PHONE_NO:-$(_readaccountconf_mutable CALLMEBOT_YOUR_PHONE_NO)}" |
||||
|
if [ -z "$CALLMEBOT_YOUR_PHONE_NO" ]; then |
||||
|
CALLMEBOT_YOUR_PHONE_NO="" |
||||
|
_err "You didn't specify a Slack webhook url CALLMEBOT_YOUR_PHONE_NO yet." |
||||
|
return 1 |
||||
|
fi |
||||
|
_saveaccountconf_mutable CALLMEBOT_YOUR_PHONE_NO "$CALLMEBOT_YOUR_PHONE_NO" |
||||
|
|
||||
|
CALLMEBOT_API_KEY="${CALLMEBOT_API_KEY:-$(_readaccountconf_mutable CALLMEBOT_API_KEY)}" |
||||
|
if [ "$CALLMEBOT_API_KEY" ]; then |
||||
|
_saveaccountconf_mutable CALLMEBOT_API_KEY "$CALLMEBOT_API_KEY" |
||||
|
fi |
||||
|
|
||||
|
_waUrl="https://api.callmebot.com/whatsapp.php" |
||||
|
|
||||
|
_Phone_No="$(printf "%s" "$CALLMEBOT_YOUR_PHONE_NO" | _url_encode)" |
||||
|
_apikey="$(printf "%s" "$CALLMEBOT_API_KEY" | _url_encode)" |
||||
|
_message="$(printf "*%s*\\n%s" "$_subject" "$_content" | _url_encode)" |
||||
|
|
||||
|
_finalUrl="$_waUrl?phone=$_Phone_No&apikey=$_apikey&text=$_message" |
||||
|
response="$(_get "$_finalUrl")" |
||||
|
|
||||
|
if [ "$?" = "0" ] && _contains ".<p><b>Message queued.</b> You will receive it in a few seconds."; then |
||||
|
_info "wa send success." |
||||
|
return 0 |
||||
|
fi |
||||
|
_err "wa send error." |
||||
|
_debug "URL" "$_finalUrl" |
||||
|
_debug "Response" "$response" |
||||
|
return 1 |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
#Support Discord webhooks |
||||
|
|
||||
|
# Required: |
||||
|
#DISCORD_WEBHOOK_URL="" |
||||
|
# Optional: |
||||
|
#DISCORD_USERNAME="" |
||||
|
#DISCORD_AVATAR_URL="" |
||||
|
|
||||
|
discord_send() { |
||||
|
_subject="$1" |
||||
|
_content="$2" |
||||
|
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped |
||||
|
_debug "_statusCode" "$_statusCode" |
||||
|
|
||||
|
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-$(_readaccountconf_mutable DISCORD_WEBHOOK_URL)}" |
||||
|
if [ -z "$DISCORD_WEBHOOK_URL" ]; then |
||||
|
DISCORD_WEBHOOK_URL="" |
||||
|
_err "You didn't specify a Discord webhook url DISCORD_WEBHOOK_URL yet." |
||||
|
return 1 |
||||
|
fi |
||||
|
_saveaccountconf_mutable DISCORD_WEBHOOK_URL "$DISCORD_WEBHOOK_URL" |
||||
|
|
||||
|
DISCORD_USERNAME="${DISCORD_USERNAME:-$(_readaccountconf_mutable DISCORD_USERNAME)}" |
||||
|
if [ "$DISCORD_USERNAME" ]; then |
||||
|
_saveaccountconf_mutable DISCORD_USERNAME "$DISCORD_USERNAME" |
||||
|
fi |
||||
|
|
||||
|
DISCORD_AVATAR_URL="${DISCORD_AVATAR_URL:-$(_readaccountconf_mutable DISCORD_AVATAR_URL)}" |
||||
|
if [ "$DISCORD_AVATAR_URL" ]; then |
||||
|
_saveaccountconf_mutable DISCORD_AVATAR_URL "$DISCORD_AVATAR_URL" |
||||
|
fi |
||||
|
|
||||
|
export _H1="Content-Type: application/json" |
||||
|
|
||||
|
_content="$(printf "**%s**\n%s" "$_subject" "$_content" | _json_encode)" |
||||
|
_data="{\"content\": \"$_content\" " |
||||
|
if [ "$DISCORD_USERNAME" ]; then |
||||
|
_data="$_data, \"username\": \"$DISCORD_USERNAME\" " |
||||
|
fi |
||||
|
if [ "$DISCORD_AVATAR_URL" ]; then |
||||
|
_data="$_data, \"avatar_url\": \"$DISCORD_AVATAR_URL\" " |
||||
|
fi |
||||
|
_data="$_data}" |
||||
|
|
||||
|
if _post "$_data" "$DISCORD_WEBHOOK_URL?wait=true"; then |
||||
|
# shellcheck disable=SC2154 |
||||
|
if [ "$response" ]; then |
||||
|
_info "discord send success." |
||||
|
return 0 |
||||
|
fi |
||||
|
fi |
||||
|
_err "discord send error." |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
#Support weixin work webhooks api |
||||
|
|
||||
|
#WEIXIN_WORK_WEBHOOK="xxxx" |
||||
|
|
||||
|
#optional |
||||
|
#WEIXIN_WORK_KEYWORD="yyyy" |
||||
|
|
||||
|
#`WEIXIN_WORK_SIGNING_KEY`="SEC08ffdbd403cbc3fc8a65xxxxxxxxxxxxxxxxxxxx" |
||||
|
|
||||
|
# subject content statusCode |
||||
|
weixin_work_send() { |
||||
|
_subject="$1" |
||||
|
_content="$2" |
||||
|
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped |
||||
|
_debug "_subject" "$_subject" |
||||
|
_debug "_content" "$_content" |
||||
|
_debug "_statusCode" "$_statusCode" |
||||
|
|
||||
|
WEIXIN_WORK_WEBHOOK="${WEIXIN_WORK_WEBHOOK:-$(_readaccountconf_mutable WEIXIN_WORK_WEBHOOK)}" |
||||
|
if [ -z "$WEIXIN_WORK_WEBHOOK" ]; then |
||||
|
WEIXIN_WORK_WEBHOOK="" |
||||
|
_err "You didn't specify a weixin_work webhooks WEIXIN_WORK_WEBHOOK yet." |
||||
|
_err "You can get yours from https://work.weixin.qq.com/api/doc/90000/90136/91770" |
||||
|
return 1 |
||||
|
fi |
||||
|
_saveaccountconf_mutable WEIXIN_WORK_WEBHOOK "$WEIXIN_WORK_WEBHOOK" |
||||
|
|
||||
|
WEIXIN_WORK_KEYWORD="${WEIXIN_WORK_KEYWORD:-$(_readaccountconf_mutable WEIXIN_WORK_KEYWORD)}" |
||||
|
if [ "$WEIXIN_WORK_KEYWORD" ]; then |
||||
|
_saveaccountconf_mutable WEIXIN_WORK_KEYWORD "$WEIXIN_WORK_KEYWORD" |
||||
|
fi |
||||
|
|
||||
|
_content=$(echo "$_content" | _json_encode) |
||||
|
_subject=$(echo "$_subject" | _json_encode) |
||||
|
_data="{\"msgtype\": \"text\", \"text\": {\"content\": \"[$WEIXIN_WORK_KEYWORD]\n$_subject\n$_content\"}}" |
||||
|
|
||||
|
response="$(_post "$_data" "$WEIXIN_WORK_WEBHOOK" "" "POST" "application/json")" |
||||
|
|
||||
|
if [ "$?" = "0" ] && _contains "$response" "errmsg\":\"ok"; then |
||||
|
_info "weixin_work webhooks event fired success." |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
_err "weixin_work webhooks event fired error." |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue