You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

506 lines
21 KiB

  1. #!/usr/bin/env sh
  2. #
  3. # Author: Daniel Harp
  4. #
  5. # ## PLEASE READ THE WARNINGS AND ISSUES BELOW BEFORE USING THIS SCRIPT. These were based on the dynadot api
  6. # current as of FEB-26-2023
  7. #
  8. # WARNING #1: During development, significant issues were experienced utilizing the dynadot apis to implement
  9. # the acme protocol, to the extent that the acme.sh automated test scripts could not repeatably and
  10. # reliably complete successfully, as explained below. If you are attempting to create a single certificate
  11. # for a single domain, this will likely work well. A single wildcard certificate is likely to work most
  12. # of the time. As the number of certificates requested under a single primary domain go up, the highly
  13. # likelihood becomes that failures will be experienced. Please read through these issues and the settings
  14. # available to mitigate these issues.
  15. #
  16. # WARNING #2: The dynadot api only allows "GET" requests. Your api token will be passed as a URL parameter.
  17. # While this doesn't inherently expose risk, this is not following security best practices as often end
  18. # up in server logs. There is currently no other option to interact with the dynadot api.
  19. #
  20. # API Issues:
  21. # Dynadot did not provide an api call to add or remove a specific subdomain, rather they provided a "set_dns2"
  22. # command that updates the entire domain configuration. That command does have an "append" option, but when
  23. # attempting to use it in the append mode, it still required a main domain record to be added or the API
  24. # would reject the request. As only TXT records for subdomains need to be appended, the append mode could
  25. # not be used.
  26. #
  27. # To utilize the "set_dns2" command, without using the append mode, the entire domain configuration had to be
  28. # provided. To add a TXT record, the current configuration was read, the new TXT record was added to that
  29. # configuration, then the entire configuration was sent to the set_dns2 api call, resulting in the added record.
  30. # Deleting a record is this same process: read config, delete specific TXT record, send the entire configuration.
  31. #
  32. # The problem with sending the entire configuration rather than being able to append a single record is the
  33. # propogation of the transactions now must complete in order. For instance if the domain currently only has a
  34. # single A record, and we need to add two text records( TXT1 & TXT2 ) the first "set_dns2" call will send a
  35. # configuration with the A record and the first TXT record, call_1=(A, TXT1). The second add call sends a
  36. # configuration that contains all three records, call2=(A, TXT1, TXT2). As these proprogate through the dynadot
  37. # system, if any part of the system applies call_2 first, it will have all three records, but then when it
  38. # processes call_1, it will end up dropping TXT2 as it applies it as the entire configuration.
  39. #
  40. # To work reliably, after applying ANY change (add or remove) to a domain, you must wait for the change to
  41. # propogate through their system entirely prior to applying any new or additional change. This was observed
  42. # behavior, not anything documented or provided by dynadot.
  43. #
  44. # The acme.sh --dnssleep does not do this. If multiple TXT records are required it will call add twice serially
  45. # without waiting, then will apply the sleep before verification. It also does not sleep after a remove. This
  46. # means moving on to additional certificates (or as observed moving on to the next set of test cases) can cause
  47. # problems as the remove has not fully propogated. Two options are available:
  48. # export DYNADOT_ADD_DNS_SLEEP=1800
  49. # export DYNADOT_REMOVE_DNS_SLEEP=1800
  50. # These are sleep times, in seconds, that will be applied immediately after EVERY ADD or REMOVE.
  51. # If you are only requesting one certificate, you likely do not need the wait after the remove. If you are running
  52. # any more DNS updates/changes to the domain after the script runs, you do need this wait to ensure it completes
  53. # first.
  54. # Note that for a wildcard certificate, two text records are added. DYNADOT_ADD_DNS_SLEEP is applied after each
  55. # so the total wait before verification will be doubled.
  56. # Dynadot notes that propogation times can range from 5 minutes to 48 hours!!! 30 minutes (1800 second) waits
  57. # appeared to consistently be sufficient. Sleep times 15 minutes or less were found to consistently cause
  58. # failures in the tests.
  59. # The "docker" test stage runs through 8 different linux containers, with at least 2 adds and 2 removes for each.
  60. # That's at least 32 "set_dns2" calls. At 30 minutes for propogation for each, that's 16 hours of just sleep time.
  61. # An additional option was then added:
  62. # export DYNADOTAPI_SKIP_REMOVE=SKIP
  63. # This option skips the removals, allowing one to clean up the TXT records by hand later, but in doing so cuts the
  64. # number of necessary operations for the tests in half. Unfortunately, that's only down to 8 hours, and GitHub
  65. # aborts the process at 6 hours, so a successful test run was never completed.
  66. #
  67. # An additional issue arose with periodic failures from the dynadot api:
  68. # {"ListDomainInfoResponse":{"ResponseCode":"-1","Status":"error","Error":"problem with connection to main server"}}
  69. # This error was reported to dynadot support. The response received was:
  70. # "sometimes we are maintaining main server so you will see such errors like connection issue. Just try again in a
  71. # few minutes."
  72. #
  73. # To accomodate this error, two additional configuration options were added:
  74. # export DYNADOTAPI_API_RETRIES=5
  75. # export DYNADOTAPI_RETRY_SLEEP=60
  76. # On seeing an error from the dynadot api, up to $DYNADOTAPI_API_RETRIES attempts will be made before giving up.
  77. # DYNADOTAPI_RETRY_SLEEP specifies the number of seconds to wait between attempts.
  78. # However, when seeing this error, it consistently lasted longer than the wait time specified, so these two options
  79. # never accomplished their goal. Instead a new test run would be attemped hours later (or the next day/night
  80. # altogether).
  81. #
  82. # Usage:
  83. # Create a dynadot api key under the web portal "Tools > API" and export that for this script:
  84. # export DYNADOTAPI_Token="ASDF1234ASDF1234"
  85. #
  86. # export DYNADOT_ADD_DNS_SLEEP=1800 (optional) Default:empty. Recommended value: at least 1800.
  87. # export DYNADOT_REMOVE_DNS_SLEEP=1800 (optional) Default:empty. Recommended:
  88. # If only a single domain certificate will be requested, this may be left blank.
  89. # If requesting multiple certificates, recommended setting is at least 1800, or use DYNADOTAPI_SKIP_REMOVE
  90. #
  91. # export DYNADOTAPI_SKIP_REMOVE=SKIP (optional) Recommended if multiple certificates will be requested to reduce the
  92. # number of api calls required (but TXT records will need to be removed manually).
  93. #
  94. # The following two options are available but are not recommended as they did not prove useful during testing.
  95. # export DYNADOTAPI_API_RETRIES=1 (optional) Number of times to attempt Dynadot API call until success (default=1)
  96. # export DYNADOTAPI_RETRY_SLEEP=30 (optional) Seconds to sleep between retry attempts (default: 30 seconds)
  97. #
  98. DYNADOT_Api=https://api.dynadot.com/api3.json
  99. ######## Public functions #####################
  100. #Usage: dns_dynadot_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  101. dns_dynadot_add() {
  102. fulldomain=$1
  103. txtvalue=$2
  104. _info "DYNADOT: Adding TXT Record"
  105. _debug "DYNADOT: fulldomain: $fulldomain"
  106. _debug "DYNADOT: txtvalue: $txtvalue"
  107. DYNADOTAPI_Token="${DYNADOTAPI_Token:-$(_readaccountconf_mutable DYNADOTAPI_Token)}"
  108. if [ -z "$DYNADOTAPI_Token" ]; then
  109. _err "You don't specify dynadot api token key."
  110. _err "Please create your token and try again."
  111. return 1
  112. fi
  113. _saveaccountconf_mutable DYNADOTAPI_Token "$DYNADOTAPI_Token"
  114. DYNADOTAPI_API_RETRIES="${DYNADOTAPI_API_RETRIES:-$(_readaccountconf_mutable DYNADOTAPI_API_RETRIES)}"
  115. if [ -z "$DYNADOTAPI_API_RETRIES" ]; then
  116. DYNADOTAPI_API_RETRIES=1
  117. fi
  118. _saveaccountconf_mutable DYNADOTAPI_API_RETRIES "$DYNADOTAPI_API_RETRIES"
  119. DYNADOTAPI_RETRY_SLEEP="${DYNADOTAPI_RETRY_SLEEP:-$(_readaccountconf_mutable DYNADOTAPI_RETRY_SLEEP)}"
  120. if [ -z "$DYNADOTAPI_RETRY_SLEEP" ]; then
  121. DYNADOTAPI_RETRY_SLEEP=30
  122. fi
  123. _saveaccountconf_mutable DYNADOTAPI_RETRY_SLEEP "$DYNADOTAPI_RETRY_SLEEP"
  124. DYNADOT_ADD_DNS_SLEEP="${DYNADOT_ADD_DNS_SLEEP:-$(_readaccountconf_mutable DYNADOT_ADD_DNS_SLEEP)}"
  125. if [ "$DYNADOT_ADD_DNS_SLEEP" ]; then
  126. _saveaccountconf_mutable DYNADOT_ADD_DNS_SLEEP "$DYNADOT_ADD_DNS_SLEEP"
  127. fi
  128. _debug "DYNADOT: Detecting root domain"
  129. if ! _get_root "$fulldomain"; then
  130. _err "DYNADOT: Root domain not found"
  131. return 1
  132. fi
  133. _debug "DYNADOT: _domain: $_domain"
  134. _debug "DYNADOT: _sub_domain: $_sub_domain"
  135. #Get the current domain settings
  136. if ! _dynadot_get_dns; then
  137. _err "cannot get domain current settings"
  138. return 1
  139. fi
  140. if _contains "$response" "$txtvalue"; then
  141. _info "DYNADOT: Already exists, Skipping ADD"
  142. return 0
  143. fi
  144. if ! _contains "$response" '"Type":"Dynadot DNS"'; then
  145. _err "Only Dynadot domains with Type 'Dynadot DNS' are supported."
  146. return 1
  147. fi
  148. if ! _dynadot_add_txt_entry; then
  149. _err "DYNADOT: Add txt record failed."
  150. return 1
  151. fi
  152. if [ "$DYNADOT_ADD_DNS_SLEEP" ]; then
  153. _debug "DYNADOT: Test Mode. Sleeping $DYNADOT_ADD_DNS_SLEEP seconds."
  154. sleep "$DYNADOT_ADD_DNS_SLEEP"
  155. fi
  156. _info "DYNADOT: TXT record added successfully"
  157. return 0
  158. }
  159. #Usage: dns_dynadot_rm fulldomain txtvalue
  160. #Remove the txt record after validation.
  161. dns_dynadot_rm() {
  162. fulldomain=$1
  163. txtvalue=$2
  164. _info "DYNADOT: Removing TXT Record"
  165. _debug "DYNADOT: fulldomain: $fulldomain"
  166. _debug "DYNADOT: txtvalue: $txtvalue"
  167. DYNADOTAPI_SKIP_REMOVE="${DYNADOTAPI_SKIP_REMOVE:-$(_readaccountconf_mutable DYNADOTAPI_SKIP_REMOVE)}"
  168. if [ "$DYNADOTAPI_SKIP_REMOVE" ]; then
  169. _saveaccountconf_mutable DYNADOTAPI_SKIP_REMOVE "$DYNADOTAPI_SKIP_REMOVE"
  170. fi
  171. if [ "$DYNADOTAPI_SKIP_REMOVE" = "SKIP" ]; then
  172. _info "DYNADOT: Skipping removal. Please remove manually."
  173. return 0
  174. fi
  175. DYNADOTAPI_Token="${DYNADOTAPI_Token:-$(_readaccountconf_mutable DYNADOTAPI_Token)}"
  176. if [ -z "$DYNADOTAPI_Token" ]; then
  177. _err "You don't specify dynadot api token key."
  178. _err "Please create your token and try again."
  179. return 1
  180. fi
  181. _saveaccountconf_mutable DYNADOTAPI_Token "$DYNADOTAPI_Token"
  182. DYNADOTAPI_API_RETRIES="${DYNADOTAPI_API_RETRIES:-$(_readaccountconf_mutable DYNADOTAPI_API_RETRIES)}"
  183. if [ -z "$DYNADOTAPI_API_RETRIES" ]; then
  184. DYNADOTAPI_API_RETRIES=1
  185. fi
  186. _saveaccountconf_mutable DYNADOTAPI_API_RETRIES "$DYNADOTAPI_API_RETRIES"
  187. DYNADOTAPI_RETRY_SLEEP="${DYNADOTAPI_RETRY_SLEEP:-$(_readaccountconf_mutable DYNADOTAPI_RETRY_SLEEP)}"
  188. if [ -z "$DYNADOTAPI_RETRY_SLEEP" ]; then
  189. DYNADOTAPI_RETRY_SLEEP=30
  190. fi
  191. _saveaccountconf_mutable DYNADOTAPI_RETRY_SLEEP "$DYNADOTAPI_RETRY_SLEEP"
  192. DYNADOT_REMOVE_DNS_SLEEP="${DYNADOT_REMOVE_DNS_SLEEP:-$(_readaccountconf_mutable DYNADOT_REMOVE_DNS_SLEEP)}"
  193. if [ "$DYNADOT_REMOVE_DNS_SLEEP" ]; then
  194. _saveaccountconf_mutable DYNADOT_REMOVE_DNS_SLEEP "$DYNADOT_REMOVE_DNS_SLEEP"
  195. fi
  196. _debug "DYNADOT: First detect the root domain"
  197. if ! _get_root "$fulldomain"; then
  198. _err "DYNADOT: Root domain not found"
  199. return 1
  200. fi
  201. _debug "DYNADOT: _domain: $_domain"
  202. _debug "DYNADOT: _sub_domain: $_sub_domain"
  203. #Get the current domain settings
  204. if ! _dynadot_get_dns; then
  205. _err "DYNADOT: cannot get domain current settings"
  206. return 1
  207. fi
  208. if ! _contains "$response" "$txtvalue"; then
  209. _info "DYNADOT: Record not found, skipping REMOVE"
  210. return 0
  211. fi
  212. if ! _dynadot_rm_txt_entry; then
  213. _err "DYNADOT: Remove txt record failed."
  214. return 1
  215. fi
  216. if [ "$DYNADOT_REMOVE_DNS_SLEEP" ]; then
  217. _debug "DYNADOT: Test Mode. Sleeping $DYNADOT_REMOVE_DNS_SLEEP seconds."
  218. sleep "$DYNADOT_REMOVE_DNS_SLEEP"
  219. fi
  220. _info "DYNADOT: TXT record removed successfully"
  221. return 0
  222. }
  223. #################### Private functions below ##################################
  224. #_acme-challenge.www.domain.com
  225. #returns
  226. # _sub_domain=_acme-challenge.www
  227. # _domain=domain.com
  228. _get_root() {
  229. domain=$1
  230. i=1
  231. p=1
  232. _debug "DYNADOT: Lookup root domain for: $domain"
  233. if ! _dynadot_list_domain; then
  234. _debug "DYNADOT: list_domain failed"
  235. return 1
  236. fi
  237. while true; do
  238. h=$(printf "%s" "$domain" | cut -d . -f $i-100)
  239. _debug h "$h"
  240. if [ -z "$h" ]; then
  241. #not valid
  242. return 1
  243. fi
  244. if _contains "$response" "\"Name\":\"$h\""; then
  245. _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
  246. _domain=$h
  247. _debug "DYNADOT: Found root domain: $_domain"
  248. return 0
  249. fi
  250. p=$i
  251. i=$(_math "$i" + 1)
  252. done
  253. return 1
  254. }
  255. _dynadot_list_domain() {
  256. _dynadot_rest "command=list_domain"
  257. return $?
  258. }
  259. _dynadot_get_dns() {
  260. _dynadot_rest "command=get_dns&domain=$_domain"
  261. return $?
  262. }
  263. _dynadot_add_txt_entry() {
  264. _debug "DYNADOT: Building add command"
  265. _json="$response"
  266. _url_params=""
  267. _sub_host_cnt=$(echo "$_json" | sed 's/[x ]/-/g' | sed 's/{"Subhost"/x /g' | sed 's/[^x ]//g' | wc -w)
  268. _main_domain_cnt=$(echo "$_json" | sed 's/[x ]/-/g' | sed 's/{"RecordType"/x /g' | sed 's/[^x ]//g' | wc -w)
  269. _debug "DYNADOT: Main Domain Count: $_main_domain_cnt"
  270. _debug "DYNADOT: Sub Domain Count: $_sub_host_cnt"
  271. _ttl=$(printf "%s" "$_json" | sed -n 's/.*"TTL":"\([^"]*\)".*/\1/p')
  272. if [ "$_ttl" ]; then
  273. _url_params="$_url_params&ttl=$_ttl"
  274. fi
  275. _debug "DYNADOT: TTL: $_ttl"
  276. # Slashes interfere with our sed commands on some systems. Changing to placeholder values and will add them back in later
  277. _json=$(printf "%s" "$_json" | _json_decode | sed 's#/#----SLASH----#g' | sed 's#\\#----BSLASH----#g')
  278. _cnt="$((_sub_host_cnt - 1))"
  279. for i in $(seq 0 "$_cnt"); do
  280. _subHostArgs=$(printf "%s" "$_json" | sed 's/.*{\("Subhost":[^}]*\)}.*/\1/')
  281. _json=$(printf "%s" "$_json" | sed "s/${_subHostArgs}/--------------------/")
  282. _subHost=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"Subhost":"\([^"]*\)".*/\1/p' | _url_encode)
  283. _subHost_type=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"RecordType":"\([^"]*\)".*/\1/p' | tr '[:upper:]' '[:lower:]' | _url_encode)
  284. _subHost_value=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"Value":"\([^"]*\)".*/\1/p' | _url_encode)
  285. _subHost_valuex=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"Value2":"\([^"]*\)".*/\1/p' | _url_encode)
  286. if [ "$_subHost" ]; then
  287. _url_params="$_url_params&subdomain$i=$_subHost"
  288. fi
  289. if [ "$_subHost_type" ]; then
  290. _url_params="$_url_params&sub_record_type$i=$_subHost_type"
  291. fi
  292. if [ "$_subHost_value" ]; then
  293. _url_params="$_url_params&sub_record$i=$_subHost_value"
  294. fi
  295. if [ "$_subHost_valuex" ]; then
  296. _url_params="$_url_params&sub_recordx$i=$_subHost_valuex"
  297. fi
  298. _debug "DYNADOT: Including Sub Domain: $_subHost : $_subHost_type : $_subHost_value : $_subHost_valuex"
  299. done
  300. _cnt="$((_main_domain_cnt - 1))"
  301. for i in $(seq 0 "$_cnt"); do
  302. _mainHostArgs=$(printf "%s" "$_json" | sed 's/.*{\("RecordType":[^}]*\)}.*/\1/')
  303. _json=$(printf "%s" "$_json" | sed "s/${_mainHostArgs}/--------------------/")
  304. _mainHost_type=$(printf "%s" "$_mainHostArgs" | sed -n 's/.*"RecordType":"\([^"]*\)".*/\1/p' | tr '[:upper:]' '[:lower:]' | _url_encode)
  305. _mainHost_value=$(printf "%s" "$_mainHostArgs" | sed -n 's/.*"Value":"\([^"]*\)".*/\1/p' | _url_encode)
  306. _mainHost_valuex=$(printf "%s" "$_mainHostArgs" | sed -n 's/.*"Value2":"\([^"]*\)".*/\1/p' | _url_encode)
  307. if [ "$_mainHost_type" ]; then
  308. _url_params="$_url_params&main_record_type$i=$_mainHost_type"
  309. fi
  310. if [ "$_mainHost_value" ]; then
  311. _url_params="$_url_params&main_record$i=$_mainHost_value"
  312. fi
  313. if [ "$_mainHost_valuex" ]; then
  314. _url_params="$_url_params&main_recordx$i=$_mainHost_valuex"
  315. fi
  316. _debug "DYNADOT: Including Main Domain: $_mainHost_type : $_mainHost_value : $_mainHost_valuex"
  317. done
  318. _url_params=$(printf "%s" "$_url_params" | sed 's#----SLASH----#%2f#g' | sed 's#----BSLASH----#%5c#g')
  319. _debug "DYNADOT: Including Sub Domain $_sub_host_cnt: $_sub_domain : txt : $txtvalue"
  320. _url_params="command=set_dns2&domain=$_domain$_url_params&subdomain$_sub_host_cnt=$_sub_domain&sub_record_type$_sub_host_cnt=txt&sub_record$_sub_host_cnt=$txtvalue"
  321. _dynadot_rest "$_url_params"
  322. return $?
  323. }
  324. _dynadot_rm_txt_entry() {
  325. _debug "DYNADOT: Building remove command"
  326. _json="$response"
  327. _url_params=""
  328. _sub_host_cnt=$(echo "$_json" | sed 's/[x ]/-/g' | sed 's/{"Subhost"/x /g' | sed 's/[^x ]//g' | wc -w)
  329. _main_domain_cnt=$(echo "$_json" | sed 's/[x ]/-/g' | sed 's/{"RecordType"/x /g' | sed 's/[^x ]//g' | wc -w)
  330. _debug "DYNADOT: Main Domain Count: $_main_domain_cnt"
  331. _debug "DYNADOT: Sub Domain Count: $_sub_host_cnt"
  332. _ttl=$(printf "%s" "$_json" | sed -n 's/.*"TTL":"\([^"]*\)".*/\1/p')
  333. if [ "$_ttl" ]; then
  334. _url_params="$_url_params&ttl=$_ttl"
  335. fi
  336. _debug "DYNADOT: TTL: $_ttl"
  337. # Slashes interfere with our sed commands on some systems. Changing to placeholder values and will add them back in later
  338. _json=$(printf "%s" "$_json" | _json_decode | sed 's#/#----SLASH----#g' | sed 's#\\#----BSLASH----#g')
  339. _cnt="$((_sub_host_cnt - 1))"
  340. for i in $(seq 0 "$_cnt"); do
  341. _subHostArgs=$(printf "%s" "$_json" | sed 's/.*{\("Subhost":[^}]*\)}.*/\1/')
  342. _json=$(printf "%s" "$_json" | sed "s/${_subHostArgs}/--------------------/")
  343. _subHost=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"Subhost":"\([^"]*\)".*/\1/p' | _url_encode)
  344. _subHost_type=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"RecordType":"\([^"]*\)".*/\1/p' | tr '[:upper:]' '[:lower:]' | _url_encode)
  345. _subHost_value=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"Value":"\([^"]*\)".*/\1/p' | _url_encode)
  346. _subHost_valuex=$(printf "%s" "$_subHostArgs" | sed -n 's/.*"Value2":"\([^"]*\)".*/\1/p' | _url_encode)
  347. if [ "$_subHost_value" != "$txtvalue" ]; then
  348. if [ "$_subHost" ]; then
  349. _url_params="$_url_params&subdomain$i=$_subHost"
  350. fi
  351. if [ "$_subHost_type" ]; then
  352. _url_params="$_url_params&sub_record_type$i=$_subHost_type"
  353. fi
  354. if [ "$_subHost_value" ]; then
  355. _url_params="$_url_params&sub_record$i=$_subHost_value"
  356. fi
  357. if [ "$_subHost_valuex" ]; then
  358. _url_params="$_url_params&sub_recordx$i=$_subHost_valuex"
  359. fi
  360. _debug "DYNADOT: Including Sub Domain: $_subHost : $_subHost_type : $_subHost_value : $_subHost_valuex"
  361. else
  362. _debug "DYNADOT: Excluding Sub Domain: $_subHost : $_subHost_type : $_subHost_value : $_subHost_valuex"
  363. fi
  364. done
  365. _cnt="$((_main_domain_cnt - 1))"
  366. for i in $(seq 0 "$_cnt"); do
  367. _mainHostArgs=$(printf "%s" "$_json" | sed 's/.*{\("RecordType":[^}]*\)}.*/\1/')
  368. _json=$(printf "%s" "$_json" | sed "s/${_mainHostArgs}/--------------------/")
  369. _mainHost_type=$(printf "%s" "$_mainHostArgs" | sed -n 's/.*"RecordType":"\([^"]*\)".*/\1/p' | tr '[:upper:]' '[:lower:]' | _url_encode)
  370. _mainHost_value=$(printf "%s" "$_mainHostArgs" | sed -n 's/.*"Value":"\([^"]*\)".*/\1/p' | _url_encode)
  371. _mainHost_valuex=$(printf "%s" "$_mainHostArgs" | sed -n 's/.*"Value2":"\([^"]*\)".*/\1/p' | _url_encode)
  372. if [ "$_mainHost_type" ]; then
  373. _url_params="$_url_params&main_record_type$i=$_mainHost_type"
  374. fi
  375. if [ "$_mainHost_value" ]; then
  376. _url_params="$_url_params&main_record$i=$_mainHost_value"
  377. fi
  378. if [ "$_mainHost_valuex" ]; then
  379. _url_params="$_url_params&main_recordx$i=$_mainHost_valuex"
  380. fi
  381. _debug "DYNADOT: Including Main Domain: $_mainHost_type : $_mainHost_value : $_mainHost_valuex"
  382. done
  383. _url_params=$(printf "%s" "$_url_params" | sed 's#----SLASH----#%2f#g' | sed 's#----BSLASH----#%5c#g')
  384. _url_params="command=set_dns2&domain=$_domain$_url_params"
  385. _dynadot_rest "$_url_params"
  386. return $?
  387. }
  388. _dynadot_rest() {
  389. url_params=$1
  390. _retry_attempts="$DYNADOTAPI_API_RETRIES"
  391. _retry_sleep="$DYNADOTAPI_RETRY_SLEEP"
  392. while true; do
  393. if _dynadot_rest_call "$url_params"; then
  394. return 0
  395. fi
  396. _retry_attempts=$(_math "$_retry_attempts" - 1)
  397. if [ "${_retry_attempts}" -lt "1" ]; then
  398. _err "DYNADOT: api call failed all retry attempts."
  399. return 1
  400. fi
  401. _info "DYNADOT: api call failed. Retrying up to $_retry_attempts times. Sleeping $_retry_sleep seconds before retry."
  402. sleep "$_retry_sleep"
  403. done
  404. # We should not get to the bottom of this function
  405. return 1
  406. }
  407. _dynadot_rest_call() {
  408. url_params=$1
  409. token_trimmed=$(echo "$DYNADOTAPI_Token" | tr -d '"')
  410. _debug "DYNADOT: Calling dynadot API: $url_params"
  411. url="$DYNADOT_Api?key=$token_trimmed&$url_params"
  412. response="$(_get "$url")"
  413. if [ "$?" != "0" ]; then
  414. _err "DYNADOT: error with: $url_params"
  415. return 1
  416. fi
  417. if ! _contains "$response" '"Status":"success"'; then
  418. _err "DYNADOT: error with: $url_params"
  419. _err "$response"
  420. return 1
  421. fi
  422. _debug2 "DYNADOT: api response: $response"
  423. return 0
  424. }