| 1 | #! /bin/sh |
| 2 | |
| 3 | set -e |
| 4 | . ./dyndns.conf |
| 5 | |
| 6 | ## Check that the environment is set up properly. |
| 7 | for i in DYNDNS_ZONE DYNDNS_HOST DYNDNS_SERVER DYNDNS_KEY SSH_CLIENT; do |
| 8 | eval havep=\${$i+t}\${$i-nil} |
| 9 | case $havep in nil) echo >&2 "$0: variable $i unset"; exit 2 ;; esac |
| 10 | done |
| 11 | |
| 12 | ## Find the client address. This may be useful. |
| 13 | set -- $SSH_CLIENT; client=$1 |
| 14 | |
| 15 | ## Parse the commad line. |
| 16 | set -- $SSH_ORIGINAL_COMMAND |
| 17 | |
| 18 | fail_usage () { |
| 19 | cat >&2 <<EOF |
| 20 | usage: $0 COMMAND ARGS... |
| 21 | |
| 22 | Commands: |
| 23 | set [-force] HOST [ADDR] |
| 24 | unset HOST |
| 25 | EOF |
| 26 | exit 1 |
| 27 | } |
| 28 | getarg='case $# in 0) fail_usage ;; esac; arg=$1; shift' |
| 29 | doneargs='case $# in 0) ;; *) fail_usage ;; esac' |
| 30 | |
| 31 | check_current_state () { |
| 32 | ck_rrty=$1 ck_name=$2 |
| 33 | set -- $(adnshost --config "nameserver $DYNDNS_SERVER" \ |
| 34 | -Fi -Tt -t$ck_rrty "$ck_name") |
| 35 | case $1,$2,$3,$5 in |
| 36 | ";,failed,permfail,nxdomain" | ";,failed,permfail,nodata") |
| 37 | err=$5 |
| 38 | ;; |
| 39 | ";,failed,"*) |
| 40 | shift 8 |
| 41 | echo >&2 "$0: lookup $ck_name ($ck_rrty) failed: $*" |
| 42 | exit 4 |
| 43 | ;; |
| 44 | *) |
| 45 | err=nil cur_ttl=$2 cur_addr=$4 |
| 46 | ;; |
| 47 | esac |
| 48 | } |
| 49 | |
| 50 | checkhost () { |
| 51 | host=$1 |
| 52 | |
| 53 | case "$host" in |
| 54 | *..* | .* | *. | *[!-_.a-zA-Z0-9]*) |
| 55 | echo >&2 "$0: invalid hostname" |
| 56 | exit 2 |
| 57 | ;; |
| 58 | esac |
| 59 | |
| 60 | matchp=nil |
| 61 | for pat in $DYNDNS_HOST; do |
| 62 | case "$host" in $pat) matchp=t ;; esac |
| 63 | done |
| 64 | case $matchp in nil) echo >&2 "$0: hostname not permitted"; exit 2 ;; esac |
| 65 | } |
| 66 | |
| 67 | doupdate () { |
| 68 | cmd=$1 |
| 69 | case ${DYNDNS_TESTONLY_P-nil} in |
| 70 | nil) |
| 71 | nsupdate -k "$DYNDNS_KEY" <<EOF |
| 72 | server $DYNDNS_SERVER |
| 73 | zone $DYNDNS_ZONE |
| 74 | $cmd |
| 75 | send |
| 76 | EOF |
| 77 | ;; |
| 78 | *) |
| 79 | cat <<EOF |
| 80 | $cmd |
| 81 | EOF |
| 82 | ;; |
| 83 | esac |
| 84 | } |
| 85 | |
| 86 | eval $getarg; cmd=$arg |
| 87 | case "$cmd" in |
| 88 | set) |
| 89 | forcep=nil |
| 90 | eval $getarg |
| 91 | case "$arg" in -force) forcep=t; eval $getarg ;; esac |
| 92 | host=$arg |
| 93 | case "$#,$forcep,$1" in |
| 94 | 0,nil,*) addr=$client ;; |
| 95 | 0,t,*) fail_usage ;; |
| 96 | *,nil,"$client" | *,t,*) addr=$1; shift ;; |
| 97 | *) |
| 98 | echo >&2 "$0: incorrect address (wanted = $client; found = $1)" |
| 99 | exit 3 |
| 100 | ;; |
| 101 | esac |
| 102 | eval $doneargs |
| 103 | checkhost "$host" |
| 104 | case $addr in |
| 105 | *:*) rrtype=AAAA ;; |
| 106 | *.*) rrtype=A ;; |
| 107 | *) echo >&2 "$0: failed to parse new address"; exit 2 ;; |
| 108 | esac |
| 109 | name=$host.$DYNDNS_ZONE |
| 110 | check_current_state $(echo $rrtype | tr A-Z a-z) "$name" |
| 111 | ttl=${DYNDNS_TTL-14400} |
| 112 | case $err,$cur_ttl,$cur_addr in |
| 113 | nil,$ttl,$addr) echo >&2 "$0: nothing to do"; exit 0 ;; |
| 114 | esac |
| 115 | doupdate " |
| 116 | update delete $name IN $rrtype |
| 117 | update add $name $ttl IN $rrtype $addr" |
| 118 | ;; |
| 119 | unset) |
| 120 | eval $getarg; host=$arg |
| 121 | eval $doneargs |
| 122 | checkhost "$host" |
| 123 | name=$host.$DYNDNS_ZONE |
| 124 | check_current_state a "$name" |
| 125 | case $err in |
| 126 | nxdomain) echo >&2 "$0: nothing to do"; exit 0 ;; |
| 127 | esac |
| 128 | doupdate "update delete $name IN" |
| 129 | ;; |
| 130 | *) |
| 131 | fail_usage |
| 132 | ;; |
| 133 | esac |