From 3988a733004005983b60aba74306368c122180f2 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 15 Jun 2003 17:33:54 +0000 Subject: [PATCH] @@ -1,8 +1,9 @@ -userv-utils (0.2.4) unstable; urgency=low +userv-utils (0.3.0) unstable; urgency=low - * Minor fixes to INSTALL. - * Report nonzero death of m4 better. - * ipif service MAXEXROUTES increased from 5 to 50. + * New dyndns service. + * ipif: Minor fixes to INSTALL. + * ipif: Report nonzero death of m4 better. + * ipif: service MAXEXROUTES increased from 5 to 50. -- --- changelog | 9 ++- debian/changelog | 9 ++- dyndns/INSTALL | 41 ++++++++++ dyndns/dyndns | 9 +++ dyndns/dyndns-domains | 51 ++++++++++++ dyndns/install | 18 +++++ dyndns/service | 176 ++++++++++++++++++++++++++++++++++++++++++ dyndns/update | 51 ++++++++++++ 8 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 dyndns/INSTALL create mode 100644 dyndns/dyndns create mode 100644 dyndns/dyndns-domains create mode 100755 dyndns/install create mode 100755 dyndns/service create mode 100755 dyndns/update diff --git a/changelog b/changelog index ee56142..4fe32ac 100644 --- a/changelog +++ b/changelog @@ -1,8 +1,9 @@ -userv-utils (0.2.4) unstable; urgency=low +userv-utils (0.3.0) unstable; urgency=low - * Minor fixes to INSTALL. - * Report nonzero death of m4 better. - * ipif service MAXEXROUTES increased from 5 to 50. + * New dyndns service. + * ipif: Minor fixes to INSTALL. + * ipif: Report nonzero death of m4 better. + * ipif: service MAXEXROUTES increased from 5 to 50. -- diff --git a/debian/changelog b/debian/changelog index ee56142..4fe32ac 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,9 @@ -userv-utils (0.2.4) unstable; urgency=low +userv-utils (0.3.0) unstable; urgency=low - * Minor fixes to INSTALL. - * Report nonzero death of m4 better. - * ipif service MAXEXROUTES increased from 5 to 50. + * New dyndns service. + * ipif: Minor fixes to INSTALL. + * ipif: Report nonzero death of m4 better. + * ipif: service MAXEXROUTES increased from 5 to 50. -- diff --git a/dyndns/INSTALL b/dyndns/INSTALL new file mode 100644 index 0000000..800d5ad --- /dev/null +++ b/dyndns/INSTALL @@ -0,0 +1,41 @@ +# To install the dyndns service: +# +# 1. Install the scripts and configuration: +# +mkdir -p /usr/local/lib/userv/dyndns /var/lib/userv/dyndns/tmp +cp install service update /usr/local/lib/userv/dyndns +cp dyndns /etc/userv/services.d/dyndns.distrib +cp dyndns-domains /etc/userv/dyndns-domains.example + +# Rename the .distrib and .example, or edit them to be how you +# want, or merge your changes. +# +# 2. For each zone, create +# /var/lib/userv/zone,/ +# and put in it the file +# Manual containing the $TTL, SOA, zone cut NS RRset, +# RP, and other fixed RRsets. +# and edit +# /etc/userv/dyndns-domains appropriately. +# and then create an RR and check that it has made the zone, +# before adding the new file +# Zone +# to your nameserver configuration. + +# Copyright (C) 2000 Ian Jackson +# +# This is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with userv-utils; if not, write to the Free Software +# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# $Id$ diff --git a/dyndns/dyndns b/dyndns/dyndns new file mode 100644 index 0000000..e73d793 --- /dev/null +++ b/dyndns/dyndns @@ -0,0 +1,9 @@ +if ( grep calling-user-shell /etc/shells + & glob service-user dyndns + ) + reset + no-set-environment + no-suppress-args + no-disconnect-hup + execute /usr/local/lib/userv/dyndns/service +fi diff --git a/dyndns/dyndns-domains b/dyndns/dyndns-domains new file mode 100644 index 0000000..e7b0b35 --- /dev/null +++ b/dyndns/dyndns-domains @@ -0,0 +1,51 @@ +# Syntax is list of directives. +# include Read at this point +# eof +# Settings directives, apply to succeeding subdomain lines. +# zone This is for stuff in +# rrs ... Allow addition/removal of (only) these RR types +# ratelimit +# Limit rate of changes. Up to one per +# will always be allowed, and never updates more than +# apart. is compared +# with events in roughly last +# ttlrange +# Actual directive: +# subdomain + +ratelimit 30 300 3600 +zone dynamic.greenend.org.uk +ttlrange 30 86400 + +rrs A +subdomain anjou rich-avn +subdomain badgers sion-net +subdomain bellevue theom +subdomain burrow vcla-brw +subdomain confusion matt-cnf +subdomain dorothee jdamery +subdomain ecstacy dunc-xtc +subdomain firestorm davi-mul +subdomain gallery jayl-dns +subdomain nosreme ceme-gal +subdomain heresy cjwa-vpn +subdomain lemoncurd bjha-dd +subdomain lilac dame-lil +subdomain nijinsky jmatthew +subdomain relativity ijackson +subdomain riva cjwatson +subdomain sinister Mgend +subdomain slappy chri-cat +subdomain stardust sgtatham +subdomain thistles rcooksey +subdomain titus owen-dns +subdomain milton stev-pub + +rrs A CNAME +subdomain gin dh219 +subdomain aibs dh219 + +rrs A MX CNAME +subdomain test ijackson + +eof diff --git a/dyndns/install b/dyndns/install new file mode 100755 index 0000000..751bb72 --- /dev/null +++ b/dyndns/install @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +zone=$1 +test -d ../zone,$zone + +ah="` + adnshost +Do +Dt -Cf -t soa $zone. || \ + (test $? == 6 && echo . . 1 0 0 0 0) +`" +re='.* \([0-9][0-9]*\) [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]*$' +serial="`expr 1 + match \"$ah\" \"$re\"`" + +sed Zone.new +cat [_0-9a-z]*,data >>Zone.new +mv Zone.new Zone + +echo "serial $serial" +userv root ndc-reload diff --git a/dyndns/service b/dyndns/service new file mode 100755 index 0000000..37bf623 --- /dev/null +++ b/dyndns/service @@ -0,0 +1,176 @@ +#!/usr/bin/perl +# usage: (cat RRs; echo .) | userv dyndns +# Not all zone file formats are accepted: +# - All RRs must have owners specified. +# - All RRs must have TTLs specified. +# - The owner must be specified as a sub-subdomain, relative +# to ., and so must not have a trailing `.'; +# where the owner is to be ., `@' must be used. + +use POSIX; + +BEGIN { + $vardir= "/var/lib/userv/dyndns"; + $defconf= "/etc/userv/dyndns-domains"; + $libdir= "/usr/local/lib/userv/dyndns"; +} +END { + remove "$vardir/tmp/$$" or $! == ENOENT or + warn "cannot remove tempfile:$!\n"; +} + +use FileHandle; +use IO::File; +use Socket; + +@ARGV==2 or die "need and arguments\n"; +($zone,$subdomain) = @ARGV; +domainsyntax("command line",$zone); +domainsyntax("command line",$subdomain) unless $subdomain eq '@'; + +@userv_groups= split m/ /, $ENV{'USERV_GROUP'}; + +@rates= (1,1,1000); +$ttlmin= 0; +$ttlmax= 86400; + +sub readconf ($) { + my ($cf,$fh) = @_; + $fh= new FileHandle; + $fh->open("< $cf") or die "$cf: $!\n"; + for (;;) { + $!=0; $_= <$fh>; + length or die "$cf:".($? ? "read:$?" : "eof")."\n"; + s/^\s+//; chomp; s/\s+$//; + last if m/^eof$/; + next if m/^\#/ or !m/\S/; + if (m/^zone\s+(\S+)$/) { + $thiszone= $1 eq $zone; + } elsif (m/^ratelimit\s+(\d+)\s+(\d+)\s+(\d+)$/) { + @rates= ($1,$2,$3); + } elsif (m/^ttlrange\s+(\d+)\s+(\d+)$/) { + ($ttlmin,$ttlmax) = ($1,$2); + } elsif (m/^rrs\s+([A-Za-z0-9 \t]+)$/) { + $rrt_list= $1; + undef %rrt_allowed; + grep { y/a-z/A-Z/; $rrt_allowed{$_}= 1; } split m/\s+/, $1; + } elsif (m/^include\s+(\S.*)$/) { + return if readconf($1); + } elsif (m/^subdomain\s+(\S+)\s+(\S+)$/) { + next unless $thiszone; + next unless $1 eq $subdomain; + next unless grep { $_ eq $2 } @userv_groups; + return 1; + } else { + die "$cf:$.: config error\n"; + } + } + close $fh or die "$cf: close: $!\n"; + return 0; +} + +readconf "$defconf" + or die "permission denied\n"; + +chdir "$vardir" or die "chdir dyndns:$!\n"; + +open T,">tmp/$$" or die "create temp file: $!\n"; + +for (;;) { + $?=0; $_= ; + die "input:$.:".($? ? "$?" : "eof") unless length; + chomp; + last if m/^\.$/; + s/^(\S+)\s+(\d+)\s+([A-Za-z][0-9A-Za-z]*)\s+// + or die "input:$.:bogus line\n"; + ($owner,$ttl,$type)= ($1,$2,$3); + if ($owner eq '@') { + $write_owner= $subdomain; + } else { + domainsyntax("input:$.",$owner) unless $owner eq '@'; + $write_owner= $subdomain eq '@' ? $owner : "$owner.$subdomain"; + } + length "$write_owner.$zone." < 255 + or die "input:$.:$owner:resulting domain name too long\n"; + + $ttl += 0; + if ($ttl < $ttlmin) { + warn "input:$.:$owner:capping ttl $ttl at lower bound $ttlmin\n"; + $ttl=$ttlmin; + } + if ($ttl > $ttlmax) { + warn "input:$.:$owner:capping ttl $ttl at upper bound $ttlmax\n"; + $ttl=$ttlmax; + } + $type =~ y/a-z/A-Z/; + die "input:$.:$owner:rr type not permitted:$type\n" + unless $rrt_allowed{$type}; + if (exists $rrset_ttl{$owner,$type}) { + die "input:$.:$owner:$type:RRset has varying TTLs\n" + unless $rrset_ttl{$owner,$type} == $ttl; + } else { + $rrset_ttl{$owner,$type}= $ttl; + } + + die "input:$.:$owner:CNAME and other records, or multiple CNAMEs\n" + if $type eq 'CNAME' + ? exists $owner_types{$owner} + : exists $owner_types{$owner}->{'CNAME'}; + + if ($type eq 'A') { + defined($addr= inet_aton $_) or + die "input:$.:$owner:invalid IP address\n"; + $data= inet_ntoa($addr); + } elsif ($type eq 'CNAME') { + $data= domainsyntax_rel("input:$.:$owner:canonical name",$_)."."; + } elsif ($type eq 'MX') { + m/^(\d+)\s+(\S+)$/ or die "input:$.:$owner:invalid MX syntax\n"; + ($pref,$target) = ($1,$2); + $pref += 0; + die "input:$.:$owner:invalid MX preference\n" + if $pref<0 || $pref>65535; + $target= domainsyntax_rel("input:$.:$owner:mail exchanger",$target); + $data= "$pref $target."; + } else { + die "input:$.:$owner:unsupported RR type:$type\n"; + } + $owner_types{$owner}->{$type}= 1; + + print T "$write_owner $ttl $type $data\n" + or die "write data to temp file:$!\n"; +} + +close T or die "close RR data include:$!\n"; +open STDIN, "< tmp/$$" or die "reopen RR data include:$!\n"; +remove "tmp/$$" or die "close RR data include:$!\n"; + +chdir "zone,$zone" or die "chdir:$zone:$!\n"; + +exec "with-lock-ex","-w","Lock", + "$libdir/update", $zone, $subdomain, @rates; +die "execute update program:$!\n"; + +sub domainsyntax ($$) { + my ($w,$d) = @_; + return if eval { + die "bad char:\`$&'\n" if $d =~ m/[^-.0-9a-z]/; + $d= ".$d."; + die "label starts with hyphen\n" if $d =~ m/\.\-/; + die "label ends with hyphen\n" if $d =~ m/\-\./; + die "empty label or dot at start or end\n" if $d =~ m/\.\./; + die "label too long\n" if $d =~ m/\..{64,}\./; + die "domain name too long\n" if length $d > 255; + 1; + }; + die "$w:invalid domain name:\`$d':$@"; +} + +sub domainsyntax_rel ($$) { + my ($w,$d,$r) = @_; + unless ($d =~ s/\.$//) { + $d .= '.' unless $d =~ s/^\@$//; + $d .= ($subdomain eq '@' ? "$zone" : "$subdomain.$zone"); + } + domainsyntax($w,$d); + return $d; +} diff --git a/dyndns/update b/dyndns/update new file mode 100755 index 0000000..7a72503 --- /dev/null +++ b/dyndns/update @@ -0,0 +1,51 @@ +#!/bin/bash +set -e + +zone="$1" +subdomain="$2" +interval_min="$3" +interval_avg="$4" +interval_mem="$5" + +now=`date +%s` +charge=0 + +case $subdomain in +'@') files=_ ;; +*) files=$subdomain ;; +esac + +if test -f $files,timings && read lastup charge <$files,timings +then + if [ $now -lt $[ $lastup + $interval_min ] ]; then + echo "wait $[ $lastup + $interval_min - $now ]" + echo >&2 "must wait at least $interval_min between updates" + exit 75 + fi + charge=$[ $charge + $interval_avg - ($now - $lastup) ] + if [ $charge -gt $interval_mem ]; then + echo "wait $[ $charge - $interval_mem ]" + echo >&2 "must wait on average $interval_avg between updates" + exit 75 + fi + if [ $charge -lt 0 ]; then charge=0; fi +fi + +sort >$files,new + +if test -f $files,data +then + set +e + diff >/dev/null $files,data $files,new + diff=$? + set -e + + if [ $diff = 0 ]; then echo 'unchanged'; exit 0; fi + if [ $diff != 1 ]; then exit 1; fi +fi + +echo $now $charge >$files,timings.new +mv -f $files,timings.new $files,timings +mv $files,new $files,data + +exec /usr/local/lib/userv/dyndns/install $zone -- 2.30.2