summaryrefslogtreecommitdiff
path: root/dyndns
diff options
context:
space:
mode:
authorian <ian>2003-06-15 17:33:54 +0000
committerian <ian>2003-06-15 17:33:54 +0000
commit3988a733004005983b60aba74306368c122180f2 (patch)
treed6fb1154de165606249b7bee1762675159cf88bc /dyndns
parent415964ddbc92d16b1fbd199b252b6ae6569411c6 (diff)
downloaduserv-utils-3988a733004005983b60aba74306368c122180f2.tar.gz
@@ -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. --
Diffstat (limited to 'dyndns')
-rw-r--r--dyndns/INSTALL41
-rw-r--r--dyndns/dyndns9
-rw-r--r--dyndns/dyndns-domains51
-rwxr-xr-xdyndns/install18
-rwxr-xr-xdyndns/service176
-rwxr-xr-xdyndns/update51
6 files changed, 346 insertions, 0 deletions
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,<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
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 <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
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 <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
index 0000000..37bf623
--- /dev/null
+++ b/dyndns/service
@@ -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
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