]> git.proxmox.com Git - mirror_acme.sh.git/blob - dnsapi/dns_freedns.sh
Merge pull request #4658 from Justman10000/master
[mirror_acme.sh.git] / dnsapi / dns_freedns.sh
1 #!/usr/bin/env sh
2
3 #This file name is "dns_freedns.sh"
4 #So, here must be a method dns_freedns_add()
5 #Which will be called by acme.sh to add the txt record to your api system.
6 #returns 0 means success, otherwise error.
7 #
8 #Author: David Kerr
9 #Report Bugs here: https://github.com/dkerr64/acme.sh
10 #or here... https://github.com/acmesh-official/acme.sh/issues/2305
11 #
12 ######## Public functions #####################
13
14 # Export FreeDNS userid and password in following variables...
15 # FREEDNS_User=username
16 # FREEDNS_Password=password
17 # login cookie is saved in acme account config file so userid / pw
18 # need to be set only when changed.
19
20 #Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
21 dns_freedns_add() {
22 fulldomain="$1"
23 txtvalue="$2"
24
25 _info "Add TXT record using FreeDNS"
26 _debug "fulldomain: $fulldomain"
27 _debug "txtvalue: $txtvalue"
28
29 if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then
30 FREEDNS_User=""
31 FREEDNS_Password=""
32 if [ -z "$FREEDNS_COOKIE" ]; then
33 _err "You did not specify the FreeDNS username and password yet."
34 _err "Please export as FREEDNS_User / FREEDNS_Password and try again."
35 return 1
36 fi
37 using_cached_cookies="true"
38 else
39 FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")"
40 if [ -z "$FREEDNS_COOKIE" ]; then
41 return 1
42 fi
43 using_cached_cookies="false"
44 fi
45
46 _debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)"
47
48 _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE"
49
50 # We may have to cycle through the domain name to find the
51 # TLD that we own...
52 i=1
53 wmax="$(echo "$fulldomain" | tr '.' ' ' | wc -w)"
54 while [ "$i" -lt "$wmax" ]; do
55 # split our full domain name into two parts...
56 sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
57 i="$(_math "$i" + 1)"
58 top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)"
59 _debug "sub_domain: $sub_domain"
60 _debug "top_domain: $top_domain"
61
62 DNSdomainid="$(_freedns_domain_id "$top_domain")"
63 if [ "$?" = "0" ]; then
64 _info "Domain $top_domain found at FreeDNS, domain_id $DNSdomainid"
65 break
66 else
67 _info "Domain $top_domain not found at FreeDNS, try with next level of TLD"
68 fi
69 done
70
71 if [ -z "$DNSdomainid" ]; then
72 # If domain ID is empty then something went wrong (top level
73 # domain not found at FreeDNS).
74 _err "Domain $top_domain not found at FreeDNS"
75 return 1
76 fi
77
78 # Add in new TXT record with the value provided
79 _debug "Adding TXT record for $fulldomain, $txtvalue"
80 _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
81 return $?
82 }
83
84 #Usage: fulldomain txtvalue
85 #Remove the txt record after validation.
86 dns_freedns_rm() {
87 fulldomain="$1"
88 txtvalue="$2"
89
90 _info "Delete TXT record using FreeDNS"
91 _debug "fulldomain: $fulldomain"
92 _debug "txtvalue: $txtvalue"
93
94 # Need to read cookie from conf file again in case new value set
95 # during login to FreeDNS when TXT record was created.
96 FREEDNS_COOKIE="$(_readaccountconf "FREEDNS_COOKIE")"
97 _debug "FreeDNS login cookies: $FREEDNS_COOKIE"
98
99 TXTdataid="$(_freedns_data_id "$fulldomain" "TXT")"
100 if [ "$?" != "0" ]; then
101 _info "Cannot delete TXT record for $fulldomain, record does not exist at FreeDNS"
102 return 1
103 fi
104 _debug "Data ID's found, $TXTdataid"
105
106 # now we have one (or more) TXT record data ID's. Load the page
107 # for that record and search for the record txt value. If match
108 # then we can delete it.
109 lines="$(echo "$TXTdataid" | wc -l)"
110 _debug "Found $lines TXT data records for $fulldomain"
111 i=0
112 while [ "$i" -lt "$lines" ]; do
113 i="$(_math "$i" + 1)"
114 dataid="$(echo "$TXTdataid" | sed -n "${i}p")"
115 _debug "$dataid"
116
117 htmlpage="$(_freedns_retrieve_data_page "$FREEDNS_COOKIE" "$dataid")"
118 if [ "$?" != "0" ]; then
119 if [ "$using_cached_cookies" = "true" ]; then
120 _err "Has your FreeDNS username and password changed? If so..."
121 _err "Please export as FREEDNS_User / FREEDNS_Password and try again."
122 fi
123 return 1
124 fi
125
126 echo "$htmlpage" | grep "value=\""$txtvalue"\"" >/dev/null
127 if [ "$?" = "0" ]; then
128 # Found a match... delete the record and return
129 _info "Deleting TXT record for $fulldomain, $txtvalue"
130 _freedns_delete_txt_record "$FREEDNS_COOKIE" "$dataid"
131 return $?
132 fi
133 done
134
135 # If we get this far we did not find a match
136 # Not necessarily an error, but log anyway.
137 _info "Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS"
138 return 0
139 }
140
141 #################### Private functions below ##################################
142
143 # usage: _freedns_login username password
144 # print string "cookie=value" etc.
145 # returns 0 success
146 _freedns_login() {
147 export _H1="Accept-Language:en-US"
148 username="$1"
149 password="$2"
150 url="https://freedns.afraid.org/zc.php?step=2"
151
152 _debug "Login to FreeDNS as user $username"
153
154 htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")"
155
156 if [ "$?" != "0" ]; then
157 _err "FreeDNS login failed for user $username bad RC from _post"
158 return 1
159 fi
160
161 cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
162
163 # if cookies is not empty then logon successful
164 if [ -z "$cookies" ]; then
165 _debug3 "htmlpage: $htmlpage"
166 _err "FreeDNS login failed for user $username. Check $HTTP_HEADER file"
167 return 1
168 fi
169
170 printf "%s" "$cookies"
171 return 0
172 }
173
174 # usage _freedns_retrieve_subdomain_page login_cookies
175 # echo page retrieved (html)
176 # returns 0 success
177 _freedns_retrieve_subdomain_page() {
178 export _H1="Cookie:$1"
179 export _H2="Accept-Language:en-US"
180 url="https://freedns.afraid.org/subdomain/"
181
182 _debug "Retrieve subdomain page from FreeDNS"
183
184 htmlpage="$(_get "$url")"
185
186 if [ "$?" != "0" ]; then
187 _err "FreeDNS retrieve subdomains failed bad RC from _get"
188 return 1
189 elif [ -z "$htmlpage" ]; then
190 _err "FreeDNS returned empty subdomain page"
191 return 1
192 fi
193
194 _debug3 "htmlpage: $htmlpage"
195
196 printf "%s" "$htmlpage"
197 return 0
198 }
199
200 # usage _freedns_retrieve_data_page login_cookies data_id
201 # echo page retrieved (html)
202 # returns 0 success
203 _freedns_retrieve_data_page() {
204 export _H1="Cookie:$1"
205 export _H2="Accept-Language:en-US"
206 data_id="$2"
207 url="https://freedns.afraid.org/subdomain/edit.php?data_id=$2"
208
209 _debug "Retrieve data page for ID $data_id from FreeDNS"
210
211 htmlpage="$(_get "$url")"
212
213 if [ "$?" != "0" ]; then
214 _err "FreeDNS retrieve data page failed bad RC from _get"
215 return 1
216 elif [ -z "$htmlpage" ]; then
217 _err "FreeDNS returned empty data page"
218 return 1
219 fi
220
221 _debug3 "htmlpage: $htmlpage"
222
223 printf "%s" "$htmlpage"
224 return 0
225 }
226
227 # usage _freedns_add_txt_record login_cookies domain_id subdomain value
228 # returns 0 success
229 _freedns_add_txt_record() {
230 export _H1="Cookie:$1"
231 export _H2="Accept-Language:en-US"
232 domain_id="$2"
233 subdomain="$3"
234 value="$(printf '%s' "$4" | _url_encode)"
235 url="https://freedns.afraid.org/subdomain/save.php?step=2"
236
237 htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")"
238
239 if [ "$?" != "0" ]; then
240 _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post"
241 return 1
242 elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then
243 _debug3 "htmlpage: $htmlpage"
244 _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
245 return 1
246 elif _contains "$htmlpage" "security code was incorrect"; then
247 _debug3 "htmlpage: $htmlpage"
248 _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code"
249 _err "Note that you cannot use automatic DNS validation for FreeDNS public domains"
250 return 1
251 fi
252
253 _debug3 "htmlpage: $htmlpage"
254 _info "Added acme challenge TXT record for $fulldomain at FreeDNS"
255 return 0
256 }
257
258 # usage _freedns_delete_txt_record login_cookies data_id
259 # returns 0 success
260 _freedns_delete_txt_record() {
261 export _H1="Cookie:$1"
262 export _H2="Accept-Language:en-US"
263 data_id="$2"
264 url="https://freedns.afraid.org/subdomain/delete2.php"
265
266 htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")"
267
268 if [ "$?" != "0" ]; then
269 _err "FreeDNS failed to delete TXT record for $data_id bad RC from _get"
270 return 1
271 elif ! _contains "$htmlheader" "200 OK"; then
272 _debug2 "htmlheader: $htmlheader"
273 _err "FreeDNS failed to delete TXT record $data_id"
274 return 1
275 fi
276
277 _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS"
278 return 0
279 }
280
281 # usage _freedns_domain_id domain_name
282 # echo the domain_id if found
283 # return 0 success
284 _freedns_domain_id() {
285 # Start by escaping the dots in the domain name
286 search_domain="$(echo "$1" | sed 's/\./\\./g')"
287
288 # Sometimes FreeDNS does not return the subdomain page but rather
289 # returns a page regarding becoming a premium member. This usually
290 # happens after a period of inactivity. Immediately trying again
291 # returns the correct subdomain page. So, we will try twice to
292 # load the page and obtain our domain ID
293 attempts=2
294 while [ "$attempts" -gt "0" ]; do
295 attempts="$(_math "$attempts" - 1)"
296
297 htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
298 if [ "$?" != "0" ]; then
299 if [ "$using_cached_cookies" = "true" ]; then
300 _err "Has your FreeDNS username and password changed? If so..."
301 _err "Please export as FREEDNS_User / FREEDNS_Password and try again."
302 fi
303 return 1
304 fi
305
306 domain_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' |
307 grep "<td>$search_domain</td>\|<td>$search_domain(.*)</td>" |
308 sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' |
309 cut -d = -f 2)"
310 # The above beauty extracts domain ID from the html page...
311 # strip out all blank space and new lines. Then insert newlines
312 # before each table row <tr>
313 # search for the domain within each row (which may or may not have
314 # a text string in brackets (.*) after it.
315 # And finally extract the domain ID.
316 if [ -n "$domain_id" ]; then
317 printf "%s" "$domain_id"
318 return 0
319 fi
320 _debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)"
321 done
322 _debug "Domain $search_domain not found after retry"
323 return 1
324 }
325
326 # usage _freedns_data_id domain_name record_type
327 # echo the data_id(s) if found
328 # return 0 success
329 _freedns_data_id() {
330 # Start by escaping the dots in the domain name
331 search_domain="$(echo "$1" | sed 's/\./\\./g')"
332 record_type="$2"
333
334 # Sometimes FreeDNS does not return the subdomain page but rather
335 # returns a page regarding becoming a premium member. This usually
336 # happens after a period of inactivity. Immediately trying again
337 # returns the correct subdomain page. So, we will try twice to
338 # load the page and obtain our domain ID
339 attempts=2
340 while [ "$attempts" -gt "0" ]; do
341 attempts="$(_math "$attempts" - 1)"
342
343 htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
344 if [ "$?" != "0" ]; then
345 if [ "$using_cached_cookies" = "true" ]; then
346 _err "Has your FreeDNS username and password changed? If so..."
347 _err "Please export as FREEDNS_User / FREEDNS_Password and try again."
348 fi
349 return 1
350 fi
351
352 data_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' |
353 grep "<td[a-zA-Z=#]*>$record_type</td>" |
354 grep "<ahref.*>$search_domain</a>" |
355 sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' |
356 cut -d = -f 2)"
357 # The above beauty extracts data ID from the html page...
358 # strip out all blank space and new lines. Then insert newlines
359 # before each table row <tr>
360 # search for the record type withing each row (e.g. TXT)
361 # search for the domain within each row (which is within a <a..>
362 # </a> anchor. And finally extract the domain ID.
363 if [ -n "$data_id" ]; then
364 printf "%s" "$data_id"
365 return 0
366 fi
367 _debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)"
368 done
369 _debug "Domain $search_domain not found after retry"
370 return 1
371 }