2 # usage: (cat RRs; echo .) | userv dyndns <zone> <subdomain>
3 # Not all zone file formats are accepted:
4 # - All RRs must have owners specified.
5 # - All RRs must have TTLs specified.
6 # - The owner must be specified as a sub-subdomain, relative
7 # to <subdomain>.<zone>, and so must not have a trailing `.';
8 # where the owner is to be <subdomain>.<zone>, `@' must be used.
10 # Copyright (C) 1999-2000,2003 Ian Jackson
12 # This file is part dyndns, part of userv-utils
14 # This is free software; you can redistribute it and/or modify it
15 # under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; either version 2 of the License, or
17 # (at your option) any later version.
19 # This program is distributed in the hope that it will be useful, but
20 # WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 # General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with userv-utils; if not, write to the Free Software
26 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 $vardir= "/var/lib/userv/dyndns";
34 $defconf= "/etc/userv/dyndns-domains";
35 $libdir= "/usr/share/userv/dyndns";
38 remove
"$vardir/tmp/$$" or $! == ENOENT
or
39 warn "cannot remove tempfile:$!\n";
47 @ARGV==2 or die "need <zone> and <domain> arguments\n";
48 ($zone,$subdomain) = @ARGV;
49 domainsyntax
("command line",$zone);
50 domainsyntax
("command line",$subdomain) unless $subdomain eq '@';
52 @userv_groups= split m/ /, $ENV{'USERV_GROUP'};
61 $fh->open("< $cf") or die "$cf: $!\n";
64 length or die "$cf:".($? ?
"read:$?" : "eof")."\n";
65 s/^\s+//; chomp; s/\s+$//;
67 next if m/^\#/ or !m/\S/;
68 if (m/^zone\s+(\S+)$/) {
69 $thiszone= $1 eq $zone;
70 } elsif (m/^ratelimit\s+(\d+)\s+(\d+)\s+(\d+)$/) {
72 } elsif (m/^ttlrange\s+(\d+)\s+(\d+)$/) {
73 ($ttlmin,$ttlmax) = ($1,$2);
74 } elsif (m/^rrs\s+([A-Za-z0-9 \t]+)$/) {
77 grep { y/a-z/A-Z/; $rrt_allowed{$_}= 1; } split m/\s+/, $1;
78 } elsif (m/^include\s+(\S.*)$/) {
79 return if readconf
($1);
80 } elsif (m/^subdomain\s+(\S+)\s+(\S+)$/) {
81 next unless $thiszone;
82 next unless $1 eq $subdomain;
83 next unless grep { $_ eq $2 } @userv_groups;
86 die "$cf:$.: config error\n";
89 close $fh or die "$cf: close: $!\n";
94 or die "permission denied\n";
96 chdir "$vardir" or die "chdir dyndns:$!\n";
98 open T
,">tmp/$$" or die "create temp file: $!\n";
102 die "input:$.:".($? ?
"$?" : "eof") unless length;
105 s/^(\S+)\s+(\d+)\s+([A-Za-z][0-9A-Za-z]*)\s+//
106 or die "input:$.:bogus line\n";
107 ($owner,$ttl,$type)= ($1,$2,$3);
109 $write_owner= $subdomain;
111 domainsyntax
("input:$.",$owner) unless $owner eq '@';
112 $write_owner= $subdomain eq '@' ?
$owner : "$owner.$subdomain";
114 length "$write_owner.$zone." < 255
115 or die "input:$.:$owner:resulting domain name too long\n";
118 if ($ttl < $ttlmin) {
119 warn "input:$.:$owner:capping ttl $ttl at lower bound $ttlmin\n";
122 if ($ttl > $ttlmax) {
123 warn "input:$.:$owner:capping ttl $ttl at upper bound $ttlmax\n";
127 die "input:$.:$owner:rr type not permitted:$type\n"
128 unless $rrt_allowed{$type};
129 if (exists $rrset_ttl{$owner,$type}) {
130 die "input:$.:$owner:$type:RRset has varying TTLs\n"
131 unless $rrset_ttl{$owner,$type} == $ttl;
133 $rrset_ttl{$owner,$type}= $ttl;
136 die "input:$.:$owner:CNAME and other records, or multiple CNAMEs\n"
138 ?
exists $owner_types{$owner}
139 : exists $owner_types{$owner}->{'CNAME'};
142 defined($addr= inet_aton
$_) or
143 die "input:$.:$owner:invalid IP address\n";
144 $data= inet_ntoa
($addr);
145 } elsif ($type eq 'AAAA') {
146 defined($addr= inet_pton
(AF_INET6
, $_)) or
147 die "input:$.:$owner:invalid IPv6 address\n";
148 $data = inet_ntop
(AF_INET6
, $addr);
149 } elsif ($type eq 'CNAME') {
150 $data= domainsyntax_rel
("input:$.:$owner:canonical name",$_).".";
151 } elsif ($type eq 'MX') {
152 m/^(\d+)\s+(\S+)$/ or die "input:$.:$owner:invalid MX syntax\n";
153 ($pref,$target) = ($1,$2);
155 die "input:$.:$owner:invalid MX preference\n"
156 if $pref<0 || $pref>65535;
157 $target= domainsyntax_rel
("input:$.:$owner:mail exchanger",$target);
158 $data= "$pref $target.";
160 die "input:$.:$owner:unsupported RR type:$type\n";
162 $owner_types{$owner}->{$type}= 1;
164 print T
"$write_owner $ttl $type $data\n"
165 or die "write data to temp file:$!\n";
168 close T
or die "close RR data include:$!\n";
169 open STDIN
, "< tmp/$$" or die "reopen RR data include:$!\n";
170 remove
"tmp/$$" or die "close RR data include:$!\n";
172 chdir "zone,$zone" or die "chdir:$zone:$!\n";
174 exec "with-lock-ex","-w","Lock",
175 "$libdir/update", $zone, $subdomain, @rates;
176 die "execute update program:$!\n";
178 sub domainsyntax
($$) {
181 die "bad char:\`$&'\n" if $d =~ m/[^-.0-9a-z]/;
183 die "label starts with hyphen\n" if $d =~ m/\.\-/;
184 die "label ends with hyphen\n" if $d =~ m/\-\./;
185 die "empty label or dot at start or end\n" if $d =~ m/\.\./;
186 die "label too long\n" if $d =~ m/\..{64,}\./;
187 die "domain name too long\n" if length $d > 255;
190 die "$w:invalid domain name:\`$d':$@";
193 sub domainsyntax_rel
($$) {
195 unless ($d =~ s/\.$//) {
196 $d .= '.' unless $d =~ s/^\@$//;
197 $d .= ($subdomain eq '@' ?
"$zone" : "$subdomain.$zone");