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.
9 #Report Bugs here: https://github.com/dkerr64/acme.sh
11 ######## Public functions #####################
13 # Export FreeDNS userid and password in following variables...
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.
19 #Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
24 _info
"Add TXT record using FreeDNS"
25 _debug
"fulldomain: $fulldomain"
26 _debug
"txtvalue: $txtvalue"
28 if [ -z "$FREEDNS_User" ] ||
[ -z "$FREEDNS_Password" ]; then
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."
36 using_cached_cookies
="true"
38 FREEDNS_COOKIE
="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")"
39 if [ -z "$FREEDNS_COOKIE" ]; then
42 using_cached_cookies
="false"
45 _debug
"FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)"
47 _saveaccountconf FREEDNS_COOKIE
"$FREEDNS_COOKIE"
49 # split our full domain name into two parts...
50 i
="$(echo "$fulldomain" | tr '.' ' ' | wc -w)"
52 top_domain
="$(echo "$fulldomain" | cut -d. -f "$i"-100)"
54 sub_domain
="$(echo "$fulldomain" | cut -d. -f -"$i")"
56 _debug
"top_domain: $top_domain"
57 _debug
"sub_domain: $sub_domain"
59 # Sometimes FreeDNS does not return the subdomain page but rather
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
65 while [ "$attempts" -gt "0" ]; do
66 attempts
="$(_math "$attempts" - 1)"
68 htmlpage
="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
69 if [ "$?" != "0" ]; then
70 if [ "$using_cached_cookies" = "true" ]; then
71 _err
"Has your FreeDNS username and password changed? If so..."
72 _err
"Please export as FREEDNS_User / FREEDNS_Password and try again."
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")"
78 _debug3
"subdomain_csv: $subdomain_csv"
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
85 # Now we have to read through this table and extract the data we need
86 lines
="$(echo "$subdomain_csv" | wc -l)"
90 while [ "$i" -lt "$lines" ]; do
92 line
="$(echo "$subdomain_csv" | sed -n "${i}p
")"
94 if [ $found = 0 ] && _contains
"$line" "<td>$top_domain</td>"; then
95 # this line will contain DNSdomainid for the top_domain
96 DNSdomainid
="$(echo "$line" | _egrep_o "edit_domain_id
*= *.
*>" | cut -d = -f 2 | cut -d '>' -f 1)"
97 _debug2
"DNSdomainid: $DNSdomainid"
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
108 _err
"Domain $top_domain not found at FreeDNS"
112 # break out of the 'retry' loop... we have found our domain ID
115 _info
"Domain $top_domain not found at FreeDNS"
116 _info
"Retry loading subdomain page ($attempts attempts remaining)"
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"
125 #Usage: fulldomain txtvalue
126 #Remove the txt record after validation.
131 _info
"Delete TXT record using FreeDNS"
132 _debug
"fulldomain: $fulldomain"
133 _debug
"txtvalue: $txtvalue"
135 # Need to read cookie from conf file again in case new value set
136 # during login to FreeDNS when TXT record was created.
137 # acme.sh does not have a _readaccountconf() function
138 FREEDNS_COOKIE
="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE
")"
139 _debug
"FreeDNS login cookies: $FREEDNS_COOKIE"
141 # Sometimes FreeDNS does not return the subdomain page but rather
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.
147 while [ "$attempts" -gt "0" ]; do
148 attempts
="$(_math "$attempts" - 1)"
150 htmlpage
="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
151 if [ "$?" != "0" ]; then
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")"
156 _debug3
"subdomain_csv: $subdomain_csv"
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
163 # Now we have to read through this table and extract the data we need
164 lines
="$(echo "$subdomain_csv" | wc -l)"
168 while [ "$i" -lt "$lines" ]; do
169 i
="$(_math "$i" + 1)"
170 line
="$(echo "$subdomain_csv" | sed -n "${i}p
")"
171 _debug3
"line: $line"
172 DNSname
="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
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-)"
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/......$//')"
192 _debug2
"DNSvalue: $DNSvalue"
194 if [ -n "$DNSdataid" ] && _startswith
"$txtvalue" "$DNSvalue"; then
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"
208 # If we get this far we did not find a match (after two attempts)
209 # Not necessarily an error, but log anyway.
210 _debug3
"$subdomain_csv"
211 _info
"Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS"
215 #################### Private functions below ##################################
217 # usage: _freedns_login username password
218 # print string "cookie=value" etc.
221 export _H1
="Accept-Language:en-US"
224 url
="https://freedns.afraid.org/zc.php?step=2"
226 _debug
"Login to FreeDNS as user $username"
228 htmlpage
="$(_post "username
=$
(printf '%s' "$username" | _url_encode
)&password
=$
(printf '%s' "$password" | _url_encode
)&submit
=Login
&action
=auth
" "$url")"
230 if [ "$?" != "0" ]; then
231 _err
"FreeDNS login failed for user $username bad RC from _post"
235 cookies
="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
237 # if cookies is not empty then logon successful
238 if [ -z "$cookies" ]; then
239 _debug3
"htmlpage: $htmlpage"
240 _err
"FreeDNS login failed for user $username. Check $HTTP_HEADER file"
244 printf "%s" "$cookies"
248 # usage _freedns_retrieve_subdomain_page login_cookies
249 # echo page retrieved (html)
251 _freedns_retrieve_subdomain_page
() {
252 export _H1
="Cookie:$1"
253 export _H2
="Accept-Language:en-US"
254 url
="https://freedns.afraid.org/subdomain/"
256 _debug
"Retrieve subdomain page from FreeDNS"
258 htmlpage
="$(_get "$url")"
260 if [ "$?" != "0" ]; then
261 _err
"FreeDNS retrieve subdomains failed bad RC from _get"
263 elif [ -z "$htmlpage" ]; then
264 _err
"FreeDNS returned empty subdomain page"
268 _debug3
"htmlpage: $htmlpage"
270 printf "%s" "$htmlpage"
274 # usage _freedns_add_txt_record login_cookies domain_id subdomain value
276 _freedns_add_txt_record
() {
277 export _H1
="Cookie:$1"
278 export _H2
="Accept-Language:en-US"
281 value
="$(printf '%s' "$4" | _url_encode)"
282 url
="https://freedns.afraid.org/subdomain/save.php?step=2"
284 htmlpage
="$(_post "type=TXT
&domain_id
=$domain_id&subdomain
=$subdomain&address
=%22$value%22&send
=Save
%21" "$url")"
286 if [ "$?" != "0" ]; then
287 _err
"FreeDNS failed to add TXT record for $subdomain bad RC from _post"
289 elif ! grep "200 OK" "$HTTP_HEADER" >/dev
/null
; then
290 _debug3
"htmlpage: $htmlpage"
291 _err
"FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
293 elif _contains
"$htmlpage" "security code was incorrect"; then
294 _debug3
"htmlpage: $htmlpage"
295 _err
"FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code"
296 _err
"Note that you cannot use automatic DNS validation for FreeDNS public domains"
300 _debug3
"htmlpage: $htmlpage"
301 _info
"Added acme challenge TXT record for $fulldomain at FreeDNS"
305 # usage _freedns_delete_txt_record login_cookies data_id
307 _freedns_delete_txt_record
() {
308 export _H1
="Cookie:$1"
309 export _H2
="Accept-Language:en-US"
311 url
="https://freedns.afraid.org/subdomain/delete2.php"
313 htmlheader
="$(_get "$url?data_id
%5B
%5D
=$data_id&submit
=delete
+selected
" "onlyheader
")"
315 if [ "$?" != "0" ]; then
316 _err
"FreeDNS failed to delete TXT record for $data_id bad RC from _get"
318 elif ! _contains
"$htmlheader" "200 OK"; then
319 _debug2
"htmlheader: $htmlheader"
320 _err
"FreeDNS failed to delete TXT record $data_id"
324 _info
"Deleted acme challenge TXT record for $fulldomain at FreeDNS"