From 57a48f81192a994c389d5d60414a3aa0a29feca0 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 21 Jan 2017 16:12:30 -0500 Subject: [PATCH 01/17] Support for FreeDNS --- dnsapi/dns_freedns.sh | 394 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100755 dnsapi/dns_freedns.sh diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh new file mode 100755 index 00000000..c51172ee --- /dev/null +++ b/dnsapi/dns_freedns.sh @@ -0,0 +1,394 @@ + +#!/usr/bin/env sh + +#This file name is "dns_freedns.sh" +#So, here must be a method dns_freedns_add() +#Which will be called by acme.sh to add the txt record to your api system. +#returns 0 means success, otherwise error. +# +#Author: David Kerr +#Report Bugs here: https://github.com/Neilpang/acme.sh +# +######## Public functions ##################### + +# Requires FreeDNS userid and password in folowing variables... +# FREEDNS_USER=username +# FREEDNS_PASSWORD=password + +#Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_freedns_add() { + fulldomain=$1 + txtvalue=$2 + + _info "Add TXT record using FreeDNS" + _debug "fulldomain: $fulldomain" + _debug "txtvalue: $txtvalue" + + if [ -z "$FREEDNS_USER" ] || [ -z "$FREEDNS_PASSWORD" ]; then + AD_API_KEY="" + _err "You didn't specify the FreeDNS username and password yet." + _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." + return 1 + fi + + login_cookies="$(_freedns_login $FREEDNS_USER $FREEDNS_PASSWORD)" + if [ -z "$login_cookies" ]; then + return 1 + fi + + _saveaccountconf FREEDNS_USER "$FREEDNS_USER" + _saveaccountconf FREEDNS_PASSWORD "$FREEDNS_PASSWORD" + + htmlpage="$(_freedns_retrieve_subdomain_page $login_cookies)" + if [ $? != 0 ]; then + return $? + fi + + # split our full domain name into two parts... + top_domain="$(echo $fulldomain | rev | cut -d. -f -2 | rev)" + sub_domain="$(echo $fulldomain | rev | cut -d. -f 3- | rev)" + + # Now convert the tables in the HTML to CSV. This litte gem from + # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv + subdomain_csv="$(echo $htmlpage | + grep -i -e ']*>/\n/Ig' | + sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' | + sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' | + sed 's/<\/T[DH][^>]*>]*>/,/Ig' | + grep 'edit.php?' | + grep $top_domain)" + # The above beauty ends with striping out rows that do not have an + # href to edit.php and do not have the top domain we are looking for. + # So all we should be left with is CSV of table of subdomains we are + # interested in. + + # Now we have to read through this table and extract the data we need + IFS=$'\n' + found=0 + for line in $subdomain_csv + do + tmp="$(echo $line | cut -d ',' -f 1)" + if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then + # this line will contain DNSdomainid for the top_domain + tmp="$(echo $line | cut -d ',' -f 2)" + url=${tmp#*=} + url=${url%%>*} + DNSdomainid=${url#*domain_id=} + found=1 + else + # lines contain DNS records for all subdomains + dns_href="$(echo $line | cut -d ',' -f 2)" + tmp=${dns_href#*>} + DNSname=${tmp%%<*} + DNStype="$(echo $line | cut -d ',' -f 3)" + if [ "$DNSname" = "$fulldomain" -a "$DNStype" = "TXT" ]; then + tmp=${dns_href#*=} + url=${tmp%%>*} + DNSdataid=${url#*data_id=} + # Now get current value for the TXT record. This method may + # produce inaccurate results as the value field is truncated + # on this webpage. To get full value we would need to load + # another page. However we don't really need this so long as + # there is only one TXT record for the acme chalenge subdomain. + tmp="$(echo $line | cut -d ',' -f 4)" + # strip the html double-quotes off the value + tmp=${tmp#"} + DNSvalue=${tmp%"} + if [ $found != 0 ]; then + break + # we are breaking out of the loop at the first match of DNS name + # and DNS type (if we are past finding the domainid). This assumes + # that there is only ever one TXT record for the LetsEncrypt/acme + # challenge subdomain. This seems to be a reasonable assumption + # as the acme client deletes the TXT record on successful validation. + fi + else + DNSname="" + DNStype="" + fi + fi + done + unset IFS + + _debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid" + _debug "DNSvalue: $DNSvalue" + + if [ -z "$DNSdomainid" ]; then + # If domain ID is empty then something went wrong (top level + # domain not found at FreeDNS). Cannot proceed. + _debug2 "$htmlpage" + _debug2 "$subdomain_csv" + _err "Domain $top_domain not found at FreeDNS" + return 1 + fi + + if [ -z "$DNSdataid" ]; then + # If data ID is empty then specific subdomain does not exist yet, need + # to create it this should always be the case as the acme client + # deletes the entry after domain is validated. + _freedns_add_txt_record $login_cookies $DNSdomainid $sub_domain "$txtvalue" + return $? + else + if [ "$txtvalue" = "$DNSvalue" ]; then + # if value in TXT record matches value requested then DNS record + # does not need to be updated. But... + # Testing value match fails. Website is truncating the value field. + # So for now we will always go down the else path. Though in theory + # should never come here anyway as the acme client deletes + # the TXT record on successful validation, so we should not even + # have found a TXT record !! + _info "No update necessary for $fulldomain at FreeDNS" + return 0 + else + # Delete the old TXT record (with the wrong value) + _freedns_delete_txt_record $login_cookies $DNSdataid + if [ $? = 0 ]; then + # And add in new TXT record with the value provided + _freedns_add_txt_record $login_cookies $DNSdomainid $sub_domain "$txtvalue" + fi + return $? + fi + fi + return 0 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_freedns_rm() { + fulldomain=$1 + txtvalue=$2 + + _info "Delete TXT record using FreeDNS" + _debug "fulldomain: $fulldomain" + _debug "txtvalue: $txtvalue" + + login_cookies="$(_freedns_login $FREEDNS_USER $FREEDNS_PASSWORD)" + if [ -z "$login_cookies" ]; then + return 1 + fi + + htmlpage="$(_freedns_retrieve_subdomain_page $login_cookies)" + if [ $? != 0 ]; then + return $? + fi + + # Now convert the tables in the HTML to CSV. This litte gem from + # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv + subdomain_csv="$(echo $htmlpage | + grep -i -e ']*>/\n/Ig' | + sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' | + sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' | + sed 's/<\/T[DH][^>]*>]*>/,/Ig' | + grep 'edit.php?' | + grep $fulldomain)" + # The above beauty ends with striping out rows that do not have an + # href to edit.php and do not have the domain name we are looking for. + # So all we should be left with is CSV of table of subdomains we are + # interested in. + + # Now we have to read through this table and extract the data we need + IFS=$'\n' + for line in $subdomain_csv + do + dns_href="$(echo $line | cut -d ',' -f 2)" + tmp=${dns_href#*>} + DNSname=${tmp%%<*} + DNStype="$(echo $line | cut -d ',' -f 3)" + if [ "$DNSname" = "$fulldomain" -a "$DNStype" = "TXT" ]; then + tmp=${dns_href#*=} + url=${tmp%%>*} + DNSdataid=${url#*data_id=} + tmp="$(echo $line | cut -d ',' -f 4)" + # strip the html double-quotes off the value + tmp=${tmp#"} + DNSvalue=${tmp%"} + _debug "DNSvalue: $DNSvalue" +# if [ "$DNSvalue" = "$txtvalue" ]; then + # Testing value match fails. Website is truncating the value + # field. So for now we will assume that there is only one TXT + # field for the sub domain and just delete it. Currently this + # is a safe assumption. + _freedns_delete_txt_record $login_cookies $DNSdataid + unset IFS + return $? +# fi + fi + done + unset IFS + + # If we get this far we did not find a match. + # Not necessarily an error, but log anyway. + _debug2 "$subdomain_csv" + _info "Cannot delete TXT record for $fulldomain/$txtvalue. Does not exist at FreeDNS" + return 0 +} + +#################### Private functions below ################################## + +# usage: _freedns_login username password +# echos string "cookie:value;cookie:value" etc +# returns 0 success +_freedns_login() { + username=$1 + password=$2 + url="https://freedns.afraid.org/zc.php?step=2" + + _debug "Login to FreeDNS as user $username" + # Not using acme.sh _post() function because I need to capture the cookies. + cookie_file="$(curl --silent \ + --user-agent "$USER_AGENT" \ + --data "username=$(_freedns_urlencode "$username")&password=$(_freedns_urlencode "$password")&submit=Login&action=auth" \ + --cookie-jar - \ + $url )" + + if [ $? != 0 ]; then + _err "FreeDNS login failed for user $username bad RC from cURL: $?" + return $? + fi + + # convert from cookie file format to cookie string + cookies="" + found=0 + IFS=$'\n' + for line in $cookie_file + do + # strip spaces from start and end of line + line="$(echo "$line" | xargs)" + if [ $found = 0 ]; then + # first line, validate that it is a cookie file + if _contains "$line" "Netscape HTTP Cookie File"; then + found=1 + else + _debug2 "$cookie_file" + _err "FreeDNS login failed for user $username bad cookie file" + unset IFS + return 1 + fi + else + # after first line skip blank line or comments + if [ -n "$line" -a "$(echo $line | cut -c 1)" != "#" ]; then + if [ -n "$cookies" ]; then + cookies="$cookies;" + fi + cookies="$cookies$(echo $line | cut -d ' ' -f 6)=$(echo $line | cut -d ' ' -f 7)" + fi + fi + done + unset IFS + + # if cookies is not empty then logon successful + if [ -z "$cookies" ]; then + _err "FreeDNS login failed for user $username" + return 1 + fi + + _debug "FreeDNS login cookies: $cookies" + echo "$cookies" + return 0 +} + +# usage _freedns_retrieve_subdomain_page login_cookies +# echo page retrieved (html) +# returns 0 success +_freedns_retrieve_subdomain_page() { + cookies=$1 + url="https://freedns.afraid.org/subdomain/" + + _debug "Retrieve subdmoain page from FreeDNS" + # Not using acme.sh _get() function becuase I need to pass in the cookies. + htmlpage="$(curl --silent \ + --user-agent "$USER_AGENT" \ + --cookie "$cookies" \ + $url )" + + if [ $? != 0 ]; then + _err "FreeDNS retrieve subdomins failed bad RC from cURL: $?" + return $? + fi + + if [ -z "$htmlpage" ]; then + _err "FreeDNS returned empty subdomain page" + return 1 + fi + + _debug2 "$htmlpage" + + echo "$htmlpage" + return 0 +} + +_freedns_add_txt_record() { + cookies=$1 + domain_id=$2 + subdomain=$3 + value="$(_freedns_urlencode "$4")" + url="http://freedns.afraid.org/subdomain/save.php?step=2" + + # Not using acme.sh _get() function becuase I need to pass in the cookies. + htmlpage="$(curl --silent \ + --user-agent "$USER_AGENT" \ + --cookie "$cookies" \ + --data "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" \ + $url )" + + if [ $? != 0 ]; then + _err "FreeDNS failed to add TXT record for $subdomain bad RC from cURL: $?" + return $? + fi + + # returned page should be empty on success + if [ -n "$htmlpage" ]; then + _debug2 "$htmlpage" + _err "FreeDNS failed to add TXT record for $subdomain" + return 1 + fi + _info "Added acme challenge TXT record for $fulldomain at FreeDNS" + return 0 +} + +_freedns_delete_txt_record() { + cookies=$1 + data_id=$2 + url="https://freedns.afraid.org/subdomain/delete2.php" + + # Not using acme.sh _get() function becuase I need to pass in the cookies. + htmlpage="$(curl --silent \ + --user-agent "$USER_AGENT" \ + --cookie "$cookies" \ + "$url?data_id%5B%5D=$data_id&submit=delete+selected" )" + + if [ $? != 0 ]; then + _err "FreeDNS failed to delete TXT record for $subdomain bad RC from cURL: $?" + return $? + fi + + # returned page should be empty on success + if [ -n "$htmlpage" ]; then + _debug2 "$htmlpage" + _err "FreeDNS failed to delete TXT record $data_id" + return 1 + fi + _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS" + return 0 +} + +# urlencode magic from... +# http://askubuntu.com/questions/53770/how-can-i-encode-and-decode-percent-encoded-strings-on-the-command-line +# The _urlencode function in acme.sh does not work ! +_freedns_urlencode() { + # urlencode + length="${#1}" + for (( i = 0; i < length; i++ )); do + c="${1:i:1}" + case $c in + [a-zA-Z0-9.~_-]) printf "$c" ;; + *) printf '%%%02X' "'$c" + esac + done +} From 8de728f354d864e9ed0a11b5d507681cefbc3342 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 21 Jan 2017 16:26:54 -0500 Subject: [PATCH 02/17] Delete stray line copied/pasted from another pugin --- dnsapi/dns_freedns.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index c51172ee..fa7102f5 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -25,7 +25,6 @@ dns_freedns_add() { _debug "txtvalue: $txtvalue" if [ -z "$FREEDNS_USER" ] || [ -z "$FREEDNS_PASSWORD" ]; then - AD_API_KEY="" _err "You didn't specify the FreeDNS username and password yet." _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." return 1 From 3674cdde767790d28935d6dd94d5f6afc7d8c908 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Fri, 27 Jan 2017 17:53:46 -0500 Subject: [PATCH 03/17] Cache FreeDNS authentication cookie rather than userid and password. Now the FREEDNS_USER and FREEDNS_PASSWORD need only be exported the first time it is used and whenever the userid or password is changed. Subsequent runs use the cookie cached in account.conf --- dnsapi/dns_freedns.sh | 102 ++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index fa7102f5..490ec307 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -11,9 +11,11 @@ # ######## Public functions ##################### -# Requires FreeDNS userid and password in folowing variables... -# FREEDNS_USER=username -# FREEDNS_PASSWORD=password +# Export FreeDNS userid and password in folowing variables... +# FREEDNS_USER=username +# FREEDNS_PASSWORD=password +# login cookie is saved in acme account config file so userid / pw +# need to be set only when changed. #Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_freedns_add() { @@ -23,24 +25,33 @@ dns_freedns_add() { _info "Add TXT record using FreeDNS" _debug "fulldomain: $fulldomain" _debug "txtvalue: $txtvalue" - + if [ -z "$FREEDNS_USER" ] || [ -z "$FREEDNS_PASSWORD" ]; then - _err "You didn't specify the FreeDNS username and password yet." - _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." - return 1 - fi - - login_cookies="$(_freedns_login $FREEDNS_USER $FREEDNS_PASSWORD)" - if [ -z "$login_cookies" ]; then - return 1 + if [ -z "$FREEDNS_COOKIE" ]; then + _err "You did not specify the FreeDNS username and password yet." + _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." + return 1 + fi + using_cached_cookies="true" + else + FREEDNS_COOKIE="$(_freedns_login $FREEDNS_USER $FREEDNS_PASSWORD)" + if [ -z "$FREEDNS_COOKIE" ]; then + return 1 + fi + using_cached_cookies="false" fi - - _saveaccountconf FREEDNS_USER "$FREEDNS_USER" - _saveaccountconf FREEDNS_PASSWORD "$FREEDNS_PASSWORD" - htmlpage="$(_freedns_retrieve_subdomain_page $login_cookies)" + _debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)" + + _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" + + htmlpage="$(_freedns_retrieve_subdomain_page $FREEDNS_COOKIE)" if [ $? != 0 ]; then - return $? + if [ "$using_cached_cookies" = "true" ]; then + _err "Has your FreeDNS username and password channged? If so..." + _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." + fi + return 1 fi # split our full domain name into two parts... @@ -118,8 +129,8 @@ dns_freedns_add() { if [ -z "$DNSdomainid" ]; then # If domain ID is empty then something went wrong (top level # domain not found at FreeDNS). Cannot proceed. - _debug2 "$htmlpage" - _debug2 "$subdomain_csv" + _debug "$htmlpage" + _debug "$subdomain_csv" _err "Domain $top_domain not found at FreeDNS" return 1 fi @@ -128,7 +139,7 @@ dns_freedns_add() { # If data ID is empty then specific subdomain does not exist yet, need # to create it this should always be the case as the acme client # deletes the entry after domain is validated. - _freedns_add_txt_record $login_cookies $DNSdomainid $sub_domain "$txtvalue" + _freedns_add_txt_record $FREEDNS_COOKIE $DNSdomainid $sub_domain "$txtvalue" return $? else if [ "$txtvalue" = "$DNSvalue" ]; then @@ -143,10 +154,10 @@ dns_freedns_add() { return 0 else # Delete the old TXT record (with the wrong value) - _freedns_delete_txt_record $login_cookies $DNSdataid + _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid if [ $? = 0 ]; then # And add in new TXT record with the value provided - _freedns_add_txt_record $login_cookies $DNSdomainid $sub_domain "$txtvalue" + _freedns_add_txt_record $FREEDNS_COOKIE $DNSdomainid $sub_domain "$txtvalue" fi return $? fi @@ -164,14 +175,16 @@ dns_freedns_rm() { _debug "fulldomain: $fulldomain" _debug "txtvalue: $txtvalue" - login_cookies="$(_freedns_login $FREEDNS_USER $FREEDNS_PASSWORD)" - if [ -z "$login_cookies" ]; then - return 1 - fi + # Need to read cookie from conf file again in case new value set + # during login to FreeDNS when TXT record was created. + + #TODO acme.sh does not have a _readaccountconf() fuction + FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" + _debug "FreeDNS login cookies: $FREEDNS_COOKIE" - htmlpage="$(_freedns_retrieve_subdomain_page $login_cookies)" + htmlpage="$(_freedns_retrieve_subdomain_page $FREEDNS_COOKIE)" if [ $? != 0 ]; then - return $? + return 1 fi # Now convert the tables in the HTML to CSV. This litte gem from @@ -213,7 +226,7 @@ dns_freedns_rm() { # field. So for now we will assume that there is only one TXT # field for the sub domain and just delete it. Currently this # is a safe assumption. - _freedns_delete_txt_record $login_cookies $DNSdataid + _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid unset IFS return $? # fi @@ -239,7 +252,7 @@ _freedns_login() { url="https://freedns.afraid.org/zc.php?step=2" _debug "Login to FreeDNS as user $username" - # Not using acme.sh _post() function because I need to capture the cookies. + #TODO Not using acme.sh _post() function because I need to capture the cookies. cookie_file="$(curl --silent \ --user-agent "$USER_AGENT" \ --data "username=$(_freedns_urlencode "$username")&password=$(_freedns_urlencode "$password")&submit=Login&action=auth" \ @@ -248,7 +261,7 @@ _freedns_login() { if [ $? != 0 ]; then _err "FreeDNS login failed for user $username bad RC from cURL: $?" - return $? + return 1 fi # convert from cookie file format to cookie string @@ -264,7 +277,7 @@ _freedns_login() { if _contains "$line" "Netscape HTTP Cookie File"; then found=1 else - _debug2 "$cookie_file" + _debug "$cookie_file" _err "FreeDNS login failed for user $username bad cookie file" unset IFS return 1 @@ -272,10 +285,12 @@ _freedns_login() { else # after first line skip blank line or comments if [ -n "$line" -a "$(echo $line | cut -c 1)" != "#" ]; then - if [ -n "$cookies" ]; then - cookies="$cookies;" + cookie_name="$(echo $line | cut -d ' ' -f 6)" + if [ "$cookie_name" = "dns_cookie" ]; then + # found the login cookie, that is all we need. + cookies="$cookie_name=$(echo $line | cut -d ' ' -f 7)" + break; fi - cookies="$cookies$(echo $line | cut -d ' ' -f 6)=$(echo $line | cut -d ' ' -f 7)" fi fi done @@ -287,7 +302,6 @@ _freedns_login() { return 1 fi - _debug "FreeDNS login cookies: $cookies" echo "$cookies" return 0 } @@ -300,7 +314,7 @@ _freedns_retrieve_subdomain_page() { url="https://freedns.afraid.org/subdomain/" _debug "Retrieve subdmoain page from FreeDNS" - # Not using acme.sh _get() function becuase I need to pass in the cookies. + #TODO Not using acme.sh _get() function becuase I need to pass in the cookies. htmlpage="$(curl --silent \ --user-agent "$USER_AGENT" \ --cookie "$cookies" \ @@ -308,7 +322,7 @@ _freedns_retrieve_subdomain_page() { if [ $? != 0 ]; then _err "FreeDNS retrieve subdomins failed bad RC from cURL: $?" - return $? + return 1 fi if [ -z "$htmlpage" ]; then @@ -329,7 +343,7 @@ _freedns_add_txt_record() { value="$(_freedns_urlencode "$4")" url="http://freedns.afraid.org/subdomain/save.php?step=2" - # Not using acme.sh _get() function becuase I need to pass in the cookies. + #TODO Not using acme.sh _get() function becuase I need to pass in the cookies. htmlpage="$(curl --silent \ --user-agent "$USER_AGENT" \ --cookie "$cookies" \ @@ -338,12 +352,12 @@ _freedns_add_txt_record() { if [ $? != 0 ]; then _err "FreeDNS failed to add TXT record for $subdomain bad RC from cURL: $?" - return $? + return 1 fi # returned page should be empty on success if [ -n "$htmlpage" ]; then - _debug2 "$htmlpage" + _debug "$htmlpage" _err "FreeDNS failed to add TXT record for $subdomain" return 1 fi @@ -356,7 +370,7 @@ _freedns_delete_txt_record() { data_id=$2 url="https://freedns.afraid.org/subdomain/delete2.php" - # Not using acme.sh _get() function becuase I need to pass in the cookies. + #TODO Not using acme.sh _get() function becuase I need to pass in the cookies. htmlpage="$(curl --silent \ --user-agent "$USER_AGENT" \ --cookie "$cookies" \ @@ -364,12 +378,12 @@ _freedns_delete_txt_record() { if [ $? != 0 ]; then _err "FreeDNS failed to delete TXT record for $subdomain bad RC from cURL: $?" - return $? + return 1 fi # returned page should be empty on success if [ -n "$htmlpage" ]; then - _debug2 "$htmlpage" + _debug "$htmlpage" _err "FreeDNS failed to delete TXT record $data_id" return 1 fi From e5975a713751eec81beaa7c4ec3f591bcbc21994 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 28 Jan 2017 14:51:07 -0500 Subject: [PATCH 04/17] login with acme.sh _post() rather than using curl. --- dnsapi/dns_freedns.sh | 53 ++++++++----------------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 490ec307..686647c9 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -252,57 +252,24 @@ _freedns_login() { url="https://freedns.afraid.org/zc.php?step=2" _debug "Login to FreeDNS as user $username" - #TODO Not using acme.sh _post() function because I need to capture the cookies. - cookie_file="$(curl --silent \ - --user-agent "$USER_AGENT" \ - --data "username=$(_freedns_urlencode "$username")&password=$(_freedns_urlencode "$password")&submit=Login&action=auth" \ - --cookie-jar - \ - $url )" - - if [ $? != 0 ]; then - _err "FreeDNS login failed for user $username bad RC from cURL: $?" + + htmlpage="$(_post "username=$(_freedns_urlencode "$username")&password=$(_freedns_urlencode "$password")&submit=Login&action=auth" "$url")" + + if [ "$?" != "0" ]; then + _err "FreeDNS login failed for user $username bad RC from _post: $?" return 1 fi - - # convert from cookie file format to cookie string - cookies="" - found=0 - IFS=$'\n' - for line in $cookie_file - do - # strip spaces from start and end of line - line="$(echo "$line" | xargs)" - if [ $found = 0 ]; then - # first line, validate that it is a cookie file - if _contains "$line" "Netscape HTTP Cookie File"; then - found=1 - else - _debug "$cookie_file" - _err "FreeDNS login failed for user $username bad cookie file" - unset IFS - return 1 - fi - else - # after first line skip blank line or comments - if [ -n "$line" -a "$(echo $line | cut -c 1)" != "#" ]; then - cookie_name="$(echo $line | cut -d ' ' -f 6)" - if [ "$cookie_name" = "dns_cookie" ]; then - # found the login cookie, that is all we need. - cookies="$cookie_name=$(echo $line | cut -d ' ' -f 7)" - break; - fi - fi - fi - done - unset IFS + + cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" # if cookies is not empty then logon successful if [ -z "$cookies" ]; then - _err "FreeDNS login failed for user $username" + _debug "$htmlpage" + _err "FreeDNS login failed for user $username. Check $HTTP_HEADER file" return 1 fi - echo "$cookies" + printf "%s" "$cookies" return 0 } From abe287528e449536c9356334c65ecb625a82e69b Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 28 Jan 2017 15:15:39 -0500 Subject: [PATCH 05/17] get subdomain page with acme.sh _get() rather than using curl --- dnsapi/dns_freedns.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 686647c9..bae6df7a 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -277,15 +277,12 @@ _freedns_login() { # echo page retrieved (html) # returns 0 success _freedns_retrieve_subdomain_page() { - cookies=$1 + _H1="Cookie: $1" url="https://freedns.afraid.org/subdomain/" _debug "Retrieve subdmoain page from FreeDNS" - #TODO Not using acme.sh _get() function becuase I need to pass in the cookies. - htmlpage="$(curl --silent \ - --user-agent "$USER_AGENT" \ - --cookie "$cookies" \ - $url )" + + htmlpage="$(_get "$url")" if [ $? != 0 ]; then _err "FreeDNS retrieve subdomins failed bad RC from cURL: $?" @@ -299,7 +296,7 @@ _freedns_retrieve_subdomain_page() { _debug2 "$htmlpage" - echo "$htmlpage" + printf "%s" "$htmlpage" return 0 } From 7da47fa55690b547d054f51ec10e62872df9bcc0 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 28 Jan 2017 21:53:03 -0500 Subject: [PATCH 06/17] Remove remaining usage of curl and replace with acme.sh _get and _post plus other misc cleanup --- dnsapi/dns_freedns.sh | 78 ++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index bae6df7a..cf2f77df 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -19,8 +19,8 @@ #Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_freedns_add() { - fulldomain=$1 - txtvalue=$2 + fulldomain="$1" + txtvalue="$2" _info "Add TXT record using FreeDNS" _debug "fulldomain: $fulldomain" @@ -46,7 +46,7 @@ dns_freedns_add() { _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" htmlpage="$(_freedns_retrieve_subdomain_page $FREEDNS_COOKIE)" - if [ $? != 0 ]; then + if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then _err "Has your FreeDNS username and password channged? If so..." _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." @@ -155,7 +155,7 @@ dns_freedns_add() { else # Delete the old TXT record (with the wrong value) _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid - if [ $? = 0 ]; then + if [ "$?" = "0" ]; then # And add in new TXT record with the value provided _freedns_add_txt_record $FREEDNS_COOKIE $DNSdomainid $sub_domain "$txtvalue" fi @@ -168,8 +168,8 @@ dns_freedns_add() { #Usage: fulldomain txtvalue #Remove the txt record after validation. dns_freedns_rm() { - fulldomain=$1 - txtvalue=$2 + fulldomain="$1" + txtvalue="$2" _info "Delete TXT record using FreeDNS" _debug "fulldomain: $fulldomain" @@ -183,7 +183,7 @@ dns_freedns_rm() { _debug "FreeDNS login cookies: $FREEDNS_COOKIE" htmlpage="$(_freedns_retrieve_subdomain_page $FREEDNS_COOKIE)" - if [ $? != 0 ]; then + if [ "$?" != "0" ]; then return 1 fi @@ -226,8 +226,8 @@ dns_freedns_rm() { # field. So for now we will assume that there is only one TXT # field for the sub domain and just delete it. Currently this # is a safe assumption. - _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid unset IFS + _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid return $? # fi fi @@ -244,11 +244,11 @@ dns_freedns_rm() { #################### Private functions below ################################## # usage: _freedns_login username password -# echos string "cookie:value;cookie:value" etc +# print string "cookie=value" etc. # returns 0 success _freedns_login() { - username=$1 - password=$2 + username="$1" + password="$2" url="https://freedns.afraid.org/zc.php?step=2" _debug "Login to FreeDNS as user $username" @@ -256,7 +256,7 @@ _freedns_login() { htmlpage="$(_post "username=$(_freedns_urlencode "$username")&password=$(_freedns_urlencode "$password")&submit=Login&action=auth" "$url")" if [ "$?" != "0" ]; then - _err "FreeDNS login failed for user $username bad RC from _post: $?" + _err "FreeDNS login failed for user $username bad RC from _post" return 1 fi @@ -277,15 +277,15 @@ _freedns_login() { # echo page retrieved (html) # returns 0 success _freedns_retrieve_subdomain_page() { - _H1="Cookie: $1" + _H1="Cookie:$1" url="https://freedns.afraid.org/subdomain/" _debug "Retrieve subdmoain page from FreeDNS" htmlpage="$(_get "$url")" - if [ $? != 0 ]; then - _err "FreeDNS retrieve subdomins failed bad RC from cURL: $?" + if [ "$?" != "0" ]; then + _err "FreeDNS retrieve subdomins failed bad RC from _get" return 1 fi @@ -300,57 +300,51 @@ _freedns_retrieve_subdomain_page() { return 0 } +# usage _freedns_add_txt_record login_cookies domain_id subdomain value +# returns 0 success _freedns_add_txt_record() { - cookies=$1 - domain_id=$2 - subdomain=$3 + _H1="Cookie:$1" + domain_id="$2" + subdomain="$3" value="$(_freedns_urlencode "$4")" url="http://freedns.afraid.org/subdomain/save.php?step=2" + + htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")" - #TODO Not using acme.sh _get() function becuase I need to pass in the cookies. - htmlpage="$(curl --silent \ - --user-agent "$USER_AGENT" \ - --cookie "$cookies" \ - --data "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" \ - $url )" - - if [ $? != 0 ]; then - _err "FreeDNS failed to add TXT record for $subdomain bad RC from cURL: $?" + if [ "$?" != "0" ]; then + _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post" return 1 fi - # returned page should be empty on success - if [ -n "$htmlpage" ]; then + if ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then _debug "$htmlpage" - _err "FreeDNS failed to add TXT record for $subdomain" + _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" return 1 fi _info "Added acme challenge TXT record for $fulldomain at FreeDNS" return 0 } +# usage _freedns_delete_txt_record login_cookies data_id +# returns 0 success _freedns_delete_txt_record() { - cookies=$1 - data_id=$2 + _H1="Cookie:$1" + data_id="$2" url="https://freedns.afraid.org/subdomain/delete2.php" - #TODO Not using acme.sh _get() function becuase I need to pass in the cookies. - htmlpage="$(curl --silent \ - --user-agent "$USER_AGENT" \ - --cookie "$cookies" \ - "$url?data_id%5B%5D=$data_id&submit=delete+selected" )" + htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")" - if [ $? != 0 ]; then - _err "FreeDNS failed to delete TXT record for $subdomain bad RC from cURL: $?" + if [ "$?" != "0" ]; then + _err "FreeDNS failed to delete TXT record for $data_id bad RC from _get" return 1 fi - # returned page should be empty on success - if [ -n "$htmlpage" ]; then - _debug "$htmlpage" + if ! _contains "$htmlheader" "200 OK"; then + _debug "$htmlheader" _err "FreeDNS failed to delete TXT record $data_id" return 1 fi + _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS" return 0 } From 2e26aa11a6ad43c9c3692fad961aa60e6d727e82 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 28 Jan 2017 22:12:25 -0500 Subject: [PATCH 07/17] Cleanup travis/spellcheck issues --- dnsapi/dns_freedns.sh | 101 ++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index cf2f77df..6a4bc197 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env sh #This file name is "dns_freedns.sh" @@ -59,27 +58,26 @@ dns_freedns_add() { sub_domain="$(echo $fulldomain | rev | cut -d. -f 3- | rev)" # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo $htmlpage | - grep -i -e ']*>/\n/Ig' | - sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' | - sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' | - sed 's/<\/T[DH][^>]*>]*>/,/Ig' | - grep 'edit.php?' | - grep $top_domain)" + # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv + subdomain_csv="$(echo $htmlpage \ + | grep -i -e ']*>/\n/Ig' \ + | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ + | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ + | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ + | grep 'edit.php?' \ + | grep $top_domain)" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the top domain we are looking for. # So all we should be left with is CSV of table of subdomains we are # interested in. - + # Now we have to read through this table and extract the data we need IFS=$'\n' found=0 - for line in $subdomain_csv - do + for line in $subdomain_csv; do tmp="$(echo $line | cut -d ',' -f 1)" if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then # this line will contain DNSdomainid for the top_domain @@ -97,7 +95,7 @@ dns_freedns_add() { if [ "$DNSname" = "$fulldomain" -a "$DNStype" = "TXT" ]; then tmp=${dns_href#*=} url=${tmp%%>*} - DNSdataid=${url#*data_id=} + DNSdataid=${url#*data_id=} # Now get current value for the TXT record. This method may # produce inaccurate results as the value field is truncated # on this webpage. To get full value we would need to load @@ -174,10 +172,10 @@ dns_freedns_rm() { _info "Delete TXT record using FreeDNS" _debug "fulldomain: $fulldomain" _debug "txtvalue: $txtvalue" - + # Need to read cookie from conf file again in case new value set # during login to FreeDNS when TXT record was created. - + #TODO acme.sh does not have a _readaccountconf() fuction FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" _debug "FreeDNS login cookies: $FREEDNS_COOKIE" @@ -189,25 +187,24 @@ dns_freedns_rm() { # Now convert the tables in the HTML to CSV. This litte gem from # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo $htmlpage | - grep -i -e ']*>/\n/Ig' | - sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' | - sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' | - sed 's/<\/T[DH][^>]*>]*>/,/Ig' | - grep 'edit.php?' | - grep $fulldomain)" + subdomain_csv="$(echo $htmlpage \ + | grep -i -e ']*>/\n/Ig' \ + | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ + | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ + | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ + | grep 'edit.php?' \ + | grep $fulldomain)" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the domain name we are looking for. # So all we should be left with is CSV of table of subdomains we are # interested in. - + # Now we have to read through this table and extract the data we need IFS=$'\n' - for line in $subdomain_csv - do + for line in $subdomain_csv; do dns_href="$(echo $line | cut -d ',' -f 2)" tmp=${dns_href#*>} DNSname=${tmp%%<*} @@ -221,19 +218,19 @@ dns_freedns_rm() { tmp=${tmp#"} DNSvalue=${tmp%"} _debug "DNSvalue: $DNSvalue" -# if [ "$DNSvalue" = "$txtvalue" ]; then - # Testing value match fails. Website is truncating the value - # field. So for now we will assume that there is only one TXT - # field for the sub domain and just delete it. Currently this - # is a safe assumption. - unset IFS - _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid - return $? -# fi + # if [ "$DNSvalue" = "$txtvalue" ]; then + # Testing value match fails. Website is truncating the value + # field. So for now we will assume that there is only one TXT + # field for the sub domain and just delete it. Currently this + # is a safe assumption. + unset IFS + _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid + return $? + # fi fi done unset IFS - + # If we get this far we did not find a match. # Not necessarily an error, but log anyway. _debug2 "$subdomain_csv" @@ -250,18 +247,18 @@ _freedns_login() { username="$1" password="$2" url="https://freedns.afraid.org/zc.php?step=2" - + _debug "Login to FreeDNS as user $username" - + htmlpage="$(_post "username=$(_freedns_urlencode "$username")&password=$(_freedns_urlencode "$password")&submit=Login&action=auth" "$url")" - + if [ "$?" != "0" ]; then _err "FreeDNS login failed for user $username bad RC from _post" return 1 fi cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" - + # if cookies is not empty then logon successful if [ -z "$cookies" ]; then _debug "$htmlpage" @@ -281,7 +278,7 @@ _freedns_retrieve_subdomain_page() { url="https://freedns.afraid.org/subdomain/" _debug "Retrieve subdmoain page from FreeDNS" - + htmlpage="$(_get "$url")" if [ "$?" != "0" ]; then @@ -293,10 +290,10 @@ _freedns_retrieve_subdomain_page() { _err "FreeDNS returned empty subdomain page" return 1 fi - + _debug2 "$htmlpage" - printf "%s" "$htmlpage" + printf "%s" "$htmlpage" return 0 } @@ -308,14 +305,14 @@ _freedns_add_txt_record() { subdomain="$3" value="$(_freedns_urlencode "$4")" url="http://freedns.afraid.org/subdomain/save.php?step=2" - + htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")" if [ "$?" != "0" ]; then _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post" return 1 fi - + if ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then _debug "$htmlpage" _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" @@ -355,11 +352,11 @@ _freedns_delete_txt_record() { _freedns_urlencode() { # urlencode length="${#1}" - for (( i = 0; i < length; i++ )); do + for ((i = 0; i < length; i++)); do c="${1:i:1}" case $c in [a-zA-Z0-9.~_-]) printf "$c" ;; - *) printf '%%%02X' "'$c" + *) printf '%%%02X' "'$c" ;; esac done } From 0efdf5e992ae4fd7dd9e32cb33e3cc930cb9a579 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 28 Jan 2017 22:28:52 -0500 Subject: [PATCH 08/17] More travis/spellcheck cleanup --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 6a4bc197..e54c590a 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -285,7 +285,7 @@ _freedns_retrieve_subdomain_page() { _err "FreeDNS retrieve subdomins failed bad RC from _get" return 1 fi - + if [ -z "$htmlpage" ]; then _err "FreeDNS returned empty subdomain page" return 1 From 23a6ce27955c5704ce8620be130e5facbf6757de Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 28 Jan 2017 23:57:40 -0500 Subject: [PATCH 09/17] More code cleanup and remove use of IFS for line splitting. --- dnsapi/dns_freedns.sh | 86 +++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index e54c590a..6c19fba9 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -6,13 +6,13 @@ #returns 0 means success, otherwise error. # #Author: David Kerr -#Report Bugs here: https://github.com/Neilpang/acme.sh +#Report Bugs here: https://github.com/dkerr64/acme.sh # ######## Public functions ##################### # Export FreeDNS userid and password in folowing variables... -# FREEDNS_USER=username -# FREEDNS_PASSWORD=password +# FREEDNS_User=username +# FREEDNS_Password=password # login cookie is saved in acme account config file so userid / pw # need to be set only when changed. @@ -25,15 +25,15 @@ dns_freedns_add() { _debug "fulldomain: $fulldomain" _debug "txtvalue: $txtvalue" - if [ -z "$FREEDNS_USER" ] || [ -z "$FREEDNS_PASSWORD" ]; then + if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then if [ -z "$FREEDNS_COOKIE" ]; then _err "You did not specify the FreeDNS username and password yet." - _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." + _err "Please export as FREEDNS_User / FREEDNS_Password and try again." return 1 fi using_cached_cookies="true" else - FREEDNS_COOKIE="$(_freedns_login $FREEDNS_USER $FREEDNS_PASSWORD)" + FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")" if [ -z "$FREEDNS_COOKIE" ]; then return 1 fi @@ -44,22 +44,22 @@ dns_freedns_add() { _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" - htmlpage="$(_freedns_retrieve_subdomain_page $FREEDNS_COOKIE)" + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then _err "Has your FreeDNS username and password channged? If so..." - _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." + _err "Please export as FREEDNS_User / FREEDNS_Password and try again." fi return 1 fi # split our full domain name into two parts... - top_domain="$(echo $fulldomain | rev | cut -d. -f -2 | rev)" - sub_domain="$(echo $fulldomain | rev | cut -d. -f 3- | rev)" + top_domain="$(echo "$fulldomain" | rev | cut -d. -f -2 | rev)" + sub_domain="$(echo "$fulldomain" | rev | cut -d. -f 3- | rev)" # Now convert the tables in the HTML to CSV. This litte gem from # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo $htmlpage \ + subdomain_csv="$(echo "$htmlpage" \ | grep -i -e ']*>\|<\/\?T[DH][^>]*>$//Ig' \ | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ | grep 'edit.php?' \ - | grep $top_domain)" + | grep "$top_domain")" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the top domain we are looking for. # So all we should be left with is CSV of table of subdomains we are # interested in. # Now we have to read through this table and extract the data we need - IFS=$'\n' + lines=$(echo "$subdomain_csv" | wc -l) + nl=' +' + i=0 found=0 - for line in $subdomain_csv; do - tmp="$(echo $line | cut -d ',' -f 1)" + while [ $i -lt $lines ]; do + ((i++)) + line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" + tmp="$(echo "$line" | cut -d ',' -f 1)" if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then # this line will contain DNSdomainid for the top_domain - tmp="$(echo $line | cut -d ',' -f 2)" + tmp="$(echo "$line" | cut -d ',' -f 2)" url=${tmp#*=} url=${url%%>*} DNSdomainid=${url#*domain_id=} found=1 else # lines contain DNS records for all subdomains - dns_href="$(echo $line | cut -d ',' -f 2)" + dns_href="$(echo "$line" | cut -d ',' -f 2)" tmp=${dns_href#*>} DNSname=${tmp%%<*} - DNStype="$(echo $line | cut -d ',' -f 3)" - if [ "$DNSname" = "$fulldomain" -a "$DNStype" = "TXT" ]; then + DNStype="$(echo "$line" | cut -d ',' -f 3)" + if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then tmp=${dns_href#*=} url=${tmp%%>*} DNSdataid=${url#*data_id=} @@ -101,7 +106,7 @@ dns_freedns_add() { # on this webpage. To get full value we would need to load # another page. However we don't really need this so long as # there is only one TXT record for the acme chalenge subdomain. - tmp="$(echo $line | cut -d ',' -f 4)" + tmp="$(echo "$line" | cut -d ',' -f 4)" # strip the html double-quotes off the value tmp=${tmp#"} DNSvalue=${tmp%"} @@ -119,7 +124,6 @@ dns_freedns_add() { fi fi done - unset IFS _debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid" _debug "DNSvalue: $DNSvalue" @@ -137,7 +141,7 @@ dns_freedns_add() { # If data ID is empty then specific subdomain does not exist yet, need # to create it this should always be the case as the acme client # deletes the entry after domain is validated. - _freedns_add_txt_record $FREEDNS_COOKIE $DNSdomainid $sub_domain "$txtvalue" + _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" return $? else if [ "$txtvalue" = "$DNSvalue" ]; then @@ -152,10 +156,10 @@ dns_freedns_add() { return 0 else # Delete the old TXT record (with the wrong value) - _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid + _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" if [ "$?" = "0" ]; then # And add in new TXT record with the value provided - _freedns_add_txt_record $FREEDNS_COOKIE $DNSdomainid $sub_domain "$txtvalue" + _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" fi return $? fi @@ -180,14 +184,14 @@ dns_freedns_rm() { FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" _debug "FreeDNS login cookies: $FREEDNS_COOKIE" - htmlpage="$(_freedns_retrieve_subdomain_page $FREEDNS_COOKIE)" + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" if [ "$?" != "0" ]; then return 1 fi # Now convert the tables in the HTML to CSV. This litte gem from # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo $htmlpage \ + subdomain_csv="$(echo "$htmlpage" \ | grep -i -e ']*>\|<\/\?T[DH][^>]*>$//Ig' \ | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ | grep 'edit.php?' \ - | grep $fulldomain)" + | grep "$fulldomain")" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the domain name we are looking for. # So all we should be left with is CSV of table of subdomains we are # interested in. # Now we have to read through this table and extract the data we need - IFS=$'\n' - for line in $subdomain_csv; do - dns_href="$(echo $line | cut -d ',' -f 2)" + lines=$(echo "$subdomain_csv" | wc -l) + nl=' +' + i=0 + found=0 + while [ $i -lt $lines ]; do + ((i++)) + line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" + dns_href="$(echo "$line" | cut -d ',' -f 2)" tmp=${dns_href#*>} DNSname=${tmp%%<*} - DNStype="$(echo $line | cut -d ',' -f 3)" - if [ "$DNSname" = "$fulldomain" -a "$DNStype" = "TXT" ]; then + DNStype="$(echo "$line" | cut -d ',' -f 3)" + if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then tmp=${dns_href#*=} url=${tmp%%>*} DNSdataid=${url#*data_id=} - tmp="$(echo $line | cut -d ',' -f 4)" + tmp="$(echo "$line" | cut -d ',' -f 4)" # strip the html double-quotes off the value tmp=${tmp#"} DNSvalue=${tmp%"} @@ -223,13 +233,11 @@ dns_freedns_rm() { # field. So for now we will assume that there is only one TXT # field for the sub domain and just delete it. Currently this # is a safe assumption. - unset IFS - _freedns_delete_txt_record $FREEDNS_COOKIE $DNSdataid + _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" return $? # fi fi done - unset IFS # If we get this far we did not find a match. # Not necessarily an error, but log anyway. @@ -274,7 +282,7 @@ _freedns_login() { # echo page retrieved (html) # returns 0 success _freedns_retrieve_subdomain_page() { - _H1="Cookie:$1" + export _H1="Cookie:$1" url="https://freedns.afraid.org/subdomain/" _debug "Retrieve subdmoain page from FreeDNS" @@ -300,7 +308,7 @@ _freedns_retrieve_subdomain_page() { # usage _freedns_add_txt_record login_cookies domain_id subdomain value # returns 0 success _freedns_add_txt_record() { - _H1="Cookie:$1" + export _H1="Cookie:$1" domain_id="$2" subdomain="$3" value="$(_freedns_urlencode "$4")" @@ -325,7 +333,7 @@ _freedns_add_txt_record() { # usage _freedns_delete_txt_record login_cookies data_id # returns 0 success _freedns_delete_txt_record() { - _H1="Cookie:$1" + export _H1="Cookie:$1" data_id="$2" url="https://freedns.afraid.org/subdomain/delete2.php" From ea8dfd4a3e638dc57422b5e8734c9694278705f0 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 00:05:59 -0500 Subject: [PATCH 10/17] Change from ((i++)) to i=$(_math $i + 1) --- dnsapi/dns_freedns.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 6c19fba9..c41218e1 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -81,8 +81,8 @@ dns_freedns_add() { i=0 found=0 while [ $i -lt $lines ]; do - ((i++)) - line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" + i=$(_math $i + 1 ) + line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" tmp="$(echo "$line" | cut -d ',' -f 1)" if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then # this line will contain DNSdomainid for the top_domain @@ -213,8 +213,8 @@ dns_freedns_rm() { i=0 found=0 while [ $i -lt $lines ]; do - ((i++)) - line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" + i=$(_math $i + 1 ) + line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" dns_href="$(echo "$line" | cut -d ',' -f 2)" tmp=${dns_href#*>} DNSname=${tmp%%<*} From a367f3737b01f298a23097481059cd379508288c Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 13:26:37 -0500 Subject: [PATCH 11/17] Remove some bash specific string handling, replace with sed. Plus some more code cleanup. --- dnsapi/dns_freedns.sh | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index c41218e1..8e77dffd 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -75,13 +75,13 @@ dns_freedns_add() { # interested in. # Now we have to read through this table and extract the data we need - lines=$(echo "$subdomain_csv" | wc -l) + lines="$(echo "$subdomain_csv" | wc -l)" nl=' ' i=0 found=0 - while [ $i -lt $lines ]; do - i=$(_math $i + 1 ) + while [ "$i" -lt "$lines" ]; do + i="$(_math "$i" + 1)" line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" tmp="$(echo "$line" | cut -d ',' -f 1)" if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then @@ -93,23 +93,16 @@ dns_freedns_add() { found=1 else # lines contain DNS records for all subdomains - dns_href="$(echo "$line" | cut -d ',' -f 2)" - tmp=${dns_href#*>} - DNSname=${tmp%%<*} + DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" DNStype="$(echo "$line" | cut -d ',' -f 3)" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - tmp=${dns_href#*=} - url=${tmp%%>*} - DNSdataid=${url#*data_id=} + DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" # Now get current value for the TXT record. This method may - # produce inaccurate results as the value field is truncated + # not produce accurate results as the value field is truncated # on this webpage. To get full value we would need to load - # another page. However we don't really need this so long as + # another page. However we don't really need this so long as # there is only one TXT record for the acme chalenge subdomain. - tmp="$(echo "$line" | cut -d ',' -f 4)" - # strip the html double-quotes off the value - tmp=${tmp#"} - DNSvalue=${tmp%"} + DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" if [ $found != 0 ]; then break # we are breaking out of the loop at the first match of DNS name @@ -207,26 +200,19 @@ dns_freedns_rm() { # interested in. # Now we have to read through this table and extract the data we need - lines=$(echo "$subdomain_csv" | wc -l) + lines="$(echo "$subdomain_csv" | wc -l)" nl=' ' i=0 found=0 - while [ $i -lt $lines ]; do - i=$(_math $i + 1 ) + while [ "$i" -lt "$lines" ]; do + i="$(_math "$i" + 1)" line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" - dns_href="$(echo "$line" | cut -d ',' -f 2)" - tmp=${dns_href#*>} - DNSname=${tmp%%<*} + DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" DNStype="$(echo "$line" | cut -d ',' -f 3)" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - tmp=${dns_href#*=} - url=${tmp%%>*} - DNSdataid=${url#*data_id=} - tmp="$(echo "$line" | cut -d ',' -f 4)" - # strip the html double-quotes off the value - tmp=${tmp#"} - DNSvalue=${tmp%"} + DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" + DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" _debug "DNSvalue: $DNSvalue" # if [ "$DNSvalue" = "$txtvalue" ]; then # Testing value match fails. Website is truncating the value From 0cac509626cb1bc3ae469ff91e6311ec67531a78 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 14:10:39 -0500 Subject: [PATCH 12/17] More cleanup and remove bash unique code. --- dnsapi/dns_freedns.sh | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 8e77dffd..99541def 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -82,14 +82,11 @@ dns_freedns_add() { found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" + line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" tmp="$(echo "$line" | cut -d ',' -f 1)" if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then # this line will contain DNSdomainid for the top_domain - tmp="$(echo "$line" | cut -d ',' -f 2)" - url=${tmp#*=} - url=${url%%>*} - DNSdomainid=${url#*domain_id=} + DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')" found=1 else # lines contain DNS records for all subdomains @@ -207,7 +204,7 @@ dns_freedns_rm() { found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f $i)" + line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" DNStype="$(echo "$line" | cut -d ',' -f 3)" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then @@ -346,10 +343,12 @@ _freedns_delete_txt_record() { _freedns_urlencode() { # urlencode length="${#1}" - for ((i = 0; i < length; i++)); do + i=0 + while [ "$i" -lt "$length" ]; do c="${1:i:1}" + i="$(_math "$i" + 1)" case $c in - [a-zA-Z0-9.~_-]) printf "$c" ;; + [a-zA-Z0-9.~_-]) printf '%s' "$c" ;; *) printf '%%%02X' "'$c" ;; esac done From d280e4ad6d8b1b0f16e1de8d0f87abf6bf84ff2f Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 15:09:08 -0500 Subject: [PATCH 13/17] Remove use of rev command... new method of separating top domain from subdomain. --- dnsapi/dns_freedns.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 99541def..abc3a3a6 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -44,6 +44,13 @@ dns_freedns_add() { _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" + # split our full domain name into two parts... + i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" + i="$(_math "$i" - 1)" + top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)" + i="$(_math "$i" - 1)" + sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then @@ -53,10 +60,6 @@ dns_freedns_add() { return 1 fi - # split our full domain name into two parts... - top_domain="$(echo "$fulldomain" | rev | cut -d. -f -2 | rev)" - sub_domain="$(echo "$fulldomain" | rev | cut -d. -f 3- | rev)" - # Now convert the tables in the HTML to CSV. This litte gem from # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv subdomain_csv="$(echo "$htmlpage" \ From 49e8ecd16873d76f5367ca7b8fc9a6bf5739e726 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 15:34:55 -0500 Subject: [PATCH 14/17] Implement retry to load subdomain page if first attempt fails. Sometimes FreeDNS does not reurn the subdomain page but rather returns a page regarding becoming a premium member. This usually happens after a period of inactivity. Immediately trying again returns the correct subdomain page. So, we will try twice to load the page --- dnsapi/dns_freedns.sh | 263 +++++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 118 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index abc3a3a6..f5fa7a29 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -51,85 +51,103 @@ dns_freedns_add() { i="$(_math "$i" - 1)" sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" - htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" - if [ "$?" != "0" ]; then - if [ "$using_cached_cookies" = "true" ]; then - _err "Has your FreeDNS username and password channged? If so..." - _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + # Sometimes FreeDNS does not reurn the subdomain page but rather + # returns a page regarding becoming a premium member. This usually + # happens after a period of inactivity. Immediately trying again + # returns the correct subdomain page. So, we will try twice to + # load the page and obtain our domain ID + attempts=2 + while [ "$attempts" -gt "0" ]; do + attempts="$(_math "$attempts" - 1)" + + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + _err "Has your FreeDNS username and password channged? If so..." + _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + fi + return 1 fi - return 1 - fi - # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo "$htmlpage" \ - | grep -i -e ']*>/\n/Ig' \ - | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ - | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ - | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ - | grep 'edit.php?' \ - | grep "$top_domain")" - # The above beauty ends with striping out rows that do not have an - # href to edit.php and do not have the top domain we are looking for. - # So all we should be left with is CSV of table of subdomains we are - # interested in. - - # Now we have to read through this table and extract the data we need - lines="$(echo "$subdomain_csv" | wc -l)" - nl=' + # Now convert the tables in the HTML to CSV. This litte gem from + # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv + subdomain_csv="$(echo "$htmlpage" \ + | grep -i -e ']*>/\n/Ig' \ + | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ + | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ + | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ + | grep 'edit.php?' \ + | grep "$top_domain")" + # The above beauty ends with striping out rows that do not have an + # href to edit.php and do not have the top domain we are looking for. + # So all we should be left with is CSV of table of subdomains we are + # interested in. + + # Now we have to read through this table and extract the data we need + lines="$(echo "$subdomain_csv" | wc -l)" + nl=' ' - i=0 - found=0 - while [ "$i" -lt "$lines" ]; do - i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" - tmp="$(echo "$line" | cut -d ',' -f 1)" - if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then - # this line will contain DNSdomainid for the top_domain - DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')" - found=1 - else - # lines contain DNS records for all subdomains - DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" - DNStype="$(echo "$line" | cut -d ',' -f 3)" - if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" - # Now get current value for the TXT record. This method may - # not produce accurate results as the value field is truncated - # on this webpage. To get full value we would need to load - # another page. However we don't really need this so long as - # there is only one TXT record for the acme chalenge subdomain. - DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" - if [ $found != 0 ]; then - break - # we are breaking out of the loop at the first match of DNS name - # and DNS type (if we are past finding the domainid). This assumes - # that there is only ever one TXT record for the LetsEncrypt/acme - # challenge subdomain. This seems to be a reasonable assumption - # as the acme client deletes the TXT record on successful validation. - fi + i=0 + found=0 + while [ "$i" -lt "$lines" ]; do + i="$(_math "$i" + 1)" + line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" + tmp="$(echo "$line" | cut -d ',' -f 1)" + if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then + # this line will contain DNSdomainid for the top_domain + DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')" + found=1 else - DNSname="" - DNStype="" + # lines contain DNS records for all subdomains + DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" + DNStype="$(echo "$line" | cut -d ',' -f 3)" + if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then + DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" + # Now get current value for the TXT record. This method may + # not produce accurate results as the value field is truncated + # on this webpage. To get full value we would need to load + # another page. However we don't really need this so long as + # there is only one TXT record for the acme chalenge subdomain. + DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" + if [ $found != 0 ]; then + break + # we are breaking out of the loop at the first match of DNS name + # and DNS type (if we are past finding the domainid). This assumes + # that there is only ever one TXT record for the LetsEncrypt/acme + # challenge subdomain. This seems to be a reasonable assumption + # as the acme client deletes the TXT record on successful validation. + fi + else + DNSname="" + DNStype="" + fi + fi + done + + _debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid" + _debug "DNSvalue: $DNSvalue" + + if [ -z "$DNSdomainid" ]; then + # If domain ID is empty then something went wrong (top level + # domain not found at FreeDNS). + if [ "$attempts" = "0" ]; then + # exhausted maximum retry attempts + _debug "$htmlpage" + _debug "$subdomain_csv" + _err "Domain $top_domain not found at FreeDNS" + return 1 fi + else + # break out of the 'retry' loop... we have found our domain ID + break; fi + _info "Domain $top_domain not found at FreeDNS" + _info "Retry loading subdomain page ($attempts attempts remaining)" done - _debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid" - _debug "DNSvalue: $DNSvalue" - - if [ -z "$DNSdomainid" ]; then - # If domain ID is empty then something went wrong (top level - # domain not found at FreeDNS). Cannot proceed. - _debug "$htmlpage" - _debug "$subdomain_csv" - _err "Domain $top_domain not found at FreeDNS" - return 1 - fi - if [ -z "$DNSdataid" ]; then # If data ID is empty then specific subdomain does not exist yet, need # to create it this should always be the case as the acme client @@ -172,60 +190,69 @@ dns_freedns_rm() { # Need to read cookie from conf file again in case new value set # during login to FreeDNS when TXT record was created. - - #TODO acme.sh does not have a _readaccountconf() fuction + # acme.sh does not have a _readaccountconf() fuction FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" _debug "FreeDNS login cookies: $FREEDNS_COOKIE" - htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" - if [ "$?" != "0" ]; then - return 1 - fi + # Sometimes FreeDNS does not reurn the subdomain page but rather + # returns a page regarding becoming a premium member. This usually + # happens after a period of inactivity. Immediately trying again + # returns the correct subdomain page. So, we will try twice to + # load the page and obtain our TXT record. + attempts=2 + while [ "$attempts" -gt "0" ]; do + attempts="$(_math "$attempts" - 1)" + + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + if [ "$?" != "0" ]; then + return 1 + fi - # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo "$htmlpage" \ - | grep -i -e ']*>/\n/Ig' \ - | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ - | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ - | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ - | grep 'edit.php?' \ - | grep "$fulldomain")" - # The above beauty ends with striping out rows that do not have an - # href to edit.php and do not have the domain name we are looking for. - # So all we should be left with is CSV of table of subdomains we are - # interested in. - - # Now we have to read through this table and extract the data we need - lines="$(echo "$subdomain_csv" | wc -l)" - nl=' + # Now convert the tables in the HTML to CSV. This litte gem from + # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv + subdomain_csv="$(echo "$htmlpage" \ + | grep -i -e ']*>/\n/Ig' \ + | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ + | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ + | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ + | grep 'edit.php?' \ + | grep "$fulldomain")" + # The above beauty ends with striping out rows that do not have an + # href to edit.php and do not have the domain name we are looking for. + # So all we should be left with is CSV of table of subdomains we are + # interested in. + + # Now we have to read through this table and extract the data we need + lines="$(echo "$subdomain_csv" | wc -l)" + nl=' ' - i=0 - found=0 - while [ "$i" -lt "$lines" ]; do - i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" - DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" - DNStype="$(echo "$line" | cut -d ',' -f 3)" - if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" - DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" - _debug "DNSvalue: $DNSvalue" - # if [ "$DNSvalue" = "$txtvalue" ]; then - # Testing value match fails. Website is truncating the value - # field. So for now we will assume that there is only one TXT - # field for the sub domain and just delete it. Currently this - # is a safe assumption. - _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" - return $? - # fi - fi + i=0 + found=0 + while [ "$i" -lt "$lines" ]; do + i="$(_math "$i" + 1)" + line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" + DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" + DNStype="$(echo "$line" | cut -d ',' -f 3)" + if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then + DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" + DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" + _debug "DNSvalue: $DNSvalue" + # if [ "$DNSvalue" = "$txtvalue" ]; then + # Testing value match fails. Website is truncating the value + # field. So for now we will assume that there is only one TXT + # field for the sub domain and just delete it. Currently this + # is a safe assumption. + _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" + return $? + # fi + fi + done done - # If we get this far we did not find a match. + # If we get this far we did not find a match (after two attempts) # Not necessarily an error, but log anyway. _debug2 "$subdomain_csv" _info "Cannot delete TXT record for $fulldomain/$txtvalue. Does not exist at FreeDNS" From 76b7731998552acf0db4cf3d217f1ba857b62f1f Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 15:43:58 -0500 Subject: [PATCH 15/17] Remove bash unique string manipulation, replace with cut function. --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index f5fa7a29..2e89f75a 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -375,8 +375,8 @@ _freedns_urlencode() { length="${#1}" i=0 while [ "$i" -lt "$length" ]; do - c="${1:i:1}" i="$(_math "$i" + 1)" + c="$(echo "$1" | cut -c "$i")" case $c in [a-zA-Z0-9.~_-]) printf '%s' "$c" ;; *) printf '%%%02X' "'$c" ;; From 9c9ecd48136d9462fa3155d3a956418854f05073 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 18:14:21 -0500 Subject: [PATCH 16/17] travis spellcheck fix --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 2e89f75a..90485525 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -142,7 +142,7 @@ dns_freedns_add() { fi else # break out of the 'retry' loop... we have found our domain ID - break; + break fi _info "Domain $top_domain not found at FreeDNS" _info "Retry loading subdomain page ($attempts attempts remaining)" From aebbb1ae570ea8061e496baced1b97630438a427 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 29 Jan 2017 21:24:31 -0500 Subject: [PATCH 17/17] Revert FREEDNS userid and password variables to all uppercase As these are exported variables by convention they should be uppercase. Travis/Spellcheck fails because they are not uppercase and it therefore expects them to be defined or set before use. --- dnsapi/dns_freedns.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 90485525..b849c1f6 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -11,8 +11,8 @@ ######## Public functions ##################### # Export FreeDNS userid and password in folowing variables... -# FREEDNS_User=username -# FREEDNS_Password=password +# FREEDNS_USER=username +# FREEDNS_PASSWORD=password # login cookie is saved in acme account config file so userid / pw # need to be set only when changed. @@ -25,15 +25,15 @@ dns_freedns_add() { _debug "fulldomain: $fulldomain" _debug "txtvalue: $txtvalue" - if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then + if [ -z "$FREEDNS_USER" ] || [ -z "$FREEDNS_PASSWORD" ]; then if [ -z "$FREEDNS_COOKIE" ]; then _err "You did not specify the FreeDNS username and password yet." - _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." return 1 fi using_cached_cookies="true" else - FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")" + FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_USER" "$FREEDNS_PASSWORD")" if [ -z "$FREEDNS_COOKIE" ]; then return 1 fi @@ -64,7 +64,7 @@ dns_freedns_add() { if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then _err "Has your FreeDNS username and password channged? If so..." - _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + _err "Please export as FREEDNS_USER / FREEDNS_PASSWORD and try again." fi return 1 fi