]>
Commit | Line | Data |
---|---|---|
600a2351 DK |
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 | |
d795fac3 | 10 | #or here... https://github.com/acmesh-official/acme.sh/issues/2305 |
600a2351 DK |
11 | # |
12 | ######## Public functions ##################### | |
13 | ||
39f32396 | 14 | # Export FreeDNS userid and password in following variables... |
600a2351 DK |
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 | ||
09fb9dcd DK |
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 | ||
66c39a95 | 62 | DNSdomainid="$(_freedns_domain_id "$top_domain")" |
09fb9dcd DK |
63 | if [ "$?" = "0" ]; then |
64 | _info "Domain $top_domain found at FreeDNS, domain_id $DNSdomainid" | |
600a2351 | 65 | break |
09fb9dcd DK |
66 | else |
67 | _info "Domain $top_domain not found at FreeDNS, try with next level of TLD" | |
600a2351 | 68 | fi |
600a2351 DK |
69 | done |
70 | ||
09fb9dcd DK |
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 | ||
e3ddb677 DK |
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 $? | |
600a2351 DK |
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. | |
2cb0b00e | 96 | FREEDNS_COOKIE="$(_readaccountconf "FREEDNS_COOKIE")" |
600a2351 DK |
97 | _debug "FreeDNS login cookies: $FREEDNS_COOKIE" |
98 | ||
09fb9dcd DK |
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")" | |
600a2351 | 118 | if [ "$?" != "0" ]; then |
09fb9dcd DK |
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 | |
600a2351 DK |
123 | return 1 |
124 | fi | |
125 | ||
09fb9dcd DK |
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 | |
600a2351 DK |
133 | done |
134 | ||
09fb9dcd | 135 | # If we get this far we did not find a match |
600a2351 | 136 | # Not necessarily an error, but log anyway. |
e3ddb677 | 137 | _info "Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS" |
600a2351 DK |
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() { | |
87f5ec5b | 147 | export _H1="Accept-Language:en-US" |
600a2351 DK |
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 | |
e3ddb677 | 165 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
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" | |
87f5ec5b | 179 | export _H2="Accept-Language:en-US" |
600a2351 DK |
180 | url="https://freedns.afraid.org/subdomain/" |
181 | ||
7f32488b | 182 | _debug "Retrieve subdomain page from FreeDNS" |
600a2351 DK |
183 | |
184 | htmlpage="$(_get "$url")" | |
185 | ||
186 | if [ "$?" != "0" ]; then | |
7f32488b | 187 | _err "FreeDNS retrieve subdomains failed bad RC from _get" |
600a2351 | 188 | return 1 |
f78b656f | 189 | elif [ -z "$htmlpage" ]; then |
600a2351 DK |
190 | _err "FreeDNS returned empty subdomain page" |
191 | return 1 | |
192 | fi | |
193 | ||
e3ddb677 | 194 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
195 | |
196 | printf "%s" "$htmlpage" | |
197 | return 0 | |
198 | } | |
199 | ||
09fb9dcd DK |
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 | ||
600a2351 DK |
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" | |
87f5ec5b | 231 | export _H2="Accept-Language:en-US" |
600a2351 DK |
232 | domain_id="$2" |
233 | subdomain="$3" | |
234 | value="$(printf '%s' "$4" | _url_encode)" | |
912bcf94 | 235 | url="https://freedns.afraid.org/subdomain/save.php?step=2" |
600a2351 DK |
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 | |
f78b656f | 242 | elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then |
e3ddb677 | 243 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
244 | _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" |
245 | return 1 | |
f78b656f | 246 | elif _contains "$htmlpage" "security code was incorrect"; then |
e3ddb677 | 247 | _debug3 "htmlpage: $htmlpage" |
00777a10 | 248 | _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code" |
f78b656f DK |
249 | _err "Note that you cannot use automatic DNS validation for FreeDNS public domains" |
250 | return 1 | |
600a2351 | 251 | fi |
f78b656f | 252 | |
e3ddb677 | 253 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
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" | |
87f5ec5b | 262 | export _H2="Accept-Language:en-US" |
600a2351 DK |
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 | |
f78b656f | 271 | elif ! _contains "$htmlheader" "200 OK"; then |
e3ddb677 | 272 | _debug2 "htmlheader: $htmlheader" |
600a2351 DK |
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 | } | |
09fb9dcd DK |
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 | ||
19c43451 | 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)" | |
09fb9dcd DK |
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 | |
66c39a95 | 322 | _debug "Domain $search_domain not found after retry" |
09fb9dcd DK |
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 | |
10994d65 | 351 | |
19c43451 | 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)" | |
09fb9dcd DK |
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..> | |
ac9f6e3a | 362 | # </a> anchor. And finally extract the domain ID. |
09fb9dcd DK |
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 | |
66c39a95 | 369 | _debug "Domain $search_domain not found after retry" |
09fb9dcd DK |
370 | return 1 |
371 | } |