#!/usr/bin/env sh
#This file name is "dns_dnsservices.sh"
#Script for Danish DNS registra and DNS hosting provider https://dns.services
#Author: Bjarke Bruun <bbruun@gmail.com>
#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 #####################
#Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dnsservices_add( ) {
fulldomain = $1
txtvalue = $2
_info "Using dns.services to create ACME DNS challenge"
_debug2 add_fulldomain " $fulldomain "
_debug2 add_txtvalue " $txtvalue "
# Read username/password from environment or .acme.sh/accounts.conf
DnsServices_Username = " ${ DnsServices_Username :- $( _readaccountconf_mutable DnsServices_Username) } "
DnsServices_Password = " ${ DnsServices_Password :- $( _readaccountconf_mutable DnsServices_Password) } "
if [ -z " $DnsServices_Username " ] || [ -z " $DnsServices_Password " ] ; then
DnsServices_Username = ""
DnsServices_Password = ""
_err "You didn't specify dns.services api username and password yet."
_err "Set environment variables DnsServices_Username and DnsServices_Password"
return 1
fi
# Setup GET/POST/DELETE headers
_setup_headers
#save the credentials to the account conf file.
_saveaccountconf_mutable DnsServices_Username " $DnsServices_Username "
_saveaccountconf_mutable DnsServices_Password " $DnsServices_Password "
if ! _contains " $DnsServices_Username " "@" ; then
_err "It seems that the username variable DnsServices_Username has not been set/left blank"
_err "or is not a valid email. Please correct and try again."
return 1
fi
if ! _get_root " ${ fulldomain } " ; then
_err " Invalid domain ${ fulldomain } "
return 1
fi
if ! createRecord " $fulldomain " " ${ txtvalue } " ; then
_err " Error creating TXT record in domain $fulldomain in $rootZoneName "
return 1
fi
_debug2 challenge-created " Created $fulldomain "
return 0
}
#Usage: fulldomain txtvalue
#Description: Remove the txt record after validation.
dns_dnsservices_rm( ) {
fulldomain = $1
txtvalue = $2
_info " Using dns.services to remove DNS record $fulldomain TXT $txtvalue "
_debug rm_fulldomain " $fulldomain "
_debug rm_txtvalue " $txtvalue "
# Read username/password from environment or .acme.sh/accounts.conf
DnsServices_Username = " ${ DnsServices_Username :- $( _readaccountconf_mutable DnsServices_Username) } "
DnsServices_Password = " ${ DnsServices_Password :- $( _readaccountconf_mutable DnsServices_Password) } "
if [ -z " $DnsServices_Username " ] || [ -z " $DnsServices_Password " ] ; then
DnsServices_Username = ""
DnsServices_Password = ""
_err "You didn't specify dns.services api username and password yet."
_err "Set environment variables DnsServices_Username and DnsServices_Password"
return 1
fi
# Setup GET/POST/DELETE headers
_setup_headers
if ! _get_root " ${ fulldomain } " ; then
_err " Invalid domain ${ fulldomain } "
return 1
fi
_debug2 rm_rootDomainInfo " found root domain $rootZoneName for $fulldomain "
if ! deleteRecord " ${ fulldomain } " " ${ txtvalue } " ; then
_err " Error removing record: $fulldomain TXT ${ txtvalue } "
return 1
fi
return 0
}
#################### Private functions below ##################################
_setup_headers( ) {
# Set up API Headers for _get() and _post()
# The <function>_add or <function>_rm must have been called before to work
if [ -z " $DnsServices_Username " ] || [ -z " $DnsServices_Password " ] ; then
_err "Could not setup BASIC authentication headers, they are missing"
return 1
fi
DnsServiceCredentials = " $( printf "%s" " $DnsServices_Username : $DnsServices_Password " | _base64) "
export _H1 = " Authorization: Basic $DnsServiceCredentials "
export _H2 = "Content-Type: application/json"
# Just return if headers are set
return 0
}
_get_root( ) {
domain = $1
_debug2 _get_root " Get the root domain of ${ domain } for DNS API "
# Setup _get() and _post() headers
#_setup_headers
result = $( _H1 = " $_H1 " _H2 = " $_H2 " _get " $DNSServices_API /dns " )
_debug2 _get_root " Got the following root domain(s) $result "
_debug2 _get_root " - JSON: $result "
if [ " $( echo " $result " | grep -c '"name"' ) " -gt "1" ] ; then
checkMultiZones = "true"
_debug2 _get_root "- multiple zones found"
else
checkMultiZones = "false"
fi
# Find/isolate the root zone to work with in createRecord() and deleteRecord()
rootZone = ""
if [ " $checkMultiZones " = "true" ] ; then
rootZone = $( for zone in $( echo " $result " | tr -d '\n' ' ' ) ; do
if [ " $( echo " $domain " | grep " $zone " ) " != "" ] ; then
_debug2 _get_root " - trying to figure out if $zone is in $domain "
echo " $zone "
break
fi
done )
else
rootZone = $( echo " $result " | _egrep_o '"name":"[^"]*' | cut -d'"' -f4)
_debug2 _get_root " - only found 1 domain in API: $rootZone "
fi
if [ -z " $rootZone " ] ; then
_err " Could not find root domain for $domain - is it correctly typed? "
return 1
fi
# Setup variables used by other functions to communicate with DNS.Services API
#zoneInfo=$(echo "$result" | sed "s,\"zones,\n&,g" | grep zones | cut -d'[' -f2 | cut -d']' -f1 | tr '}' '\n' | grep "\"$rootZone\"")
zoneInfo = $( echo " $result " | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g' | grep " \" $rootZone \" " )
rootZoneName = " $rootZone "
subDomainName = " $( echo " $domain " | sed " s,\. $rootZone ,,g " ) "
subDomainNameClean = " $( echo " $domain " | sed "s,_acme-challenge.,,g" ) "
rootZoneDomainID = $( echo " $result " | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"domain_id":")([^"]*)"(.*)$,\2,g' )
rootZoneServiceID = $( echo " $result " | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"service_id":")([^"]*)"(.*)$,\2,g' )
_debug2 _zoneInfo " Zone info from API : $zoneInfo "
_debug2 _get_root " Root zone name : $rootZoneName "
_debug2 _get_root " Root zone domain ID : $rootZoneDomainID "
_debug2 _get_root " Root zone service ID: $rootZoneServiceID "
_debug2 _get_root " Sub domain : $subDomainName "
_debug _get_root " Found valid root domain $rootZone for $subDomainNameClean "
return 0
}
createRecord( ) {
fulldomain = $1
txtvalue = " $2 "
# Get root domain information - needed for DNS.Services API communication
if [ -z " $rootZoneName " ] || [ -z " $rootZoneDomainID " ] || [ -z " $rootZoneServiceID " ] ; then
_get_root " $fulldomain "
fi
_debug2 createRecord " CNAME TXT value is: $txtvalue "
# Prepare data to send to API
data = " {\"name\":\" ${ fulldomain } \",\"type\":\"TXT\",\"content\":\" ${ txtvalue } \", \"ttl\":\"10\"} "
_debug2 createRecord " data to API: $data "
result = $( _post " $data " " $DNSServices_API /service/ $rootZoneServiceID /dns/ $rootZoneDomainID /records " "" "POST" )
_debug2 createRecord " result from API: $result "
if [ " $( echo " $result " | _egrep_o "\"success\":true" ) " = "" ] ; then
_err " Failed to create TXT record $fulldomain with content $txtvalue in zone $rootZoneName "
_err " $result "
return 1
fi
_info " Record \" $fulldomain TXT $txtvalue \" has been created "
return 0
}
deleteRecord( ) {
fulldomain = $1
txtvalue = $2
_log deleteRecord " Deleting $fulldomain TXT $txtvalue record "
if [ -z " $rootZoneName " ] || [ -z " $rootZoneDomainID " ] || [ -z " $rootZoneServiceID " ] ; then
_get_root " $fulldomain "
fi
result = " $( _H1 = " $_H1 " _H2 = " $_H2 " _get " $DNSServices_API /service/ $rootZoneServiceID /dns/ $rootZoneDomainID " ) "
recordInfo = " $( echo " $result " | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep " ${ txtvalue } " ) "
recordID = " $( echo " $recordInfo " | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep " ${ txtvalue } " | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g' ) "
if [ -z " $recordID " ] ; then
_info " Record $fulldomain TXT $txtvalue not found or already deleted "
return 0
else
_debug2 deleteRecord " Found recordID= $recordID "
fi
_debug2 deleteRecord " DELETE request $DNSServices_API /service/ $rootZoneServiceID /dns/ $rootZoneDomainID /records/ $recordID "
_log " curl DELETE request $DNSServices_API /service/ $rootZoneServiceID /dns/ $rootZoneDomainID /records/ $recordID "
result = " $( _H1 = " $_H1 " _H2 = " $_H2 " _post "" " $DNSServices_API /service/ $rootZoneServiceID /dns/ $rootZoneDomainID /records/ $recordID " "" "DELETE" ) "
_debug2 deleteRecord " API Delete result \" $result \" "
_log " curl API Delete result \" $result \" "
# Return OK regardless
return 0
}