]>
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 | |
10 | # | |
11 | ######## Public functions ##################### | |
12 | ||
39f32396 | 13 | # Export FreeDNS userid and password in following variables... |
600a2351 DK |
14 | # FREEDNS_User=username |
15 | # FREEDNS_Password=password | |
16 | # login cookie is saved in acme account config file so userid / pw | |
17 | # need to be set only when changed. | |
18 | ||
19 | #Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | |
20 | dns_freedns_add() { | |
21 | fulldomain="$1" | |
22 | txtvalue="$2" | |
23 | ||
24 | _info "Add TXT record using FreeDNS" | |
25 | _debug "fulldomain: $fulldomain" | |
26 | _debug "txtvalue: $txtvalue" | |
27 | ||
28 | if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then | |
29 | FREEDNS_User="" | |
30 | FREEDNS_Password="" | |
31 | if [ -z "$FREEDNS_COOKIE" ]; then | |
32 | _err "You did not specify the FreeDNS username and password yet." | |
33 | _err "Please export as FREEDNS_User / FREEDNS_Password and try again." | |
34 | return 1 | |
35 | fi | |
36 | using_cached_cookies="true" | |
37 | else | |
38 | FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")" | |
39 | if [ -z "$FREEDNS_COOKIE" ]; then | |
40 | return 1 | |
41 | fi | |
42 | using_cached_cookies="false" | |
43 | fi | |
44 | ||
45 | _debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)" | |
46 | ||
47 | _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" | |
48 | ||
49 | # split our full domain name into two parts... | |
50 | i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" | |
51 | i="$(_math "$i" - 1)" | |
52 | top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)" | |
53 | i="$(_math "$i" - 1)" | |
54 | sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" | |
55 | ||
e3ddb677 DK |
56 | _debug "top_domain: $top_domain" |
57 | _debug "sub_domain: $sub_domain" | |
58 | ||
3c07f57a | 59 | # Sometimes FreeDNS does not return the subdomain page but rather |
600a2351 DK |
60 | # returns a page regarding becoming a premium member. This usually |
61 | # happens after a period of inactivity. Immediately trying again | |
62 | # returns the correct subdomain page. So, we will try twice to | |
63 | # load the page and obtain our domain ID | |
64 | attempts=2 | |
65 | while [ "$attempts" -gt "0" ]; do | |
66 | attempts="$(_math "$attempts" - 1)" | |
e3ddb677 | 67 | |
600a2351 DK |
68 | htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" |
69 | if [ "$?" != "0" ]; then | |
70 | if [ "$using_cached_cookies" = "true" ]; then | |
9b2aa974 | 71 | _err "Has your FreeDNS username and password changed? If so..." |
600a2351 DK |
72 | _err "Please export as FREEDNS_User / FREEDNS_Password and try again." |
73 | fi | |
74 | return 1 | |
75 | fi | |
ceafe389 | 76 | |
dcf8457f | 77 | subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")" |
e3ddb677 | 78 | _debug3 "subdomain_csv: $subdomain_csv" |
600a2351 | 79 | |
600a2351 DK |
80 | # The above beauty ends with striping out rows that do not have an |
81 | # href to edit.php and do not have the top domain we are looking for. | |
82 | # So all we should be left with is CSV of table of subdomains we are | |
83 | # interested in. | |
84 | ||
85 | # Now we have to read through this table and extract the data we need | |
86 | lines="$(echo "$subdomain_csv" | wc -l)" | |
600a2351 DK |
87 | i=0 |
88 | found=0 | |
e3ddb677 | 89 | DNSdomainid="" |
600a2351 DK |
90 | while [ "$i" -lt "$lines" ]; do |
91 | i="$(_math "$i" + 1)" | |
dcf8457f | 92 | line="$(echo "$subdomain_csv" | sed -n "${i}p")" |
62dd3a53 | 93 | _debug2 "line: $line" |
ceafe389 | 94 | if [ $found = 0 ] && _contains "$line" "<td>$top_domain</td>"; then |
600a2351 | 95 | # this line will contain DNSdomainid for the top_domain |
ceafe389 | 96 | DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" |
e3ddb677 | 97 | _debug2 "DNSdomainid: $DNSdomainid" |
600a2351 | 98 | found=1 |
62dd3a53 | 99 | break |
600a2351 DK |
100 | fi |
101 | done | |
102 | ||
600a2351 DK |
103 | if [ -z "$DNSdomainid" ]; then |
104 | # If domain ID is empty then something went wrong (top level | |
105 | # domain not found at FreeDNS). | |
106 | if [ "$attempts" = "0" ]; then | |
107 | # exhausted maximum retry attempts | |
600a2351 DK |
108 | _err "Domain $top_domain not found at FreeDNS" |
109 | return 1 | |
110 | fi | |
111 | else | |
112 | # break out of the 'retry' loop... we have found our domain ID | |
113 | break | |
114 | fi | |
115 | _info "Domain $top_domain not found at FreeDNS" | |
116 | _info "Retry loading subdomain page ($attempts attempts remaining)" | |
117 | done | |
118 | ||
e3ddb677 DK |
119 | # Add in new TXT record with the value provided |
120 | _debug "Adding TXT record for $fulldomain, $txtvalue" | |
121 | _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" | |
122 | return $? | |
600a2351 DK |
123 | } |
124 | ||
125 | #Usage: fulldomain txtvalue | |
126 | #Remove the txt record after validation. | |
127 | dns_freedns_rm() { | |
128 | fulldomain="$1" | |
129 | txtvalue="$2" | |
130 | ||
131 | _info "Delete TXT record using FreeDNS" | |
132 | _debug "fulldomain: $fulldomain" | |
133 | _debug "txtvalue: $txtvalue" | |
134 | ||
135 | # Need to read cookie from conf file again in case new value set | |
136 | # during login to FreeDNS when TXT record was created. | |
d39b1082 | 137 | # acme.sh does not have a _readaccountconf() function |
600a2351 DK |
138 | FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" |
139 | _debug "FreeDNS login cookies: $FREEDNS_COOKIE" | |
140 | ||
3c07f57a | 141 | # Sometimes FreeDNS does not return the subdomain page but rather |
600a2351 DK |
142 | # returns a page regarding becoming a premium member. This usually |
143 | # happens after a period of inactivity. Immediately trying again | |
144 | # returns the correct subdomain page. So, we will try twice to | |
145 | # load the page and obtain our TXT record. | |
146 | attempts=2 | |
147 | while [ "$attempts" -gt "0" ]; do | |
148 | attempts="$(_math "$attempts" - 1)" | |
149 | ||
150 | htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" | |
151 | if [ "$?" != "0" ]; then | |
152 | return 1 | |
153 | fi | |
154 | ||
dcf8457f | 155 | subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")" |
e3ddb677 | 156 | _debug3 "subdomain_csv: $subdomain_csv" |
ceafe389 | 157 | |
600a2351 DK |
158 | # The above beauty ends with striping out rows that do not have an |
159 | # href to edit.php and do not have the domain name we are looking for. | |
160 | # So all we should be left with is CSV of table of subdomains we are | |
161 | # interested in. | |
162 | ||
163 | # Now we have to read through this table and extract the data we need | |
164 | lines="$(echo "$subdomain_csv" | wc -l)" | |
600a2351 DK |
165 | i=0 |
166 | found=0 | |
e3ddb677 | 167 | DNSdataid="" |
600a2351 DK |
168 | while [ "$i" -lt "$lines" ]; do |
169 | i="$(_math "$i" + 1)" | |
dcf8457f | 170 | line="$(echo "$subdomain_csv" | sed -n "${i}p")" |
e3ddb677 | 171 | _debug3 "line: $line" |
ceafe389 | 172 | DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)" |
e3ddb677 DK |
173 | _debug2 "DNSname: $DNSname" |
174 | if [ "$DNSname" = "$fulldomain" ]; then | |
175 | DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)" | |
176 | _debug2 "DNStype: $DNStype" | |
177 | if [ "$DNStype" = "TXT" ]; then | |
178 | DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" | |
179 | _debug2 "DNSdataid: $DNSdataid" | |
180 | DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)" | |
181 | if _startswith "$DNSvalue" """; then | |
182 | # remove the quotation from the start | |
183 | DNSvalue="$(echo "$DNSvalue" | cut -c 7-)" | |
184 | fi | |
185 | if _endswith "$DNSvalue" "..."; then | |
186 | # value was truncated, remove the dot dot dot from the end | |
187 | DNSvalue="$(echo "$DNSvalue" | sed 's/...$//')" | |
188 | elif _endswith "$DNSvalue" """; then | |
189 | # else remove the closing quotation from the end | |
190 | DNSvalue="$(echo "$DNSvalue" | sed 's/......$//')" | |
191 | fi | |
192 | _debug2 "DNSvalue: $DNSvalue" | |
193 | ||
62dd3a53 | 194 | if [ -n "$DNSdataid" ] && _startswith "$txtvalue" "$DNSvalue"; then |
e3ddb677 DK |
195 | # Found a match. But note... Website is truncating the |
196 | # value field so we are only testing that part that is not | |
197 | # truncated. This should be accurate enough. | |
198 | _debug "Deleting TXT record for $fulldomain, $txtvalue" | |
199 | _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" | |
200 | return $? | |
201 | fi | |
202 | ||
203 | fi | |
600a2351 DK |
204 | fi |
205 | done | |
206 | done | |
207 | ||
208 | # If we get this far we did not find a match (after two attempts) | |
209 | # Not necessarily an error, but log anyway. | |
e3ddb677 DK |
210 | _debug3 "$subdomain_csv" |
211 | _info "Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS" | |
600a2351 DK |
212 | return 0 |
213 | } | |
214 | ||
215 | #################### Private functions below ################################## | |
216 | ||
217 | # usage: _freedns_login username password | |
218 | # print string "cookie=value" etc. | |
219 | # returns 0 success | |
220 | _freedns_login() { | |
87f5ec5b | 221 | export _H1="Accept-Language:en-US" |
600a2351 DK |
222 | username="$1" |
223 | password="$2" | |
224 | url="https://freedns.afraid.org/zc.php?step=2" | |
225 | ||
226 | _debug "Login to FreeDNS as user $username" | |
227 | ||
228 | htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")" | |
229 | ||
230 | if [ "$?" != "0" ]; then | |
231 | _err "FreeDNS login failed for user $username bad RC from _post" | |
232 | return 1 | |
233 | fi | |
234 | ||
235 | cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" | |
236 | ||
237 | # if cookies is not empty then logon successful | |
238 | if [ -z "$cookies" ]; then | |
e3ddb677 | 239 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
240 | _err "FreeDNS login failed for user $username. Check $HTTP_HEADER file" |
241 | return 1 | |
242 | fi | |
243 | ||
244 | printf "%s" "$cookies" | |
245 | return 0 | |
246 | } | |
247 | ||
248 | # usage _freedns_retrieve_subdomain_page login_cookies | |
249 | # echo page retrieved (html) | |
250 | # returns 0 success | |
251 | _freedns_retrieve_subdomain_page() { | |
252 | export _H1="Cookie:$1" | |
87f5ec5b | 253 | export _H2="Accept-Language:en-US" |
600a2351 DK |
254 | url="https://freedns.afraid.org/subdomain/" |
255 | ||
7f32488b | 256 | _debug "Retrieve subdomain page from FreeDNS" |
600a2351 DK |
257 | |
258 | htmlpage="$(_get "$url")" | |
259 | ||
260 | if [ "$?" != "0" ]; then | |
7f32488b | 261 | _err "FreeDNS retrieve subdomains failed bad RC from _get" |
600a2351 | 262 | return 1 |
f78b656f | 263 | elif [ -z "$htmlpage" ]; then |
600a2351 DK |
264 | _err "FreeDNS returned empty subdomain page" |
265 | return 1 | |
266 | fi | |
267 | ||
e3ddb677 | 268 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
269 | |
270 | printf "%s" "$htmlpage" | |
271 | return 0 | |
272 | } | |
273 | ||
274 | # usage _freedns_add_txt_record login_cookies domain_id subdomain value | |
275 | # returns 0 success | |
276 | _freedns_add_txt_record() { | |
277 | export _H1="Cookie:$1" | |
87f5ec5b | 278 | export _H2="Accept-Language:en-US" |
600a2351 DK |
279 | domain_id="$2" |
280 | subdomain="$3" | |
281 | value="$(printf '%s' "$4" | _url_encode)" | |
912bcf94 | 282 | url="https://freedns.afraid.org/subdomain/save.php?step=2" |
600a2351 DK |
283 | |
284 | htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")" | |
285 | ||
286 | if [ "$?" != "0" ]; then | |
287 | _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post" | |
288 | return 1 | |
f78b656f | 289 | elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then |
e3ddb677 | 290 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
291 | _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" |
292 | return 1 | |
f78b656f | 293 | elif _contains "$htmlpage" "security code was incorrect"; then |
e3ddb677 | 294 | _debug3 "htmlpage: $htmlpage" |
00777a10 | 295 | _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code" |
f78b656f DK |
296 | _err "Note that you cannot use automatic DNS validation for FreeDNS public domains" |
297 | return 1 | |
600a2351 | 298 | fi |
f78b656f | 299 | |
e3ddb677 | 300 | _debug3 "htmlpage: $htmlpage" |
600a2351 DK |
301 | _info "Added acme challenge TXT record for $fulldomain at FreeDNS" |
302 | return 0 | |
303 | } | |
304 | ||
305 | # usage _freedns_delete_txt_record login_cookies data_id | |
306 | # returns 0 success | |
307 | _freedns_delete_txt_record() { | |
308 | export _H1="Cookie:$1" | |
87f5ec5b | 309 | export _H2="Accept-Language:en-US" |
600a2351 DK |
310 | data_id="$2" |
311 | url="https://freedns.afraid.org/subdomain/delete2.php" | |
312 | ||
313 | htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")" | |
314 | ||
315 | if [ "$?" != "0" ]; then | |
316 | _err "FreeDNS failed to delete TXT record for $data_id bad RC from _get" | |
317 | return 1 | |
f78b656f | 318 | elif ! _contains "$htmlheader" "200 OK"; then |
e3ddb677 | 319 | _debug2 "htmlheader: $htmlheader" |
600a2351 DK |
320 | _err "FreeDNS failed to delete TXT record $data_id" |
321 | return 1 | |
322 | fi | |
323 | ||
324 | _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS" | |
325 | return 0 | |
326 | } |