3 #This file name is "dns_dnsservices.sh"
4 #Script for Danish DNS registra and DNS hosting provider https://dns.services
6 #Author: Bjarke Bruun <bbruun@gmail.com>
7 #Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152
9 # Global variable to connect to the DNS.Services API
10 DNSServices_API
=https
://dns.services
/api
12 ######## Public functions #####################
14 #Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
15 dns_dnsservices_add
() {
19 _info
"Using dns.services to create ACME DNS challenge"
20 _debug2 add_fulldomain
"$fulldomain"
21 _debug2 add_txtvalue
"$txtvalue"
23 # Read username/password from environment or .acme.sh/accounts.conf
24 DnsServices_Username
="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
25 DnsServices_Password
="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
26 if [ -z "$DnsServices_Username" ] ||
[ -z "$DnsServices_Password" ]; then
27 DnsServices_Username
=""
28 DnsServices_Password
=""
29 _err
"You didn't specify dns.services api username and password yet."
30 _err
"Set environment variables DnsServices_Username and DnsServices_Password"
34 # Setup GET/POST/DELETE headers
37 #save the credentials to the account conf file.
38 _saveaccountconf_mutable DnsServices_Username
"$DnsServices_Username"
39 _saveaccountconf_mutable DnsServices_Password
"$DnsServices_Password"
41 if ! _contains
"$DnsServices_Username" "@"; then
42 _err
"It seems that the username variable DnsServices_Username has not been set/left blank"
43 _err
"or is not a valid email. Please correct and try again."
47 if ! _get_root
"${fulldomain}"; then
48 _err
"Invalid domain ${fulldomain}"
52 if ! createRecord
"$fulldomain" "${txtvalue}"; then
53 _err
"Error creating TXT record in domain $fulldomain in $rootZoneName"
57 _debug2 challenge-created
"Created $fulldomain"
61 #Usage: fulldomain txtvalue
62 #Description: Remove the txt record after validation.
63 dns_dnsservices_rm
() {
67 _info
"Using dns.services to remove DNS record $fulldomain TXT $txtvalue"
68 _debug rm_fulldomain
"$fulldomain"
69 _debug rm_txtvalue
"$txtvalue"
71 # Read username/password from environment or .acme.sh/accounts.conf
72 DnsServices_Username
="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
73 DnsServices_Password
="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
74 if [ -z "$DnsServices_Username" ] ||
[ -z "$DnsServices_Password" ]; then
75 DnsServices_Username
=""
76 DnsServices_Password
=""
77 _err
"You didn't specify dns.services api username and password yet."
78 _err
"Set environment variables DnsServices_Username and DnsServices_Password"
82 # Setup GET/POST/DELETE headers
85 if ! _get_root
"${fulldomain}"; then
86 _err
"Invalid domain ${fulldomain}"
90 _debug2 rm_rootDomainInfo
"found root domain $rootZoneName for $fulldomain"
92 if ! deleteRecord
"${fulldomain}" "${txtvalue}"; then
93 _err
"Error removing record: $fulldomain TXT ${txtvalue}"
100 #################### Private functions below ##################################
103 # Set up API Headers for _get() and _post()
104 # The <function>_add or <function>_rm must have been called before to work
106 if [ -z "$DnsServices_Username" ] ||
[ -z "$DnsServices_Password" ]; then
107 _err
"Could not setup BASIC authentication headers, they are missing"
111 DnsServiceCredentials
="$(printf "%s
" "$DnsServices_Username:$DnsServices_Password" | _base64)"
112 export _H1
="Authorization: Basic $DnsServiceCredentials"
113 export _H2
="Content-Type: application/json"
115 # Just return if headers are set
121 _debug2 _get_root
"Get the root domain of ${domain} for DNS API"
123 # Setup _get() and _post() headers
126 result
=$
(_H1
="$_H1" _H2
="$_H2" _get
"$DNSServices_API/dns")
127 result2
="$(printf "%s
\n" "$result" | tr '[' '\n' | grep '"name
"')"
128 result3
="$(printf "%s
\n" "$result2" | tr '}' '\n' | grep '"name
"' | sed "s
,^\
,,,g
" | sed "s
,$
,},g
")"
130 _debug2 _get_root
"Got the following root domain(s) $result"
131 _debug2 _get_root
"- JSON: $result"
133 if [ "$(printf "%s
\n" "$result" | tr '}' '\n' | grep -c '"name
"')" -gt "1" ]; then
134 checkMultiZones
="true"
135 _debug2 _get_root
"- multiple zones found"
137 checkMultiZones
="false"
138 _debug2 _get_root
"- single zone found"
141 # Find/isolate the root zone to work with in createRecord() and deleteRecord()
143 if [ "$checkMultiZones" = "true" ]; then
144 #rootZone=$(for x in $(printf "%s" "${result3}" | tr ',' '\n' | sed -n 's/.*"name":"\(.*\)",.*/\1/p'); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
145 rootZone
=$
(for x
in $
(printf "%s\n" "${result3}" |
tr ',' '\n' |
grep name | cut
-d'"' -f4); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
146 if [ "$rootZone" != "" ]; then
147 _debug2 _rootZone
"- root zone for $domain is $rootZone"
149 _err
"Could not find root zone for $domain, is it correctly typed?"
153 rootZone
=$
(echo "$result" |
tr '}' '\n' | _egrep_o
'"name":"[^"]*' | cut
-d'"' -f4)
154 _debug2 _get_root
"- only found 1 domain in API: $rootZone"
157 if [ -z "$rootZone" ]; then
158 _err
"Could not find root domain for $domain - is it correctly typed?"
162 # Make sure we use the correct API zone data
163 useResult
="$(printf "%s
\n" "${result3}" tr ',' '\n' | grep "$rootZone")"
164 _debug2 _useResult
"useResult=$useResult"
166 # Setup variables used by other functions to communicate with DNS.Services API
167 #zoneInfo=$(printf "%s\n" "$useResult" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g')
168 zoneInfo
=$
(printf "%s\n" "$useResult" |
tr ',' '\n' |
grep '"name"' | cut
-d'"' -f4)
169 rootZoneName
="$rootZone"
170 subDomainName
="$(printf "%s
\n" "$domain" | sed "s
,\.
$rootZone,,g
")"
171 subDomainNameClean
="$(printf "%s
\n" "$domain" | sed "s
,_acme-challenge.
,,g
")"
172 rootZoneDomainID
=$
(printf "%s\n" "$useResult" |
tr ',' '\n' |
grep domain_id | cut
-d'"' -f4)
173 rootZoneServiceID
=$
(printf "%s\n" "$useResult" |
tr ',' '\n' |
grep service_id | cut
-d'"' -f4)
175 _debug2 _zoneInfo
"Zone info from API : $zoneInfo"
176 _debug2 _get_root
"Root zone name : $rootZoneName"
177 _debug2 _get_root
"Root zone domain ID : $rootZoneDomainID"
178 _debug2 _get_root
"Root zone service ID: $rootZoneServiceID"
179 _debug2 _get_root
"Sub domain : $subDomainName"
181 _debug _get_root
"Found valid root domain $rootZone for $subDomainNameClean"
189 # Get root domain information - needed for DNS.Services API communication
190 if [ -z "$rootZoneName" ] ||
[ -z "$rootZoneDomainID" ] ||
[ -z "$rootZoneServiceID" ]; then
191 _get_root
"$fulldomain"
193 if [ -z "$rootZoneName" ] ||
[ -z "$rootZoneDomainID" ] ||
[ -z "$rootZoneServiceID" ]; then
194 _err
"Something happend - could not get the API zone information"
198 _debug2 createRecord
"CNAME TXT value is: $txtvalue"
200 # Prepare data to send to API
201 data
="{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\", \"ttl\":\"10\"}"
203 _debug2 createRecord
"data to API: $data"
204 result
=$
(_post
"$data" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records" "" "POST")
205 _debug2 createRecord
"result from API: $result"
207 if [ "$(echo "$result" | _egrep_o "\"success
\":true
")" = "" ]; then
208 _err
"Failed to create TXT record $fulldomain with content $txtvalue in zone $rootZoneName"
213 _info
"Record \"$fulldomain TXT $txtvalue\" has been created"
221 _log deleteRecord
"Deleting $fulldomain TXT $txtvalue record"
223 if [ -z "$rootZoneName" ] ||
[ -z "$rootZoneDomainID" ] ||
[ -z "$rootZoneServiceID" ]; then
224 _get_root
"$fulldomain"
227 result
="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service
/$rootZoneServiceID/dns
/$rootZoneDomainID")"
228 #recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")"
229 #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')"
230 recordID
="$(printf "%s
\n" "$result" | tr '}' '\n' | grep -- "$txtvalue" | tr ',' '\n' | grep '"id
"' | cut -d'"' -f4)"
231 _debug2 _recordID "recordID used for deletion of record: $recordID"
233 if [ -z "$recordID" ]; then
234 _info "Record $fulldomain TXT $txtvalue not found or already deleted"
237 _debug2 deleteRecord "Found recordID=$recordID"
240 _debug2 deleteRecord "DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
241 _log "curl DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
242 result="$(_H1="$_H1" _H2="$_H2" _post "" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" "" "DELETE")"
243 _debug2 deleteRecord "API Delete result \"$result\""
244 _log "curl API Delete result \"$result\""
246 # Return OK regardless