Changeset 587 for branches


Ignore:
Timestamp:
01/17/14 10:49:12 (10 years ago)
Author:
Kris Deugau
Message:

/branches/stable

Merge forward bugfixes and option additions from /trunk r583 through r586.
Bump version patch number.

Location:
branches/stable
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/stable

  • branches/stable/DNSDB.pm

    r582 r587  
    3636use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
    3737
    38 $VERSION        = "1.2.2";      ##VERSION##
     38$VERSION        = "1.2.3";      ##VERSION##
    3939@ISA            = qw(Exporter);
    4040@EXPORT_OK      = qw(
     
    213213                force_refresh   => 1,
    214214                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',
    216221        );
    217222
     
    243248
    244249  # 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') {
    246251    if ($self->{$boolopt} ne '1' && $self->{$boolopt} ne '0') {
    247252      # true/false, on/off, yes/no all valid.
     
    253258        }
    254259      } 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};
    257262      }
    258263    }
     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';
    259270  }
    260271
     
    637648# TXT record
    638649sub _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
    639661  # Could arguably put a WARN return here on very long (>512) records
    640662  return ('OK','OK');
     
    683705  ${$args{dist}} =~ s/\s*//g;
    684706  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$/;
    685712
    686713  return ('FAIL',"SRV records must begin with _service._protocol [${$args{host}}]")
     
    696723  ${$args{fields}} = "distance,weight,port,";
    697724  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$/;
    703725
    704726  return ('OK','OK');
     
    11361158    # v4 revzone, formal zone name type
    11371159    my $tmpzone = $zone;
     1160    return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name [$tmpzone]")
     1161        if $tmpzone !~ m{^(?:\d+[/-])?[\d\.]+\.in-addr\.arpa\.?$};
    11381162    $tmpzone =~ s/\.in-addr\.arpa\.?//;
    1139     return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name [$tmpzone]") if $tmpzone !~ m{^(?:\d+[/-])?[\d\.]+$};
    11401163
    11411164    # Snag the octet pieces
     
    11851208    }
    11861209
    1187   } elsif ($zone =~ /\.ip6\.arpa$/) {
     1210  } elsif ($zone =~ /\.ip6\.arpa\.?$/) {
    11881211    # v6 revzone, formal zone name type
    11891212    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\.?$/;
    11901216    $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 segment
    1192     return ('FAIL', "Non-hexadecimals in apparent IPv6 reverse zone name") if $tmpzone !~ /^[a-fA-F\d\.]+$/;
    11931217    my @quads = reverse(split(/\./, $tmpzone));
    11941218    $warnmsg .= "Apparent sub-/64 IPv6 reverse zone\n" if $#quads > 15;
     
    11961220    foreach (@quads) {
    11971221      $tmpcidr .= $_;
    1198       $tmpcidr .= ":" if ++$nc % 4 == 0;
     1222      $tmpcidr .= ":" if ++$nc % 4 == 0 && $nc < $#quads;
    11991223    }
    12001224    my $nq = 1 if $nc % 4 != 0;
     
    12031227      $tmpcidr .= "0";
    12041228    }
    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;
    12061231  }
    12071232
     
    12151240  }
    12161241  return ('OK', $cidr);
     1242##fixme:  use wantarray() to decide what to return?
    12171243} # done _zone2cidr()
    12181244
     
    13431369      $cfg->{force_refresh}     = $1 if /^force_refresh\s*=\s*([a-z01]+)/i;
    13441370      $cfg->{lowercase}         = $1 if /^lowercase\s*=\s*([a-z01]+)/i;
    1345       $cfg->{showrec_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;
    13461372# not supported in dns.cgi yet
    13471373#      $cfg->{templatedir}      = $1 if m{^templatedir\s*=\s*([a-z0-9/_.-]+)}i;
     
    19992025## DNSDB::revName()
    20002026# 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
    20022029# Returns the reverse zone name or undef on failure
    20032030sub revName {
     
    20062033  my $dbh = $self->{dbh};
    20072034  my $revid = shift;
     2035  my $cidrflag = shift || 'n';
    20082036  my ($revname) = $dbh->selectrow_array("SELECT revnet FROM revzones WHERE rdns_id=?", undef, ($revid) );
    20092037  $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';
    20102041  return $revname if $revname;
    20112042} # end revName()
     
    20612092  my $self = shift;
    20622093  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);
    20642103
    20652104  return ('FAIL',"Zone name must be a valid CIDR netblock") unless ($zone && $zone->addr !~ /^0/);
     
    22652304    $sql = "SELECT count(*) FROM revzones".
    22662305        " 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#    }
    22692315  }
    22702316  my ($count) = $dbh->selectrow_array($sql, undef, @filterargs);
     
    23202366        " INNER JOIN groups ON revzones.group_id=groups.group_id".
    23212367        " 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#    }
    23242377  }
    23252378  # A common tail.
     
    23282381        " OFFSET ".$args{offset}*$self->{perpage});
    23292382
    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;
    23322394} # end getZoneList()
    23332395
     
    34913553    if (!$oldsoa) {
    34923554      # 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,?,?)";
    34943557      $dbh->do($sql, undef, ($soa{id}, "$soa{contact}:$soa{prins}",
    34953558        "$soa{refresh}:$soa{retry}:$soa{expire}:$soa{minttl}", $soa{ttl}) );
     
    35853648  my %args = @_;
    35863649
    3587   my @filterargs;
    3588 
    3589   push @filterargs, $args{filter} if $args{filter};
    3590 
    35913650  # protection against bad or missing arguments
    35923651  $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
     
    36133672  $newsort =~ s/^,//;
    36143673
     3674  my @bindvars = ($args{id});
     3675  push @bindvars, ($args{filter},$args{filter}) if $args{filter};
     3676
    36153677##fixme:  do we need a knob to twist to switch from unix epoch to postgres time string?
    36163678  my $sql = "SELECT r.record_id,r.host,r.type,r.val,r.ttl";
     
    36233685  $sql .= "WHERE "._recparent($args{defrec},$args{revrec})." = ?";
    36243686  $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#    }
    36263696  $sql .= " ORDER BY $newsort $args{sortorder}";
    36273697  # ensure consistent ordering by sorting on record_id too
     
    36293699  $sql .= ($args{offset} eq 'all' ? '' : " LIMIT $perpage OFFSET ".$args{offset}*$perpage);
    36303700
    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;
    36383713} # end getRecList()
    36393714
     
    36573732
    36583733  my @bindvars = ($id);
    3659   push @bindvars, $filter if $filter;
     3734  push @bindvars, ($filter,$filter) if $filter;
    36603735  my $sql = "SELECT count(*) FROM ".
    36613736        _rectable($defrec,$revrec).
    36623737        " 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
    36653749  my ($count) = $dbh->selectrow_array($sql, undef, (@bindvars) );
    36663750
     
    37243808
    37253809  # 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  }
    37263816  my $addr = NetAddr::IP->new($$val);
    37273817  $$host =~ s/\.+$//;   # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI.
     
    38883978
    38893979  # 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  }
    38903986  my $addr = NetAddr::IP->new($$val);
    38913987  $$host =~ s/\.+$//;   # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI.
  • branches/stable/Makefile

    r568 r587  
    33
    44PKGNAME=dnsadmin
    5 VERSION=1.2.2
     5VERSION=1.2.3
    66RELEASE=1
    77
  • branches/stable/dns.cgi

    r582 r587  
    20042004
    20052005  foreach my $rec (@$foo2) {
    2006     # NS records.  Need to do this first before we convert the type-value to the text representation
    2007     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     }
    20112006    $rec->{type} = $typemap{$rec->{type}};
    20122007    $rec->{fwdzone} = $rev eq 'n';
     
    20342029
    20352030sub 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
    20392032  no warnings qw( uninitialized );
    20402033
     
    20422035# prefill <domain> or DOMAIN in "Host" space for new records
    20432036  if ($webvar{revrec} eq 'n') {
     2037    $page->param(typelist => $dnsdb->getTypelist($webvar{revrec}, $webvar{type}));
    20442038    my $domroot = ($webvar{defrec} eq 'y' ? 'DOMAIN' : $dnsdb->domainName($webvar{parentid}));
    20452039    $page->param(name   => ($webvar{name} ? $webvar{name} : $domroot));
     
    20522046    my $domroot = ($webvar{defrec} eq 'y' ? 'ADMINDOMAIN' : ".$dnsdb->{domain}");
    20532047    $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;
    20552050    $zname =~ s|\d*/\d+$||;
    20562051    $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'})));
    20572054  }
    20582055# retrieve the right ttl instead of falling (way) back to the hardcoded system default
     
    21832180        );
    21842181# probably don't need this, keeping for reference for now
    2185 #  foreach (@$zonelist) {
     2182#  foreach my $rec (@$zonelist) {
    21862183#  }
    21872184  $page->param(domtable => $zonelist);
  • branches/stable/dnsdb.conf

    r582 r587  
    3838#lowercase = 0
    3939
    40 # Show formal .arpa zone name instead of usual CIDR for reverse zone NS records?
    41 #showrec_arpa_ns = 0
     40# Show formal .arpa zone name instead of the natural IP or CIDR for reverse zone names and records?
     41#showrev_arpa = 0
    4242
    4343## General RPC options
  • branches/stable/textrecs.cgi

    r582 r587  
    8383  $rec->{val} = "$rec->{distance}  $rec->{weight}  $rec->{port}  $rec->{val}" if $rec->{type} eq 'SRV';
    8484  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     }
    8985    printf "%-16s\t%d\t%s\t%s\n", $rec->{val}, $rec->{ttl}, $rec->{type}, $rec->{host};
    9086  } else {
Note: See TracChangeset for help on using the changeset viewer.