chiark / gitweb /
@@ -1,8 +1,9 @@
authorian <ian>
Sun, 15 Jun 2003 17:33:54 +0000 (17:33 +0000)
committerian <ian>
Sun, 15 Jun 2003 17:33:54 +0000 (17:33 +0000)
-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
debian/changelog
dyndns/INSTALL [new file with mode: 0644]
dyndns/dyndns [new file with mode: 0644]
dyndns/dyndns-domains [new file with mode: 0644]
dyndns/install [new file with mode: 0755]
dyndns/service [new file with mode: 0755]
dyndns/update [new file with mode: 0755]

index ee56142b215f442118edf981c7d0da0a9d070cbb..4fe32ac6b836260f9a49a2f7504c0dbeba43158c 100644 (file)
--- 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.
 
  --
 
index ee56142b215f442118edf981c7d0da0a9d070cbb..4fe32ac6b836260f9a49a2f7504c0dbeba43158c 100644 (file)
@@ -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 (file)
index 0000000..800d5ad
--- /dev/null
@@ -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,<zone-name-without-trailing-dot>/
+#    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 (file)
index 0000000..e73d793
--- /dev/null
@@ -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 (file)
index 0000000..e7b0b35
--- /dev/null
@@ -0,0 +1,51 @@
+# Syntax is list of directives.
+#  include <filename> Read <filename> at this point
+#  eof
+# Settings directives, apply to succeeding subdomain lines.
+#  zone <zone>        This is for stuff in <zone>
+#  rrs <rrtype> ...   Allow addition/removal of (only) these RR types
+#  ratelimit <min-seconds> <avg-seconds> <memory-seconds>
+#    Limit rate of changes.  Up to one per <avg-seconds>
+#    will always be allowed, and never updates more than
+#    <min-seconds> apart.  <avg-seconds> is compared
+#    with events in roughly last <memory-seconds>
+#  ttlrange <minttl> <maxttl>
+# Actual directive:
+#  subdomain <subdomain> <groupname>
+
+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 (executable)
index 0000000..751bb72
--- /dev/null
@@ -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 <Manual -e 's/^[   ]*%SERIAL[      ]*$/ '" $serial;serial/" >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 (executable)
index 0000000..37bf623
--- /dev/null
@@ -0,0 +1,176 @@
+#!/usr/bin/perl
+# usage: (cat RRs; echo .) | userv dyndns <zone> <subdomain>
+# 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 <subdomain>.<zone>, and so must not have a trailing `.';
+#    where the owner is to be <subdomain>.<zone>, `@' 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 <zone> and <domain> 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; $_= <STDIN>;
+    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 (executable)
index 0000000..7a72503
--- /dev/null
@@ -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