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.

434 lines
9.8 KiB

7 years ago
7 years ago
2 months ago
2 months ago
2 months ago
7 years ago
7 years ago
7 years ago
1 month ago
1 month ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC2034
  3. dns_inwx_info='INWX.de
  4. Site: INWX.de
  5. Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_inwx
  6. Options:
  7. INWX_User Username
  8. INWX_Password Password
  9. '
  10. # Dependencies:
  11. # -------------
  12. # - oathtool (When using 2 Factor Authentication)
  13. INWX_Api="https://api.domrobot.com/xmlrpc/"
  14. ######## Public functions #####################
  15. #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  16. dns_inwx_add() {
  17. fulldomain=$1
  18. txtvalue=$2
  19. INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
  20. INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
  21. INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
  22. if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
  23. INWX_User=""
  24. INWX_Password=""
  25. _err "You don't specify inwx user and password yet."
  26. _err "Please create you key and try again."
  27. return 1
  28. fi
  29. #save the api key and email to the account conf file.
  30. _saveaccountconf_mutable INWX_User "$INWX_User"
  31. _saveaccountconf_mutable INWX_Password "$INWX_Password"
  32. _saveaccountconf_mutable INWX_Shared_Secret "$INWX_Shared_Secret"
  33. if ! _inwx_login; then
  34. return 1
  35. fi
  36. _debug "First detect the root zone"
  37. if ! _get_root "$fulldomain"; then
  38. _err "invalid domain"
  39. return 1
  40. fi
  41. _debug _sub_domain "$_sub_domain"
  42. _debug _domain "$_domain"
  43. _info "Adding record"
  44. _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
  45. }
  46. #fulldomain txtvalue
  47. dns_inwx_rm() {
  48. fulldomain=$1
  49. txtvalue=$2
  50. INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
  51. INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
  52. INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
  53. if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
  54. INWX_User=""
  55. INWX_Password=""
  56. _err "You don't specify inwx user and password yet."
  57. _err "Please create you key and try again."
  58. return 1
  59. fi
  60. if ! _inwx_login; then
  61. return 1
  62. fi
  63. _debug "First detect the root zone"
  64. if ! _get_root "$fulldomain"; then
  65. _err "invalid domain"
  66. return 1
  67. fi
  68. _debug _sub_domain "$_sub_domain"
  69. _debug _domain "$_domain"
  70. _debug "Getting txt records"
  71. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  72. <methodCall>
  73. <methodName>nameserver.info</methodName>
  74. <params>
  75. <param>
  76. <value>
  77. <struct>
  78. <member>
  79. <name>domain</name>
  80. <value>
  81. <string>%s</string>
  82. </value>
  83. </member>
  84. <member>
  85. <name>type</name>
  86. <value>
  87. <string>TXT</string>
  88. </value>
  89. </member>
  90. <member>
  91. <name>name</name>
  92. <value>
  93. <string>%s</string>
  94. </value>
  95. </member>
  96. </struct>
  97. </value>
  98. </param>
  99. </params>
  100. </methodCall>' "$_domain" "$_sub_domain")
  101. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  102. if ! _contains "$response" "Command completed successfully"; then
  103. _err "Error could not get txt records"
  104. return 1
  105. fi
  106. if ! printf "%s" "$response" | grep "count" >/dev/null; then
  107. _info "Do not need to delete record"
  108. else
  109. _record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+')
  110. _info "Deleting record"
  111. _inwx_delete_record "$_record_id"
  112. fi
  113. }
  114. #################### Private functions below ##################################
  115. _inwx_check_cookie() {
  116. INWX_Cookie="${INWX_Cookie:-$(_readaccountconf_mutable INWX_Cookie)}"
  117. if [ -z "$INWX_Cookie" ]; then
  118. _debug "No cached cookie found"
  119. return 1
  120. fi
  121. _H1="$INWX_Cookie"
  122. export _H1
  123. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  124. <methodCall>
  125. <methodName>account.info</methodName>
  126. </methodCall>')
  127. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  128. if _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
  129. _debug "Cached cookie still valid"
  130. return 0
  131. fi
  132. _debug "Cached cookie no longer valid"
  133. _H1=""
  134. export _H1
  135. INWX_Cookie=""
  136. _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
  137. return 1
  138. }
  139. _htmlEscape() {
  140. _s="$1"
  141. _s=$(echo "$_s" | sed "s/&/&amp;/g")
  142. _s=$(echo "$_s" | sed "s/</\&lt;/g")
  143. _s=$(echo "$_s" | sed "s/>/\&gt;/g")
  144. _s=$(echo "$_s" | sed 's/"/\&quot;/g')
  145. printf -- %s "$_s"
  146. }
  147. _inwx_login() {
  148. if _inwx_check_cookie; then
  149. _debug "Already logged in"
  150. return 0
  151. fi
  152. XML_PASS=$(_htmlEscape "$INWX_Password")
  153. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  154. <methodCall>
  155. <methodName>account.login</methodName>
  156. <params>
  157. <param>
  158. <value>
  159. <struct>
  160. <member>
  161. <name>user</name>
  162. <value>
  163. <string>%s</string>
  164. </value>
  165. </member>
  166. <member>
  167. <name>pass</name>
  168. <value>
  169. <string>%s</string>
  170. </value>
  171. </member>
  172. </struct>
  173. </value>
  174. </param>
  175. </params>
  176. </methodCall>' "$INWX_User" "$XML_PASS")
  177. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  178. INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
  179. _H1=$INWX_Cookie
  180. export _H1
  181. export INWX_Cookie
  182. _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
  183. if ! _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
  184. _err "INWX API: Authentication error (username/password correct?)"
  185. return 1
  186. fi
  187. #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
  188. if _contains "$response" "<member><name>tfa</name><value><string>GOOGLE-AUTH</string></value></member>"; then
  189. if [ -z "$INWX_Shared_Secret" ]; then
  190. _err "INWX API: Mobile TAN detected."
  191. _err "Please define a shared secret."
  192. return 1
  193. fi
  194. if ! _exists oathtool; then
  195. _err "Please install oathtool to use 2 Factor Authentication."
  196. _err ""
  197. return 1
  198. fi
  199. tan="$(oathtool --base32 --totp "${INWX_Shared_Secret}" 2>/dev/null)"
  200. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  201. <methodCall>
  202. <methodName>account.unlock</methodName>
  203. <params>
  204. <param>
  205. <value>
  206. <struct>
  207. <member>
  208. <name>tan</name>
  209. <value>
  210. <string>%s</string>
  211. </value>
  212. </member>
  213. </struct>
  214. </value>
  215. </param>
  216. </params>
  217. </methodCall>' "$tan")
  218. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  219. if ! _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
  220. _err "INWX API: Mobile TAN not correct."
  221. return 1
  222. fi
  223. fi
  224. }
  225. _get_root() {
  226. domain=$1
  227. _debug "get root"
  228. domain=$1
  229. i=2
  230. p=1
  231. xml_content='<?xml version="1.0" encoding="UTF-8"?>
  232. <methodCall>
  233. <methodName>nameserver.list</methodName>
  234. <params>
  235. <param>
  236. <value>
  237. <struct>
  238. <member>
  239. <name>pagelimit</name>
  240. <value>
  241. <int>9999</int>
  242. </value>
  243. </member>
  244. </struct>
  245. </value>
  246. </param>
  247. </params>
  248. </methodCall>'
  249. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  250. while true; do
  251. h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
  252. _debug h "$h"
  253. if [ -z "$h" ]; then
  254. #not valid
  255. return 1
  256. fi
  257. if _contains "$response" "$h"; then
  258. _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
  259. _domain="$h"
  260. return 0
  261. fi
  262. p=$i
  263. i=$(_math "$i" + 1)
  264. done
  265. return 1
  266. }
  267. _inwx_delete_record() {
  268. record_id=$1
  269. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  270. <methodCall>
  271. <methodName>nameserver.deleteRecord</methodName>
  272. <params>
  273. <param>
  274. <value>
  275. <struct>
  276. <member>
  277. <name>id</name>
  278. <value>
  279. <int>%s</int>
  280. </value>
  281. </member>
  282. </struct>
  283. </value>
  284. </param>
  285. </params>
  286. </methodCall>' "$record_id")
  287. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  288. if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
  289. _err "Error"
  290. return 1
  291. fi
  292. return 0
  293. }
  294. _inwx_update_record() {
  295. record_id=$1
  296. txtval=$2
  297. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  298. <methodCall>
  299. <methodName>nameserver.updateRecord</methodName>
  300. <params>
  301. <param>
  302. <value>
  303. <struct>
  304. <member>
  305. <name>content</name>
  306. <value>
  307. <string>%s</string>
  308. </value>
  309. </member>
  310. <member>
  311. <name>id</name>
  312. <value>
  313. <int>%s</int>
  314. </value>
  315. </member>
  316. </struct>
  317. </value>
  318. </param>
  319. </params>
  320. </methodCall>' "$txtval" "$record_id")
  321. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  322. if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
  323. _err "Error"
  324. return 1
  325. fi
  326. return 0
  327. }
  328. _inwx_add_record() {
  329. domain=$1
  330. sub_domain=$2
  331. txtval=$3
  332. xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
  333. <methodCall>
  334. <methodName>nameserver.createRecord</methodName>
  335. <params>
  336. <param>
  337. <value>
  338. <struct>
  339. <member>
  340. <name>domain</name>
  341. <value>
  342. <string>%s</string>
  343. </value>
  344. </member>
  345. <member>
  346. <name>type</name>
  347. <value>
  348. <string>TXT</string>
  349. </value>
  350. </member>
  351. <member>
  352. <name>content</name>
  353. <value>
  354. <string>%s</string>
  355. </value>
  356. </member>
  357. <member>
  358. <name>name</name>
  359. <value>
  360. <string>%s</string>
  361. </value>
  362. </member>
  363. </struct>
  364. </value>
  365. </param>
  366. </params>
  367. </methodCall>' "$domain" "$txtval" "$sub_domain")
  368. response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
  369. if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
  370. _err "Error"
  371. return 1
  372. fi
  373. return 0
  374. }