chiark / gitweb /
bin/update: Check current state before submitting an update.
[dyndns] / bin / update
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