dns_namecheap.sh 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. #!/usr/bin/env sh
  2. # Namecheap API
  3. # https://www.namecheap.com/support/api/intro.aspx
  4. #
  5. # Requires Namecheap API key set in
  6. #NAMECHEAP_API_KEY,
  7. #NAMECHEAP_USERNAME,
  8. #NAMECHEAP_SOURCEIP
  9. # Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
  10. ######## Public functions #####################
  11. NAMECHEAP_API="https://api.namecheap.com/xml.response"
  12. #Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  13. dns_namecheap_add() {
  14. fulldomain=$1
  15. txtvalue=$2
  16. if ! _namecheap_check_config; then
  17. _err "$error"
  18. return 1
  19. fi
  20. if ! _namecheap_set_publicip; then
  21. return 1
  22. fi
  23. _debug "First detect the root zone"
  24. if ! _get_root "$fulldomain"; then
  25. _err "invalid domain"
  26. return 1
  27. fi
  28. _debug fulldomain "$fulldomain"
  29. _debug txtvalue "$txtvalue"
  30. _debug domain "$_domain"
  31. _debug sub_domain "$_sub_domain"
  32. _set_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
  33. }
  34. #Usage: fulldomain txtvalue
  35. #Remove the txt record after validation.
  36. dns_namecheap_rm() {
  37. fulldomain=$1
  38. txtvalue=$2
  39. if ! _namecheap_set_publicip; then
  40. return 1
  41. fi
  42. if ! _namecheap_check_config; then
  43. _err "$error"
  44. return 1
  45. fi
  46. _debug "First detect the root zone"
  47. if ! _get_root "$fulldomain"; then
  48. _err "invalid domain"
  49. return 1
  50. fi
  51. _debug fulldomain "$fulldomain"
  52. _debug txtvalue "$txtvalue"
  53. _debug domain "$_domain"
  54. _debug sub_domain "$_sub_domain"
  55. _del_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
  56. }
  57. #################### Private functions below ##################################
  58. #_acme-challenge.www.domain.com
  59. #returns
  60. # _sub_domain=_acme-challenge.www
  61. # _domain=domain.com
  62. _get_root() {
  63. fulldomain=$1
  64. if ! _get_root_by_getList "$fulldomain"; then
  65. _debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api."
  66. # The above "getList" api will only return hosts *owned* by the calling user. However, if the calling
  67. # user is not the owner, but still has administrative rights, we must query the getHosts api directly.
  68. # See this comment and the official namecheap response: http://disq.us/p/1q6v9x9
  69. if ! _get_root_by_getHosts "$fulldomain"; then
  70. return 1
  71. fi
  72. fi
  73. return 0
  74. }
  75. _get_root_by_getList() {
  76. domain=$1
  77. if ! _namecheap_post "namecheap.domains.getList"; then
  78. _err "$error"
  79. return 1
  80. fi
  81. i=2
  82. p=1
  83. while true; do
  84. h=$(printf "%s" "$domain" | cut -d . -f $i-100)
  85. _debug h "$h"
  86. if [ -z "$h" ]; then
  87. #not valid
  88. return 1
  89. fi
  90. if ! _contains "$h" "\\."; then
  91. #not valid
  92. return 1
  93. fi
  94. if ! _contains "$response" "$h"; then
  95. _debug "$h not found"
  96. else
  97. _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
  98. _domain="$h"
  99. return 0
  100. fi
  101. p="$i"
  102. i=$(_math "$i" + 1)
  103. done
  104. return 1
  105. }
  106. _get_root_by_getHosts() {
  107. i=100
  108. p=99
  109. while [ $p -ne 0 ]; do
  110. h=$(printf "%s" "$1" | cut -d . -f $i-100)
  111. if [ -n "$h" ]; then
  112. if _contains "$h" "\\."; then
  113. _debug h "$h"
  114. if _namecheap_set_tld_sld "$h"; then
  115. _sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p)
  116. _domain="$h"
  117. return 0
  118. else
  119. _debug "$h not found"
  120. fi
  121. fi
  122. fi
  123. i="$p"
  124. p=$(_math "$p" - 1)
  125. done
  126. return 1
  127. }
  128. _namecheap_set_publicip() {
  129. if [ -z "$NAMECHEAP_SOURCEIP" ]; then
  130. _err "No Source IP specified for Namecheap API."
  131. _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
  132. return 1
  133. else
  134. _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
  135. _debug sourceip "$NAMECHEAP_SOURCEIP"
  136. ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
  137. addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https):\/\/.*')
  138. _debug2 ip "$ip"
  139. _debug2 addr "$addr"
  140. if [ -n "$ip" ]; then
  141. _publicip="$ip"
  142. elif [ -n "$addr" ]; then
  143. _publicip=$(_get "$addr")
  144. else
  145. _err "No Source IP specified for Namecheap API."
  146. _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
  147. return 1
  148. fi
  149. fi
  150. _debug publicip "$_publicip"
  151. return 0
  152. }
  153. _namecheap_post() {
  154. command=$1
  155. data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}"
  156. _debug2 "_namecheap_post data" "$data"
  157. response="$(_post "$data" "$NAMECHEAP_API" "" "POST")"
  158. _debug2 response "$response"
  159. if _contains "$response" "Status=\"ERROR\"" >/dev/null; then
  160. error=$(echo "$response" | _egrep_o ">.*<\\/Error>" | cut -d '<' -f 1 | tr -d '>')
  161. _err "error $error"
  162. return 1
  163. fi
  164. return 0
  165. }
  166. _namecheap_parse_host() {
  167. _host=$1
  168. _debug _host "$_host"
  169. _hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2)
  170. _hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2)
  171. _hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2)
  172. _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2)
  173. _hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2)
  174. _hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2)
  175. _debug hostid "$_hostid"
  176. _debug hostname "$_hostname"
  177. _debug hosttype "$_hosttype"
  178. _debug hostaddress "$_hostaddress"
  179. _debug hostmxpref "$_hostmxpref"
  180. _debug hostttl "$_hostttl"
  181. }
  182. _namecheap_check_config() {
  183. if [ -z "$NAMECHEAP_API_KEY" ]; then
  184. _err "No API key specified for Namecheap API."
  185. _err "Create your key and export it as NAMECHEAP_API_KEY"
  186. return 1
  187. fi
  188. if [ -z "$NAMECHEAP_USERNAME" ]; then
  189. _err "No username key specified for Namecheap API."
  190. _err "Create your key and export it as NAMECHEAP_USERNAME"
  191. return 1
  192. fi
  193. _saveaccountconf NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY"
  194. _saveaccountconf NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME"
  195. return 0
  196. }
  197. _set_namecheap_TXT() {
  198. subdomain=$2
  199. txt=$3
  200. if ! _namecheap_set_tld_sld "$1"; then
  201. return 1
  202. fi
  203. request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
  204. if ! _namecheap_post "$request"; then
  205. _err "$error"
  206. return 1
  207. fi
  208. hosts=$(echo "$response" | _egrep_o '<host[^>]*')
  209. _debug hosts "$hosts"
  210. if [ -z "$hosts" ]; then
  211. _error "Hosts not found"
  212. return 1
  213. fi
  214. _namecheap_reset_hostList
  215. while read -r host; do
  216. if _contains "$host" "<host"; then
  217. _namecheap_parse_host "$host"
  218. _debug2 _hostname "_hostname"
  219. _debug2 _hosttype "_hosttype"
  220. _debug2 _hostaddress "_hostaddress"
  221. _debug2 _hostmxpref "_hostmxpref"
  222. _hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)"
  223. _debug2 "encoded _hostaddress" "_hostaddress"
  224. _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
  225. fi
  226. done <<EOT
  227. echo "$hosts"
  228. EOT
  229. _namecheap_add_host "$subdomain" "TXT" "$txt" 10 120
  230. _debug hostrequestfinal "$_hostrequest"
  231. request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}"
  232. if ! _namecheap_post "$request"; then
  233. _err "$error"
  234. return 1
  235. fi
  236. return 0
  237. }
  238. _del_namecheap_TXT() {
  239. subdomain=$2
  240. txt=$3
  241. if ! _namecheap_set_tld_sld "$1"; then
  242. return 1
  243. fi
  244. request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
  245. if ! _namecheap_post "$request"; then
  246. _err "$error"
  247. return 1
  248. fi
  249. hosts=$(echo "$response" | _egrep_o '<host[^>]*')
  250. _debug hosts "$hosts"
  251. if [ -z "$hosts" ]; then
  252. _error "Hosts not found"
  253. return 1
  254. fi
  255. _namecheap_reset_hostList
  256. found=0
  257. while read -r host; do
  258. if _contains "$host" "<host"; then
  259. _namecheap_parse_host "$host"
  260. if [ "$_hosttype" = "TXT" ] && [ "$_hostname" = "$subdomain" ] && [ "$_hostaddress" = "$txt" ]; then
  261. _debug "TXT entry found"
  262. found=1
  263. else
  264. _hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)"
  265. _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
  266. fi
  267. fi
  268. done <<EOT
  269. echo "$hosts"
  270. EOT
  271. if [ $found -eq 0 ]; then
  272. _debug "TXT entry not found"
  273. return 0
  274. fi
  275. _debug hostrequestfinal "$_hostrequest"
  276. request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}"
  277. if ! _namecheap_post "$request"; then
  278. _err "$error"
  279. return 1
  280. fi
  281. return 0
  282. }
  283. _namecheap_reset_hostList() {
  284. _hostindex=0
  285. _hostrequest=""
  286. }
  287. #Usage: _namecheap_add_host HostName RecordType Address MxPref TTL
  288. _namecheap_add_host() {
  289. _hostindex=$(_math "$_hostindex" + 1)
  290. _hostrequest=$(printf '%s&HostName%d=%s&RecordType%d=%s&Address%d=%s&MXPref%d=%d&TTL%d=%d' "$_hostrequest" "$_hostindex" "$1" "$_hostindex" "$2" "$_hostindex" "$3" "$_hostindex" "$4" "$_hostindex" "$5")
  291. }
  292. _namecheap_set_tld_sld() {
  293. domain=$1
  294. _tld=""
  295. _sld=""
  296. i=2
  297. while true; do
  298. _tld=$(printf "%s" "$domain" | cut -d . -f $i-100)
  299. _debug tld "$_tld"
  300. if [ -z "$_tld" ]; then
  301. _debug "invalid tld"
  302. return 1
  303. fi
  304. j=$(_math "$i" - 1)
  305. _sld=$(printf "%s" "$domain" | cut -d . -f 1-"$j")
  306. _debug sld "$_sld"
  307. if [ -z "$_sld" ]; then
  308. _debug "invalid sld"
  309. return 1
  310. fi
  311. request="namecheap.domains.dns.getHosts&SLD=$_sld&TLD=$_tld"
  312. if ! _namecheap_post "$request"; then
  313. _debug "sld($_sld)/tld($_tld) not found"
  314. else
  315. _debug "sld($_sld)/tld($_tld) found"
  316. return 0
  317. fi
  318. i=$(_math "$i" + 1)
  319. done
  320. }