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