- Timestamp:
- 01/17/14 10:49:12 (11 years ago)
- Location:
- branches/stable
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable
-
branches/stable/DNSDB.pm
r582 r587 36 36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 37 37 38 $VERSION = "1.2. 2"; ##VERSION##38 $VERSION = "1.2.3"; ##VERSION## 39 39 @ISA = qw(Exporter); 40 40 @EXPORT_OK = qw( … … 213 213 force_refresh => 1, 214 214 lowercase => 0, # mangle as little as possible by default 215 showrec_arpa_ns => 0, # show formal .arpa zone name instead of logical CIDR on reverse NS records 215 # show IPs and CIDR blocks as-is for reverse zones. valid values are 216 # 'none' (default, show natural IP or CIDR) 217 # 'zone' (zone name, wherever used) 218 # 'record' (IP or CIDR values in reverse record lists) 219 # 'all' (all IP values in any reverse zone view) 220 showrev_arpa => 'none', 216 221 ); 217 222 … … 243 248 244 249 # Several settings are booleans. Handle multiple possible ways of setting them. 245 for my $boolopt ('log_failures', 'force_refresh', 'lowercase', 'usecache' , 'showrec_arpa_ns') {250 for my $boolopt ('log_failures', 'force_refresh', 'lowercase', 'usecache') { 246 251 if ($self->{$boolopt} ne '1' && $self->{$boolopt} ne '0') { 247 252 # true/false, on/off, yes/no all valid. … … 253 258 } 254 259 } else { 255 warn "Bad $boolopt setting $self->{$boolopt} \n";256 $self->{$boolopt} = 1;260 warn "Bad $boolopt setting $self->{$boolopt}, using default\n"; 261 $self->{$boolopt} = $defconfig{$boolopt}; 257 262 } 258 263 } 264 } 265 266 # Enum-ish option(s) 267 if (!grep /$self->{showrev_arpa}/, ('none','zone','record','all')) { 268 warn "Bad showrev_arpa setting $self->{showrev_arpa}, using default\n"; 269 $self->{showrev_arpa} = 'none'; 259 270 } 260 271 … … 637 648 # TXT record 638 649 sub _validate_16 { 650 my $self = shift; 651 652 my %args = @_; 653 654 if ($args{revrec} eq 'y') { 655 # Coerce all hostnames to end in ".DOMAIN" for group/default records, 656 # or the intended parent domain for live records. 657 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 658 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/; 659 } 660 639 661 # Could arguably put a WARN return here on very long (>512) records 640 662 return ('OK','OK'); … … 683 705 ${$args{dist}} =~ s/\s*//g; 684 706 return ('FAIL',"Distance is required, and must be numeric") unless ${$args{dist}} =~ /^\d+$/; 707 708 # Coerce all hostnames to end in ".DOMAIN" for group/default records, 709 # or the intended parent domain for live records. 710 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 711 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/; 685 712 686 713 return ('FAIL',"SRV records must begin with _service._protocol [${$args{host}}]") … … 696 723 ${$args{fields}} = "distance,weight,port,"; 697 724 push @{$args{vallist}}, (${$args{dist}}, ${$args{weight}}, ${$args{port}}); 698 699 # Coerce all hostnames to end in ".DOMAIN" for group/default records,700 # or the intended parent domain for live records.701 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id}));702 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/;703 725 704 726 return ('OK','OK'); … … 1136 1158 # v4 revzone, formal zone name type 1137 1159 my $tmpzone = $zone; 1160 return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name [$tmpzone]") 1161 if $tmpzone !~ m{^(?:\d+[/-])?[\d\.]+\.in-addr\.arpa\.?$}; 1138 1162 $tmpzone =~ s/\.in-addr\.arpa\.?//; 1139 return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name [$tmpzone]") if $tmpzone !~ m{^(?:\d+[/-])?[\d\.]+$};1140 1163 1141 1164 # Snag the octet pieces … … 1185 1208 } 1186 1209 1187 } elsif ($zone =~ /\.ip6\.arpa $/) {1210 } elsif ($zone =~ /\.ip6\.arpa\.?$/) { 1188 1211 # v6 revzone, formal zone name type 1189 1212 my $tmpzone = $zone; 1213 ##fixme: if-n-when we decide we can support sub-nibble v6 zone names, we'll need to change this segment 1214 return ('FAIL', "Non-hexadecimals in apparent IPv6 reverse zone name [$tmpzone]") 1215 if $tmpzone !~ /^[a-fA-F\d\.]+\.ip6\.arpa\.?$/; 1190 1216 $tmpzone =~ s/\.ip6\.arpa\.?//; 1191 ##fixme: if-n-when we decide we can support sub-nibble v6 zone names, we'll need to change this segment1192 return ('FAIL', "Non-hexadecimals in apparent IPv6 reverse zone name") if $tmpzone !~ /^[a-fA-F\d\.]+$/;1193 1217 my @quads = reverse(split(/\./, $tmpzone)); 1194 1218 $warnmsg .= "Apparent sub-/64 IPv6 reverse zone\n" if $#quads > 15; … … 1196 1220 foreach (@quads) { 1197 1221 $tmpcidr .= $_; 1198 $tmpcidr .= ":" if ++$nc % 4 == 0 ;1222 $tmpcidr .= ":" if ++$nc % 4 == 0 && $nc < $#quads; 1199 1223 } 1200 1224 my $nq = 1 if $nc % 4 != 0; … … 1203 1227 $tmpcidr .= "0"; 1204 1228 } 1205 $tmpcidr .= ($nq ? '::' : ':')."/$mask"; 1229 # polish it off with trailing ::/mask if this is a CIDR block instead of an IP 1230 $tmpcidr .= "::/$mask" if $mask != 128; 1206 1231 } 1207 1232 … … 1215 1240 } 1216 1241 return ('OK', $cidr); 1242 ##fixme: use wantarray() to decide what to return? 1217 1243 } # done _zone2cidr() 1218 1244 … … 1343 1369 $cfg->{force_refresh} = $1 if /^force_refresh\s*=\s*([a-z01]+)/i; 1344 1370 $cfg->{lowercase} = $1 if /^lowercase\s*=\s*([a-z01]+)/i; 1345 $cfg->{showre c_arpa_ns} = $1 if /^showrec_arpa_ns\s*=\s*([a-z01]+)/i;1371 $cfg->{showrev_arpa} = $1 if /^showrev_arpa\s*=\s*([a-z]+)/i; 1346 1372 # not supported in dns.cgi yet 1347 1373 # $cfg->{templatedir} = $1 if m{^templatedir\s*=\s*([a-z0-9/_.-]+)}i; … … 1999 2025 ## DNSDB::revName() 2000 2026 # Return the reverse zone name based on an rDNS ID 2001 # Takes a database handle and the rDNS ID 2027 # Takes a database handle and the rDNS ID, and an optional flag to force return of the CIDR zone 2028 # instead of the formal .arpa zone name 2002 2029 # Returns the reverse zone name or undef on failure 2003 2030 sub revName { … … 2006 2033 my $dbh = $self->{dbh}; 2007 2034 my $revid = shift; 2035 my $cidrflag = shift || 'n'; 2008 2036 my ($revname) = $dbh->selectrow_array("SELECT revnet FROM revzones WHERE rdns_id=?", undef, ($revid) ); 2009 2037 $errstr = $DBI::errstr if !$revname; 2038 my $tmp = new NetAddr::IP $revname; 2039 $revname = _ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa') 2040 if ($self->{showrev_arpa} eq 'zone' || $self->{showrev_arpa} eq 'all') && $cidrflag eq 'n'; 2010 2041 return $revname if $revname; 2011 2042 } # end revName() … … 2061 2092 my $self = shift; 2062 2093 my $dbh = $self->{dbh}; 2063 my $zone = NetAddr::IP->new(shift); 2094 my $zone = shift; 2095 2096 # Autodetect formal .arpa zones 2097 if ($zone =~ /\.arpa\.?$/) { 2098 my $code; 2099 ($code,$zone) = _zone2cidr($zone); 2100 return ('FAIL', $zone) if $code eq 'FAIL'; 2101 } 2102 $zone = NetAddr::IP->new($zone); 2064 2103 2065 2104 return ('FAIL',"Zone name must be a valid CIDR netblock") unless ($zone && $zone->addr !~ /^0/); … … 2265 2304 $sql = "SELECT count(*) FROM revzones". 2266 2305 " WHERE group_id IN ($args{curgroup}".($args{childlist} ? ",$args{childlist}" : '').")". 2267 ($args{startwith} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''). 2268 ($args{filter} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''); 2306 ($args{startwith} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''); 2307 # if ($self->{showrev_arpa} eq 'zone' || $self->{showrev_arpa} eq 'all') { 2308 # Just In Case the UI is using formal .arpa notation, and someone enters something reversed, 2309 # we want to match both the formal and natural zone name 2310 $sql .= ($args{filter} ? " AND (CAST(revnet AS VARCHAR) ~* ? OR CAST(revnet AS VARCHAR) ~* ?)" : ''); 2311 push @filterargs, join('[.]',reverse(split(/\[\.\]/,$args{filter}))) if $args{filter}; 2312 # } else { 2313 # $sql .= ($args{filter} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''); 2314 # } 2269 2315 } 2270 2316 my ($count) = $dbh->selectrow_array($sql, undef, @filterargs); … … 2320 2366 " INNER JOIN groups ON revzones.group_id=groups.group_id". 2321 2367 " WHERE revzones.group_id IN ($args{curgroup}".($args{childlist} ? ",$args{childlist}" : '').")". 2322 ($args{startwith} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''). 2323 ($args{filter} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''); 2368 ($args{startwith} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''); 2369 # if ($self->{showrev_arpa} eq 'zone' || $self->{showrev_arpa} eq 'all') { 2370 # Just In Case the UI is using formal .arpa notation, and someone enters something reversed, 2371 # we want to match both the formal and natural zone name 2372 $sql .= ($args{filter} ? " AND (CAST(revnet AS VARCHAR) ~* ? OR CAST(revnet AS VARCHAR) ~* ?)" : ''); 2373 push @filterargs, join('[.]',reverse(split(/\[\.\]/,$args{filter}))) if $args{filter}; 2374 # } else { 2375 # $sql .= ($args{filter} ? " AND CAST(revnet AS VARCHAR) ~* ?" : ''); 2376 # } 2324 2377 } 2325 2378 # A common tail. … … 2328 2381 " OFFSET ".$args{offset}*$self->{perpage}); 2329 2382 2330 my $ret = $dbh->selectall_arrayref($sql, { Slice => {} }, @filterargs); 2331 return $ret; 2383 my @working; 2384 my $zsth = $dbh->prepare($sql); 2385 $zsth->execute(@filterargs); 2386 while (my $zone = $zsth->fetchrow_hashref) { 2387 if ($args{revrec} eq 'y' && ($self->{showrev_arpa} eq 'zone' || $self->{showrev_arpa} eq 'all')) { 2388 my $tmp = new NetAddr::IP $zone->{zone}; 2389 $zone->{zone} = DNSDB::_ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa'); 2390 } 2391 push @working, $zone; 2392 } 2393 return \@working; 2332 2394 } # end getZoneList() 2333 2395 … … 3491 3553 if (!$oldsoa) { 3492 3554 # old SOA record is missing for some reason. create a new one. 3493 my $sql = "INSERT INTO "._rectable($defrec, $revrec)." (group_id, host, type, val, ttl) VALUES (?,?,6,?,?)"; 3555 my $sql = "INSERT INTO "._rectable($defrec, $revrec)." ("._recparent($defrec, $revrec). 3556 ", host, type, val, ttl) VALUES (?,?,6,?,?)"; 3494 3557 $dbh->do($sql, undef, ($soa{id}, "$soa{contact}:$soa{prins}", 3495 3558 "$soa{refresh}:$soa{retry}:$soa{expire}:$soa{minttl}", $soa{ttl}) ); … … 3585 3648 my %args = @_; 3586 3649 3587 my @filterargs;3588 3589 push @filterargs, $args{filter} if $args{filter};3590 3591 3650 # protection against bad or missing arguments 3592 3651 $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC'); … … 3613 3672 $newsort =~ s/^,//; 3614 3673 3674 my @bindvars = ($args{id}); 3675 push @bindvars, ($args{filter},$args{filter}) if $args{filter}; 3676 3615 3677 ##fixme: do we need a knob to twist to switch from unix epoch to postgres time string? 3616 3678 my $sql = "SELECT r.record_id,r.host,r.type,r.val,r.ttl"; … … 3623 3685 $sql .= "WHERE "._recparent($args{defrec},$args{revrec})." = ?"; 3624 3686 $sql .= " AND NOT r.type=$reverse_typemap{SOA}"; 3625 $sql .= " AND (r.host ~* ? OR r.val ~* ?)" if $args{filter}; 3687 # if ($self->{showrev_arpa} eq 'zone' || $self->{showrev_arpa} eq 'all') { 3688 # Just In Case the UI is using formal .arpa notation, and someone enters something reversed, 3689 # we want to match both the formal and natural zone name 3690 $sql .= " AND (r.host ~* ? OR r.val ~* ? OR r.host ~* ? OR r.val ~* ?)" if $args{filter}; 3691 my $tmp = join('.',reverse(split(/\./,$args{filter}))); 3692 push @bindvars, ($tmp, $tmp) if $args{filter}; 3693 # } else { 3694 # $sql .= " AND (r.host ~* ? OR r.val ~* ?)" if $args{filter}; 3695 # } 3626 3696 $sql .= " ORDER BY $newsort $args{sortorder}"; 3627 3697 # ensure consistent ordering by sorting on record_id too … … 3629 3699 $sql .= ($args{offset} eq 'all' ? '' : " LIMIT $perpage OFFSET ".$args{offset}*$perpage); 3630 3700 3631 my @bindvars = ($args{id}); 3632 push @bindvars, ($args{filter},$args{filter}) if $args{filter}; 3633 3634 my $ret = $dbh->selectall_arrayref($sql, { Slice => {} }, (@bindvars) ); 3635 $errstr = "Error retrieving records: ".$dbh->errstr if !$ret; 3636 3637 return $ret; 3701 my @working; 3702 my $recsth = $dbh->prepare($sql); 3703 $recsth->execute(@bindvars); 3704 while (my $rec = $recsth->fetchrow_hashref) { 3705 if ($args{revrec} eq 'y' && ($self->{showrev_arpa} eq 'record' || $self->{showrev_arpa} eq 'all')) { 3706 ##enhance: extend {showrev_arpa} eq 'record' to specify record types 3707 my $tmp = new NetAddr::IP $rec->{val}; 3708 $rec->{val} = DNSDB::_ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa'); 3709 } 3710 push @working, $rec; 3711 } 3712 return \@working; 3638 3713 } # end getRecList() 3639 3714 … … 3657 3732 3658 3733 my @bindvars = ($id); 3659 push @bindvars, $filterif $filter;3734 push @bindvars, ($filter,$filter) if $filter; 3660 3735 my $sql = "SELECT count(*) FROM ". 3661 3736 _rectable($defrec,$revrec). 3662 3737 " WHERE "._recparent($defrec,$revrec)."=? ". 3663 "AND NOT type=$reverse_typemap{SOA}". 3664 ($filter ? " AND host ~* ?" : ''); 3738 "AND NOT type=$reverse_typemap{SOA}"; 3739 # if ($self->{showrev_arpa} eq 'zone' || $self->{showrev_arpa} eq 'all') { 3740 # Just In Case the UI is using formal .arpa notation, and someone enters something reversed, 3741 # we want to match both the formal and natural zone name 3742 $sql .= " AND (host ~* ? OR val ~* ? OR host ~* ? OR val ~* ?)" if $filter; 3743 my $tmp = join('.',reverse(split(/\./,$filter))); 3744 push @bindvars, ($tmp, $tmp) if $filter; 3745 # } else { 3746 # $sql .= " AND (host ~* ? OR val ~* ?)" if $filter; 3747 # } 3748 3665 3749 my ($count) = $dbh->selectrow_array($sql, undef, (@bindvars) ); 3666 3750 … … 3724 3808 3725 3809 # prep for validation 3810 # Autodetect formal .arpa names 3811 if ($$val =~ /\.arpa\.?$/) { 3812 my ($code,$tmpval) = _zone2cidr($$val); 3813 return ('FAIL', $tmpval) if $code eq 'FAIL'; 3814 $$val = $tmpval; 3815 } 3726 3816 my $addr = NetAddr::IP->new($$val); 3727 3817 $$host =~ s/\.+$//; # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI. … … 3888 3978 3889 3979 # prep for validation 3980 # Autodetect formal .arpa names 3981 if ($$val =~ /\.arpa\.?$/) { 3982 my ($code,$tmpval) = _zone2cidr($$val); 3983 return ('FAIL', $tmpval) if $code eq 'FAIL'; 3984 $$val = $tmpval; 3985 } 3890 3986 my $addr = NetAddr::IP->new($$val); 3891 3987 $$host =~ s/\.+$//; # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI. -
branches/stable/Makefile
r568 r587 3 3 4 4 PKGNAME=dnsadmin 5 VERSION=1.2. 25 VERSION=1.2.3 6 6 RELEASE=1 7 7 -
branches/stable/dns.cgi
r582 r587 2004 2004 2005 2005 foreach my $rec (@$foo2) { 2006 # NS records. Need to do this first before we convert the type-value to the text representation2007 if ($rev eq 'y' && $dnsdb->{showrec_arpa_ns} && $rec->{type} == $reverse_typemap{NS}) {2008 my $tmp = new NetAddr::IP $rec->{val};2009 $rec->{val} = DNSDB::_ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa');2010 }2011 2006 $rec->{type} = $typemap{$rec->{type}}; 2012 2007 $rec->{fwdzone} = $rev eq 'n'; … … 2034 2029 2035 2030 sub fill_recdata { 2036 $page->param(typelist => $dnsdb->getTypelist($webvar{revrec}, $webvar{type})); 2037 2038 # le sigh. we may get called with many empty %webvar keys 2031 # le sigh. we may get called with many empty %webvar keys 2039 2032 no warnings qw( uninitialized ); 2040 2033 … … 2042 2035 # prefill <domain> or DOMAIN in "Host" space for new records 2043 2036 if ($webvar{revrec} eq 'n') { 2037 $page->param(typelist => $dnsdb->getTypelist($webvar{revrec}, $webvar{type})); 2044 2038 my $domroot = ($webvar{defrec} eq 'y' ? 'DOMAIN' : $dnsdb->domainName($webvar{parentid})); 2045 2039 $page->param(name => ($webvar{name} ? $webvar{name} : $domroot)); … … 2052 2046 my $domroot = ($webvar{defrec} eq 'y' ? 'ADMINDOMAIN' : ".$dnsdb->{domain}"); 2053 2047 $page->param(name => ($webvar{name} ? $webvar{name} : $domroot)); 2054 my $zname = ($webvar{defrec} eq 'y' ? 'ZONE' : $dnsdb->revName($webvar{parentid})); 2048 my $zname = ($webvar{defrec} eq 'y' ? 'ZONE' : $dnsdb->revName($webvar{parentid}, 'y')); 2049 my $cidr = new NetAddr::IP $zname; 2055 2050 $zname =~ s|\d*/\d+$||; 2056 2051 $page->param(address => ($webvar{address} ? $webvar{address} : $zname)); 2052 $page->param(typelist => $dnsdb->getTypelist($webvar{revrec}, 2053 $webvar{type} || ($cidr->{isv6} ? $reverse_typemap{'AAAA+PTR'} : $reverse_typemap{'A+PTR'}))); 2057 2054 } 2058 2055 # retrieve the right ttl instead of falling (way) back to the hardcoded system default … … 2183 2180 ); 2184 2181 # probably don't need this, keeping for reference for now 2185 # foreach (@$zonelist) {2182 # foreach my $rec (@$zonelist) { 2186 2183 # } 2187 2184 $page->param(domtable => $zonelist); -
branches/stable/dnsdb.conf
r582 r587 38 38 #lowercase = 0 39 39 40 # Show formal .arpa zone name instead of usual CIDR for reverse zone NSrecords?41 #showre c_arpa_ns= 040 # Show formal .arpa zone name instead of the natural IP or CIDR for reverse zone names and records? 41 #showrev_arpa = 0 42 42 43 43 ## General RPC options -
branches/stable/textrecs.cgi
r582 r587 83 83 $rec->{val} = "$rec->{distance} $rec->{weight} $rec->{port} $rec->{val}" if $rec->{type} eq 'SRV'; 84 84 if ($webvar{revrec} eq 'y') { 85 if ($dnsdb->{showrec_arpa_ns} && $rec->{type} eq 'NS') {86 my $tmp = new NetAddr::IP $rec->{val};87 $rec->{val} = DNSDB::_ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa');88 }89 85 printf "%-16s\t%d\t%s\t%s\n", $rec->{val}, $rec->{ttl}, $rec->{type}, $rec->{host}; 90 86 } else {
Note:
See TracChangeset
for help on using the changeset viewer.