Browse Source

Merge branch 'dev' of https://github.com/Neilpang/acme.sh into dev

neil 8 years ago
parent
commit
67e3dd36b3
11 changed files with 550 additions and 32 deletions
  1. 4 0
      README.md
  2. 247 21
      acme.sh
  3. 1 0
      deploy/README.md
  4. 81 0
      deploy/kong.sh
  5. 21 0
      dnsapi/README.md
  6. 1 1
      dnsapi/dns_ali.sh
  7. 2 2
      dnsapi/dns_aws.sh
  8. 2 2
      dnsapi/dns_cx.sh
  9. 183 0
      dnsapi/dns_linode.sh
  10. 7 5
      dnsapi/dns_lua.sh
  11. 1 1
      dnsapi/dns_me.sh

+ 4 - 0
README.md

@@ -13,6 +13,9 @@ It's probably the `easiest&smallest&smartest` shell script to automatically issu
 Wiki: https://github.com/Neilpang/acme.sh/wiki
 Wiki: https://github.com/Neilpang/acme.sh/wiki
 
 
 
 
+Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
+
+
 # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
 # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
 
 
 
 
@@ -266,6 +269,7 @@ You don't have to do anything manually!
 1. aliyun.com(阿里云) API
 1. aliyun.com(阿里云) API
 1. ISPConfig 3.1 API
 1. ISPConfig 3.1 API
 1. Alwaysdata.com API
 1. Alwaysdata.com API
+1. Linode.com API
 
 
 **More APIs coming soon...**
 **More APIs coming soon...**
 
 

+ 247 - 21
acme.sh

@@ -336,15 +336,241 @@ _h2b() {
   done
   done
 }
 }
 
 
-#hex string
-_hex() {
-  _str="$1"
-  _str_len=${#_str}
-  _h_i=1
-  while [ "$_h_i" -le "$_str_len" ]; do
-    _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")"
-    printf "%02x" "'$_str_c"
-    _h_i="$(_math "$_h_i" + 1)"
+_is_solaris() {
+  _contains "${__OS__:=$(uname -a)}" "solaris" || _contains "${__OS__:=$(uname -a)}" "SunOS"
+}
+
+#stdin  output hexstr splited by one space
+#input:"abc"
+#output: " 61 62 63"
+_hex_dump() {
+  od -A n -v -t x1 | tr -d "\r\t" | tr -s " " | sed "s/ $//" | tr -d "\n"
+}
+
+#url encode, no-preserved chars
+#A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z
+#41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a
+
+#a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
+#61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a
+
+#0  1  2  3  4  5  6  7  8  9  -  _  .  ~
+#30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e
+
+#stdin stdout
+_url_encode() {
+  _hex_str=$(_hex_dump)
+  _debug3 "_url_encode"
+  _debug3 "_hex_str" "$_hex_str"
+  for _hex_code in $_hex_str; do
+    #upper case
+    case "${_hex_code}" in
+      "41")
+        printf "%s" "A"
+        ;;
+      "42")
+        printf "%s" "B"
+        ;;
+      "43")
+        printf "%s" "C"
+        ;;
+      "44")
+        printf "%s" "D"
+        ;;
+      "45")
+        printf "%s" "E"
+        ;;
+      "46")
+        printf "%s" "F"
+        ;;
+      "47")
+        printf "%s" "G"
+        ;;
+      "48")
+        printf "%s" "H"
+        ;;
+      "49")
+        printf "%s" "I"
+        ;;
+      "4a")
+        printf "%s" "J"
+        ;;
+      "4b")
+        printf "%s" "K"
+        ;;
+      "4c")
+        printf "%s" "L"
+        ;;
+      "4d")
+        printf "%s" "M"
+        ;;
+      "4e")
+        printf "%s" "N"
+        ;;
+      "4f")
+        printf "%s" "O"
+        ;;
+      "50")
+        printf "%s" "P"
+        ;;
+      "51")
+        printf "%s" "Q"
+        ;;
+      "52")
+        printf "%s" "R"
+        ;;
+      "53")
+        printf "%s" "S"
+        ;;
+      "54")
+        printf "%s" "T"
+        ;;
+      "55")
+        printf "%s" "U"
+        ;;
+      "56")
+        printf "%s" "V"
+        ;;
+      "57")
+        printf "%s" "W"
+        ;;
+      "58")
+        printf "%s" "X"
+        ;;
+      "59")
+        printf "%s" "Y"
+        ;;
+      "5a")
+        printf "%s" "Z"
+        ;;
+
+      #lower case
+      "61")
+        printf "%s" "a"
+        ;;
+      "62")
+        printf "%s" "b"
+        ;;
+      "63")
+        printf "%s" "c"
+        ;;
+      "64")
+        printf "%s" "d"
+        ;;
+      "65")
+        printf "%s" "e"
+        ;;
+      "66")
+        printf "%s" "f"
+        ;;
+      "67")
+        printf "%s" "g"
+        ;;
+      "68")
+        printf "%s" "h"
+        ;;
+      "69")
+        printf "%s" "i"
+        ;;
+      "6a")
+        printf "%s" "j"
+        ;;
+      "6b")
+        printf "%s" "k"
+        ;;
+      "6c")
+        printf "%s" "l"
+        ;;
+      "6d")
+        printf "%s" "m"
+        ;;
+      "6e")
+        printf "%s" "n"
+        ;;
+      "6f")
+        printf "%s" "o"
+        ;;
+      "70")
+        printf "%s" "p"
+        ;;
+      "71")
+        printf "%s" "q"
+        ;;
+      "72")
+        printf "%s" "r"
+        ;;
+      "73")
+        printf "%s" "s"
+        ;;
+      "74")
+        printf "%s" "t"
+        ;;
+      "75")
+        printf "%s" "u"
+        ;;
+      "76")
+        printf "%s" "v"
+        ;;
+      "77")
+        printf "%s" "w"
+        ;;
+      "78")
+        printf "%s" "x"
+        ;;
+      "79")
+        printf "%s" "y"
+        ;;
+      "7a")
+        printf "%s" "z"
+        ;;
+      #numbers
+      "30")
+        printf "%s" "0"
+        ;;
+      "31")
+        printf "%s" "1"
+        ;;
+      "32")
+        printf "%s" "2"
+        ;;
+      "33")
+        printf "%s" "3"
+        ;;
+      "34")
+        printf "%s" "4"
+        ;;
+      "35")
+        printf "%s" "5"
+        ;;
+      "36")
+        printf "%s" "6"
+        ;;
+      "37")
+        printf "%s" "7"
+        ;;
+      "38")
+        printf "%s" "8"
+        ;;
+      "39")
+        printf "%s" "9"
+        ;;
+      "2d")
+        printf "%s" "-"
+        ;;
+      "5f")
+        printf "%s" "_"
+        ;;
+      "2e")
+        printf "%s" "."
+        ;;
+      "7e")
+        printf "%s" "~"
+        ;;
+      #other hex  
+      *)
+        printf '%%%s' "$_hex_code"
+        ;;
+    esac
   done
   done
 }
 }
 
 
@@ -868,7 +1094,7 @@ createCSR() {
 
 
 }
 }
 
 
-_urlencode() {
+_url_replace() {
   tr '/+' '_-' | tr -d '= '
   tr '/+' '_-' | tr -d '= '
 }
 }
 
 
@@ -935,7 +1161,7 @@ _calcjwk() {
 
 
     modulus=$($OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
     modulus=$($OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
     _debug3 modulus "$modulus"
     _debug3 modulus "$modulus"
-    n="$(printf "%s" "$modulus" | _h2b | _base64 | _urlencode)"
+    n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
     _debug3 n "$n"
     _debug3 n "$n"
 
 
     jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
     jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
@@ -990,14 +1216,14 @@ _calcjwk() {
     x="$(printf "%s" "$pubtext" | cut -d : -f 2-"$xend")"
     x="$(printf "%s" "$pubtext" | cut -d : -f 2-"$xend")"
     _debug3 x "$x"
     _debug3 x "$x"
 
 
-    x64="$(printf "%s" "$x" | tr -d : | _h2b | _base64 | _urlencode)"
+    x64="$(printf "%s" "$x" | tr -d : | _h2b | _base64 | _url_replace)"
     _debug3 x64 "$x64"
     _debug3 x64 "$x64"
 
 
     xend=$(_math "$xend" + 1)
     xend=$(_math "$xend" + 1)
     y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)"
     y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)"
     _debug3 y "$y"
     _debug3 y "$y"
 
 
-    y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _urlencode)"
+    y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _url_replace)"
     _debug3 y64 "$y64"
     _debug3 y64 "$y64"
 
 
     jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
     jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
@@ -1241,7 +1467,7 @@ _send_signed_request() {
     return 1
     return 1
   fi
   fi
 
 
-  payload64=$(printf "%s" "$payload" | _base64 | _urlencode)
+  payload64=$(printf "%s" "$payload" | _base64 | _url_replace)
   _debug3 payload64 "$payload64"
   _debug3 payload64 "$payload64"
 
 
   if [ -z "$_CACHED_NONCE" ]; then
   if [ -z "$_CACHED_NONCE" ]; then
@@ -1267,7 +1493,7 @@ _send_signed_request() {
   protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2"
   protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2"
   _debug3 protected "$protected"
   _debug3 protected "$protected"
 
 
-  protected64="$(printf "%s" "$protected" | _base64 | _urlencode)"
+  protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
   _debug3 protected64 "$protected64"
   _debug3 protected64 "$protected64"
 
 
   if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
   if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
@@ -1276,7 +1502,7 @@ _send_signed_request() {
   fi
   fi
   _debug3 _sig_t "$_sig_t"
   _debug3 _sig_t "$_sig_t"
 
 
-  sig="$(printf "%s" "$_sig_t" | _urlencode)"
+  sig="$(printf "%s" "$_sig_t" | _url_replace)"
   _debug3 sig "$sig"
   _debug3 sig "$sig"
 
 
   body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
   body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
@@ -2009,7 +2235,7 @@ _clearupdns() {
     keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
     keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
     vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
     vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
     _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
     _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
-    txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _urlencode)"
+    txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
     _debug txt "$txt"
     _debug txt "$txt"
     if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
     if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
       _info "$d is already verified, skip $vtype."
       _info "$d is already verified, skip $vtype."
@@ -2553,7 +2779,7 @@ issue() {
 
 
       if [ -z "$thumbprint" ]; then
       if [ -z "$thumbprint" ]; then
         accountkey_json=$(printf "%s" "$jwk" | tr -d ' ')
         accountkey_json=$(printf "%s" "$jwk" | tr -d ' ')
-        thumbprint=$(printf "%s" "$accountkey_json" | _digest "sha256" | _urlencode)
+        thumbprint=$(printf "%s" "$accountkey_json" | _digest "sha256" | _url_replace)
       fi
       fi
 
 
       entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
       entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
@@ -2604,7 +2830,7 @@ issue() {
         dnsadded='0'
         dnsadded='0'
         txtdomain="_acme-challenge.$d"
         txtdomain="_acme-challenge.$d"
         _debug txtdomain "$txtdomain"
         _debug txtdomain "$txtdomain"
-        txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _urlencode)"
+        txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
         _debug txt "$txt"
         _debug txt "$txt"
 
 
         d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
         d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
@@ -2879,7 +3105,7 @@ issue() {
 
 
   _clearup
   _clearup
   _info "Verify finished, start to sign."
   _info "Verify finished, start to sign."
-  der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)"
+  der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
 
 
   if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"; then
   if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"; then
     _err "Sign failed."
     _err "Sign failed."
@@ -3457,7 +3683,7 @@ revoke() {
     return 1
     return 1
   fi
   fi
 
 
-  cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _urlencode)"
+  cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
 
 
   if [ -z "$cert" ]; then
   if [ -z "$cert" ]; then
     _err "Cert for $Le_Domain is empty found, skip."
     _err "Cert for $Le_Domain is empty found, skip."

+ 1 - 0
deploy/README.md

@@ -0,0 +1 @@
+#Using deploy api

+ 81 - 0
deploy/kong.sh

@@ -0,0 +1,81 @@
+#!/usr/bin/env sh
+
+# This deploy hook will deploy ssl cert on kong proxy engine based on api request_host parameter.
+# Note that ssl plugin should be available on Kong instance
+# The hook will match cdomain to request_host, in case of multiple domain it will always take the first
+# one (acme.sh behaviour).
+# If ssl config already exist it will update only cert and key not touching other parameter
+# If ssl config doesn't exist it will only upload cert and key and not set other parameter
+# Not that we deploy full chain
+# See https://getkong.org/plugins/dynamic-ssl/ for other options
+# Written by Geoffroi Genot <ggenot@voxbone.com>
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+kong_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+  _info "Deploying certificate on Kong instance"
+  if [ -z "$KONG_URL" ]; then
+    _debug "KONG_URL Not set, using default http://localhost:8001"
+    KONG_URL="http://localhost:8001"
+  fi
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  #Get uuid linked to the domain
+  uuid=$(_get "$KONG_URL/apis?request_host=$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
+  if [ -z "$uuid" ]; then
+    _err "Unable to get Kong uuid for domain $_cdomain"
+    _err "Make sure that KONG_URL is correctly configured"
+    _err "Make sure that a Kong api request_host match the domain"
+    _err "Kong url: $KONG_URL"
+    return 1
+  fi
+  #Save kong url if it's succesful (First run case)
+  _saveaccountconf KONG_URL "$KONG_URL"
+  #Generate DEIM
+  delim="-----MultipartDelimeter$(date "+%s%N")"
+  nl="\015\012"
+  #Set Header
+  _H1="Content-Type: multipart/form-data; boundary=$delim"
+  #Generate data for request (Multipart/form-data with mixed content)
+  #set name to ssl
+  content="--$delim${nl}Content-Disposition: form-data; name=\"name\"${nl}${nl}ssl"
+  #add key
+  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
+  #Add cert
+  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
+  #Close multipart
+  content="$content${nl}--$delim--${nl}"
+  #Convert CRLF
+  content=$(printf %b "$content")
+  #DEBUG
+  _debug header "$_H1"
+  _debug content "$content"
+  #Check if ssl plugins is aready enabled (if not => POST else => PATCH)
+  ssl_uuid=$(_get "$KONG_URL/apis/$uuid/plugins" | _egrep_o '"id":"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"[a-zA-Z0-9\-\,\"_\:]*"name":"ssl"' | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
+  _debug ssl_uuid "$ssl_uuid"
+  if [ -z "$ssl_uuid" ]; then
+    #Post certificate to Kong
+    response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins" "" "POST")
+  else
+    #patch
+    response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins/$ssl_uuid" "" "PATCH")
+  fi
+  if ! [ "$(echo "$response" | _egrep_o "ssl")" = "ssl" ]; then
+    _err "An error occured with cert upload. Check response:"
+    _err "$response"
+    return 1
+  fi
+  _debug response "$response"
+  _info "Certificate successfully deployed"
+}

+ 21 - 0
dnsapi/README.md

@@ -257,6 +257,27 @@ acme.sh --issue --dns dns_ad -d example.com -d www.example.com
 The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused
 The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused
 when needed.
 when needed.
 
 
+## 14. Use Linode domain API
+
+First you need to login to your Linode account to get your API Key.
+[https://manager.linode.com/profile/api](https://manager.linode.com/profile/api)
+
+Then add an API key with label *ACME* and copy the new key.
+
+```sh
+export LINODE_API_KEY="..."
+```
+
+Due to the reload time of any changes in the DNS records, we have to use the `dnssleep` option to wait at least 15 minutes for the changes to take effect.
+
+Ok, let's issue a cert now:
+
+```sh
+acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com
+```
+
+The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
 # Use custom API
 # Use custom API
 
 
 If your API is not supported yet, you can write your own DNS API.
 If your API is not supported yet, you can write your own DNS API.

+ 1 - 1
dnsapi/dns_ali.sh

@@ -67,7 +67,7 @@ _get_root() {
 }
 }
 
 
 _ali_rest() {
 _ali_rest() {
-  signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(_hex "$Ali_Secret&")" | _base64)
+  signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
   signature=$(_ali_urlencode "$signature")
   signature=$(_ali_urlencode "$signature")
   url="$Ali_API?$query&Signature=$signature"
   url="$Ali_API?$query&Signature=$signature"
 
 

+ 2 - 2
dnsapi/dns_aws.sh

@@ -93,7 +93,7 @@ _get_root() {
       fi
       fi
 
 
       if _contains "$response" "<Name>$h.</Name>"; then
       if _contains "$response" "<Name>$h.</Name>"; then
-        hostedzone="$(echo "$response" | sed 's/<HostedZone>/\n&/g' | _egrep_o "<HostedZone>.*?<Name>$h.<.Name>.*?<.HostedZone>")"
+        hostedzone="$(echo "$response" | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<.HostedZone>")"
         _debug hostedzone "$hostedzone"
         _debug hostedzone "$hostedzone"
         if [ -z "$hostedzone" ]; then
         if [ -z "$hostedzone" ]; then
           _err "Error, can not get hostedzone."
           _err "Error, can not get hostedzone."
@@ -183,7 +183,7 @@ aws_rest() {
 
 
   _debug2 kSecret "$kSecret"
   _debug2 kSecret "$kSecret"
 
 
-  kSecretH="$(_hex "$kSecret")"
+  kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
   _debug2 kSecretH "$kSecretH"
   _debug2 kSecretH "$kSecretH"
 
 
   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"

+ 2 - 2
dnsapi/dns_cx.sh

@@ -82,7 +82,7 @@ existing_records() {
     return 1
     return 1
   fi
   fi
 
 
-  seg=$(printf "%s\n" "$response" | _egrep_o '[^{]*host":"'"$_sub_domain"'"[^}]*\}')
+  seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
   _debug seg "$seg"
   _debug seg "$seg"
   if [ -z "$seg" ]; then
   if [ -z "$seg" ]; then
     return 0
     return 0
@@ -155,7 +155,7 @@ _get_root() {
     fi
     fi
 
 
     if _contains "$response" "$h."; then
     if _contains "$response" "$h."; then
-      seg=$(printf "%s\n" "$response" | _egrep_o '[^{]*"'"$h"'."[^}]*}')
+      seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
       _debug seg "$seg"
       _debug seg "$seg"
       _domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
       _domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
       _debug _domain_id "$_domain_id"
       _debug _domain_id "$_domain_id"

+ 183 - 0
dnsapi/dns_linode.sh

@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
+
+LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
+
+########  Public functions #####################
+
+#Usage: dns_linode_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_linode_add() {
+  fulldomain="${1}"
+  txtvalue="${2}"
+
+  if ! _Linode_API; then
+    return 1
+  fi
+
+  _info "Using Linode"
+  _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "Domain does not exist."
+    return 1
+  fi
+  _debug _domain_id "$_domain_id"
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
+
+  if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
+    _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+    _debug _resource_id "$_resource_id"
+
+    if [ -z "$_resource_id" ]; then
+      _err "Error adding the domain resource."
+      return 1
+    fi
+
+    _info "Domain resource successfully added."
+    return 0
+  fi
+
+  return 1
+}
+
+#Usage: dns_linode_rm   _acme-challenge.www.domain.com
+dns_linode_rm() {
+  fulldomain="${1}"
+
+  if ! _Linode_API; then
+    return 1
+  fi
+
+  _info "Using Linode"
+  _debug "Calling: dns_linode_rm() '${fulldomain}'"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "Domain does not exist."
+    return 1
+  fi
+  _debug _domain_id "$_domain_id"
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _parameters="&DomainID=$_domain_id"
+
+  if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
+    response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
+
+    resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
+    if [ "$resource" ]; then
+      _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+      if [ "$_resource_id" ]; then
+        _debug _resource_id "$_resource_id"
+
+        _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
+
+        if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
+          _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+          _debug _resource_id "$_resource_id"
+
+          if [ -z "$_resource_id" ]; then
+            _err "Error deleting the domain resource."
+            return 1
+          fi
+
+          _info "Domain resource successfully deleted."
+          return 0
+        fi
+      fi
+
+      return 1
+    fi
+
+    return 0
+  fi
+
+  return 1
+}
+
+####################  Private functions below ##################################
+
+_Linode_API() {
+  if [ -z "$LINODE_API_KEY" ]; then
+    LINODE_API_KEY=""
+
+    _err "You didn't specify the Linode API key yet."
+    _err "Please create your key and try again."
+
+    return 1
+  fi
+
+  _saveaccountconf LINODE_API_KEY "$LINODE_API_KEY"
+}
+
+####################  Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+  domain=$1
+  i=2
+  p=1
+
+  if _rest GET "domain.list"; then
+    response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
+    while true; do
+      h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+      _debug h "$h"
+      if [ -z "$h" ]; then
+        #not valid
+        return 1
+      fi
+
+      hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")"
+      if [ "$hostedzone" ]; then
+        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+        if [ "$_domain_id" ]; then
+          _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+          _domain=$h
+          return 0
+        fi
+        return 1
+      fi
+      p=$i
+      i=$(_math "$i" + 1)
+    done
+  fi
+  return 1
+}
+
+#method method action data
+_rest() {
+  mtd="$1"
+  ep="$2"
+  data="$3"
+
+  _debug mtd "$mtd"
+  _debug ep "$ep"
+
+  export _H1="Accept: application/json"
+  export _H2="Content-Type: application/json"
+
+  if [ "$mtd" != "GET" ]; then
+    # both POST and DELETE.
+    _debug data "$data"
+    response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")"
+  else
+    response="$(_get "$LINODE_API_URL$ep$data")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "error $ep"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}

+ 7 - 5
dnsapi/dns_lua.sh

@@ -46,12 +46,12 @@ dns_lua_add() {
     return 1
     return 1
   fi
   fi
 
 
-  count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain\"" | wc -l)
+  count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
   _debug count "$count"
   _debug count "$count"
   if [ "$count" = "0" ]; then
   if [ "$count" = "0" ]; then
     _info "Adding record"
     _info "Adding record"
     if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
     if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
-      if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
+      if _contains "$response" "$fulldomain"; then
         _info "Added"
         _info "Added"
         #todo: check if the record takes effect
         #todo: check if the record takes effect
         return 0
         return 0
@@ -63,11 +63,11 @@ dns_lua_add() {
     _err "Add txt record error."
     _err "Add txt record error."
   else
   else
     _info "Updating record"
     _info "Updating record"
-    record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | cut -d: -f2 | cut -d, -f1)
+    record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
     _debug "record_id" "$record_id"
     _debug "record_id" "$record_id"
 
 
-    _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"ttl\":120}"
-    if [ "$?" = "0" ]; then
+    _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}"
+    if [ "$?" = "0" ] && _contains "$response" "updated_at"; then
       _info "Updated!"
       _info "Updated!"
       #todo: check if the record takes effect
       #todo: check if the record takes effect
       return 0
       return 0
@@ -99,6 +99,7 @@ _get_root() {
   fi
   fi
   while true; do
   while true; do
     h=$(printf "%s" "$domain" | cut -d . -f $i-100)
     h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    _debug h "$h"
     if [ -z "$h" ]; then
     if [ -z "$h" ]; then
       #not valid
       #not valid
       return 1
       return 1
@@ -106,6 +107,7 @@ _get_root() {
 
 
     if _contains "$response" "\"name\":\"$h\""; then
     if _contains "$response" "\"name\":\"$h\""; then
       _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
       _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
+      _debug _domain_id "$_domain_id"
       if [ "$_domain_id" ]; then
       if [ "$_domain_id" ]; then
         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
         _domain="$h"
         _domain="$h"

+ 1 - 1
dnsapi/dns_me.sh

@@ -124,7 +124,7 @@ _me_rest() {
   _debug "$ep"
   _debug "$ep"
 
 
   cdate=$(date -u +"%a, %d %b %Y %T %Z")
   cdate=$(date -u +"%a, %d %b %Y %T %Z")
-  hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(_hex "$ME_Secret")" hex)
+  hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex)
 
 
   export _H1="x-dnsme-apiKey: $ME_Key"
   export _H1="x-dnsme-apiKey: $ME_Key"
   export _H2="x-dnsme-requestDate: $cdate"
   export _H2="x-dnsme-requestDate: $cdate"