Changeset 543


Ignore:
Timestamp:
Dec 10, 2013, 4:22:10 PM (6 years ago)
Author:
Kris Deugau
Message:

/trunk

Implement most of the UI and back end for handling scheduled changes
to records. See #40.

This turned out to be most of what I had vaguely imagined; only SOA
records can't sanely be set for scheduled changes yet (can't think of
a scenario where this would even be useful) and there's only a small
dusting of UI chrome left for another time.

Bumped up from projected 1.4 to 1.2 per request from Reid Sutherland.

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/DNSDB.pm

    r542 r543  
    3131use POSIX;
    3232use Fcntl qw(:flock);
     33use Time::TAI64 qw(:tai64);
    3334
    3435use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
     
    35363537  my $id = shift;
    35373538
     3539##fixme: do we need a knob to twist to switch between unix epoch and postgres time string?
    35383540  my $sql = "SELECT record_id,host,type,val,ttl".
    35393541        ($defrec eq 'n' ? ',location' : '').
    35403542        ($revrec eq 'n' ? ',distance,weight,port' : '').
    3541         (($defrec eq 'y') ? ',group_id FROM ' : ',domain_id,rdns_id FROM ').
     3543        (($defrec eq 'y') ? ',group_id FROM ' : ',domain_id,rdns_id,stamp,stamp < now() AS ispast,expires,stampactive FROM ').
    35423544        _rectable($defrec,$revrec)." WHERE record_id=?";
    35433545  my $ret = $dbh->selectrow_hashref($sql, undef, ($id) );
     
    36083610  $newsort =~ s/^,//;
    36093611
     3612##fixme:  do we need a knob to twist to switch from unix epoch to postgres time string?
    36103613  my $sql = "SELECT r.record_id,r.host,r.type,r.val,r.ttl";
    3611   $sql .= ",l.description AS locname" if $args{defrec} eq 'n';
     3614  $sql .= ",l.description AS locname,stamp,r.stamp < now() AS ispast,r.expires,r.stampactive"
     3615        if $args{defrec} eq 'n';
    36123616  $sql .= ",r.distance,r.weight,r.port" if $args{revrec} eq 'n';
    36133617  $sql .= " FROM "._rectable($args{defrec},$args{revrec})." r ";
     
    36273631  my $ret = $dbh->selectall_arrayref($sql, { Slice => {} }, (@bindvars) );
    36283632  $errstr = "Error retrieving records: ".$dbh->errstr if !$ret;
     3633
    36293634  return $ret;
    36303635} # end getRecList()
     
    36853690  my $location = shift;
    36863691  $location  = '' if !$location;
     3692
     3693  my $expires = shift;
     3694  $expires = 1 if $expires eq 'until';  # Turn some special values into the appropriate booleans.
     3695  $expires = 0 if $expires eq 'after';
     3696  my $stamp = shift;
     3697  $stamp = '' if !$stamp;        # Timestamp should be a string at this point.
    36873698
    36883699  # Spaces are evil.
     
    37453756  push @vallist, ($$host,$$rectype,$$val,$ttl,$id);
    37463757
    3747   # locations are not for default records, silly coder!
    37483758  if ($defrec eq 'n') {
     3759    # locations are not for default records, silly coder!
    37493760    $fields .= ",location";
    37503761    push @vallist, $location;
    3751   }
     3762    # timestamps are rare.
     3763    if ($stamp) {
     3764      $fields .= ",stamp,expires,stampactive";
     3765      push @vallist, $stamp, $expires, 'y';
     3766    } else {
     3767      $fields .= ",stampactive";
     3768      push @vallist, 'n';
     3769    }
     3770  }
     3771
     3772  # a little magic to get the right number of ? placeholders based on how many values we're providing
    37523773  my $vallen = '?'.(',?'x$#vallist);
    37533774
     
    37773798  $logdata{entry} .= "', TTL $ttl";
    37783799  $logdata{entry} .= ", location ".$self->getLoc($location)->{description} if $location;
     3800  $logdata{entry} .= ($expires eq 'after' ? ', valid after ' : ', expires at ').$stamp if $stamp;
    37793801
    37803802  # Allow transactions, and raise an exception on errors so we can catch it later.
     
    38303852  $location  = '' if !$location;
    38313853
     3854  my $expires = shift;
     3855  $expires = 1 if $expires eq 'until';  # Turn some special values into the appropriate booleans.
     3856  $expires = 0 if $expires eq 'after';
     3857  my $stamp = shift;
     3858  $stamp = '' if !$stamp;        # Timestamp should be a string at this point.
     3859
    38323860  # just set it to an empty string;  failures will be caught later.
    38333861  $$host = '' if !$$host;
     
    39013929        ($defrec eq 'y' ? $oldrec->{group_id} : ($revrec eq 'n' ? $oldrec->{domain_id} : $oldrec->{rdns_id})) );
    39023930
    3903   # locations are not for default records, silly coder!
    39043931  if ($defrec eq 'n') {
     3932    # locations are not for default records, silly coder!
    39053933    $fields .= ",location";
    39063934    push @vallist, $location;
     3935    # timestamps are rare.
     3936    if ($stamp) {
     3937      $fields .= ",stamp,expires,stampactive";
     3938      push @vallist, $stamp, $expires, 'y';
     3939    } else {
     3940      $fields .= ",stampactive";
     3941      push @vallist, 'n';
     3942    }
    39073943  }
    39083944
     
    39583994  $logdata{entry} .= "', TTL $oldrec->{ttl}";
    39593995  $logdata{entry} .= ", location ".$self->getLoc($oldrec->{location})->{description} if $oldrec->{location};
     3996  $logdata{entry} .= ($oldrec->{expires} ? ', expires at ' : ', valid after ').$oldrec->{stamp}
     3997        if $oldrec->{stampactive};
    39603998  $logdata{entry} .= "\nto\n";
    39613999  # More NS special
     
    39694007  $logdata{entry} .= "', TTL $ttl";
    39704008  $logdata{entry} .= ", location ".$self->getLoc($location)->{description} if $location;
     4009  $logdata{entry} .= ($expires eq 'after' ? ', valid after ' : ', expires at ').$stamp if $stamp;
    39714010
    39724011  local $dbh->{AutoCommit} = 0;
     
    49755014  my $soasth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
    49765015        "FROM records WHERE rdns_id=? AND type=6");
    4977   my $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
     5016  my $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
    49785017        "FROM records WHERE rdns_id=? AND not type=6 ".
    49795018        "ORDER BY masklen(CAST(val AS inet)) DESC, CAST(val AS inet)");
     
    50085047
    50095048        $recsth->execute($revid);
    5010         while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc) = $recsth->fetchrow_array) {
     5049        while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc,$stamp,$expires,$stampactive) = $recsth->fetchrow_array) {
    50115050          next if $recflags{$recid};
    50125051
    5013           $loc = '' if !$loc;   # de-nullify - just in case
    5014 ##fixme:  handle case of record-with-location-that-doesn't-exist better.
    5015 # note this currently fails safe (tested) - records with a location that
    5016 # doesn't exist will not be sent to any client
    5017 #       $loc = '' if !$lochash->{$loc};
    5018 
    5019 ##fixme:  record validity timestamp. tinydns supports fiddling with timestamps.
    5020 # note $ttl must be set to 0 if we want to use tinydns's auto-expiring timestamps.
    5021 # timestamps are TAI64
    5022 # ~~ 2^62 + time()
    5023           my $stamp = '';
    5024 
    5025           # support tinydns' auto-TTL
    5026           $ttl = '' if $ttl == -1;
     5052# not sure this is necessary for revzones.
     5053#         # Spaces are evil.
     5054#         $val =~ s/^\s+//;
     5055#         $val =~ s/\s+$//;
     5056#         if ($typemap{$type} ne 'TXT') {
     5057#           # Leading or trailng spaces could be legit in TXT records.
     5058#           $host =~ s/^\s+//;
     5059#           $host =~ s/\s+$//;
     5060#         }
    50275061
    50285062          _printrec_tiny(*ZONECACHE, 'y', \%recflags, $revzone,
    5029             $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp)
     5063            $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive)
    50305064                if *ZONECACHE;
    50315065
     
    50625096
    50635097  my $domsth = $dbh->prepare("SELECT domain_id,domain,status,changed FROM domains WHERE status=1");
    5064   $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
     5098  $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
    50655099        "FROM records WHERE domain_id=?");      # Just exclude all types relating to rDNS
    50665100#       "FROM records WHERE domain_id=? AND type < 65280");     # Just exclude all types relating to rDNS
     
    50835117
    50845118        $recsth->execute($domid);
    5085         while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc) = $recsth->fetchrow_array) {
     5119        while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc,$stamp,$expires,$stampactive) = $recsth->fetchrow_array) {
    50865120          next if $recflags{$recid};
    5087 
    5088           $loc = '' if !$loc;   # de-nullify - just in case
    5089 ##fixme:  handle case of record-with-location-that-doesn't-exist better.
    5090 # note this currently fails safe (tested) - records with a location that
    5091 # doesn't exist will not be sent to any client
    5092 #       $loc = '' if !$lochash->{$loc};
    5093 
    5094 ##fixme:  record validity timestamp. tinydns supports fiddling with timestamps.
    5095 # note $ttl must be set to 0 if we want to use tinydns's auto-expiring timestamps.
    5096 # timestamps are TAI64
    5097 # ~~ 2^62 + time()
    5098           my $stamp = '';
    5099 
    5100           # support tinydns' auto-TTL
    5101           $ttl = '' if $ttl == -1;
    51025121
    51035122          # Spaces are evil.
     
    51115130
    51125131          _printrec_tiny(*ZONECACHE, 'n', \%recflags,
    5113                 $dom, $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp)
     5132                $dom, $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive)
    51145133                if *ZONECACHE;
    51155134
     
    51505169# Utility sub for __export_tiny above
    51515170sub _printrec_tiny {
    5152   my ($datafile,$revrec,$recflags,$zone,$host,$type,$val,$dist,$weight,$port,$ttl,$loc,$stamp) = @_;
     5171  my ($datafile,$revrec,$recflags,$zone,$host,$type,$val,$dist,$weight,$port,$ttl,$loc,$stamp,$expires,$stampactive) = @_;
     5172
     5173  $loc = '' if !$loc;   # de-nullify - just in case
     5174##fixme:  handle case of record-with-location-that-doesn't-exist better.
     5175# note this currently fails safe (tested) - records with a location that
     5176# doesn't exist will not be sent to any client
     5177#       $loc = '' if !$lochash->{$loc};
     5178
     5179
     5180## Records that are valid only before or after a set time
     5181
     5182# record due to expire sometime is the complex case.  we don't want to just
     5183# rely on tinydns' auto-adjusting TTLs, because the default TTL in that case
     5184# is one day instead of the SOA minttl as BIND might do.
     5185
     5186# consider the case where a record is set to expire a week ahead, but the next
     5187# day later you want to change it NOW (or as NOWish as you get with your DNS
     5188# management practice).  but now you're stuck, because someone, somewhere,
     5189# has just done a lookup before your latest change was published, and they'll
     5190# be caching that old, broken record for 1 day instead of your zone default
     5191# TTL.
     5192
     5193# $stamp-$ttl is the *latest* we can publish the record with the defined TTL
     5194# to still have the expiry happen as scheduled, but we need to find some
     5195# *earlier* point.  We can maybe guess, and 2x TTL is probably reasonable,
     5196# but we need info on the export frequency.
     5197
     5198# export the normal, non-expiring record up until $stamp-<guesstimate>, then
     5199# switch to exporting a record with the TAI64 stamp and a 0 TTL so tinydns
     5200# takes over TTL management.
     5201
     5202  if ($stampactive) {
     5203    if ($expires) {
     5204      # record expires at $stamp;  decide if we need to keep the TTL and ignore
     5205      # the stamp for a time or if we need to change the TTL to 0 and convert
     5206      # $stamp to TAI64 so tinydns can use $stamp to autoadjust the TTL on the fly.
     5207# extra hack, optimally needs more knowledge of data export frequency
     5208# smack the idiot customer who insists on 0 TTLs;  they can suck up and
     5209# deal with a 10-minute TTL.  especially on scheduled changes.  note this
     5210# should be (export freq * 2), but we don't know the actual export frequency.
     5211$ttl = 300 if $ttl == 0;        #hack phtui
     5212      my $ahead = (86400 < $ttl*2 ? 86400 : $ttl*2);
     5213      if ((time() + $ahead) < $stamp) {
     5214        # more than 2x TTL OR more than one day (whichever is less) from expiry time;  publish normal record
     5215        $stamp = '';
     5216      } else {
     5217        # less than 2x TTL from expiry time, let tinydns take over TTL management and publish the TAI64 stamp.
     5218        $ttl = 0;
     5219        $stamp = unixtai64($stamp);
     5220        $stamp =~ s/\@//;
     5221      }
     5222    } else {
     5223      # record is "active after";  convert epoch from database to TAI64, publish, and collect $200.
     5224      $stamp = unixtai64($stamp);
     5225      $stamp =~ s/\@//;
     5226    }
     5227  } else {
     5228    # flag for active timestamp is false;  don't actually put a timestamp in the output
     5229    $stamp = '';
     5230  }
     5231
     5232  # support tinydns' auto-TTL
     5233  $ttl = '' if $ttl == -1;
     5234# these are WAY FREAKING HIGH - higher even than most TLD registry TTLs!
     5235# NS          259200  => 3d
     5236# all others   86400  => 1d
    51535237
    51545238  if ($revrec eq 'y') {
  • trunk/dns-1.0-1.2.sql

    r507 r543  
    7272ALTER TABLE records ADD COLUMN rdns_id INTEGER DEFAULT 0 NOT NULL;
    7373ALTER TABLE records ADD COLUMN location character varying (4) DEFAULT '' NOT NULL;
     74-- Scheduled changes.
     75ALTER TABLE records ADD COLUMN stamp TIMESTAMP WITH TIME ZONE DEFAULT 'epoch' NOT NULL;
     76ALTER TABLE records ADD COLUMN expires boolean DEFAULT 'n' NOT NULL;
     77ALTER TABLE records ADD COLUMN stampactive boolean DEFAULT 'n' NOT NULL;
    7478
    7579-- ~120s -> 75s performance boost on 100K records when always exporting all records
  • trunk/dns-rpc.cgi

    r516 r543  
    475475
    476476  my @recargs = ($args{defrec}, $args{revrec}, $args{parent_id},
    477         \$args{name}, \$args{type}, \$args{address}, $args{ttl}, $args{location});
     477        \$args{name}, \$args{type}, \$args{address}, $args{ttl}, $args{location},
     478        $args{expires}, $args{stamp});
    478479  if ($args{type} == $DNSDB::reverse_typemap{MX} or $args{type} == $DNSDB::reverse_typemap{SRV}) {
    479480    push @recargs, $args{distance};
     
    497498  _reccheck(\%args);
    498499
     500  # put some caller-friendly names in their rightful DB column places
     501  $args{val} = $args{address};
     502  $args{host} = $args{name};
     503
    499504  # get old line, so we can update only the bits that the caller passed to change
    500   # note we subbed address for val since it's a little more caller-friendly
    501505  my $oldrec = $dnsdb->getRecLine($args{defrec}, $args{revrec}, $args{id});
    502   foreach my $field (qw(name type address ttl location distance weight port)) {
     506  foreach my $field (qw(host type val ttl location expires distance weight port)) {
    503507    $args{$field} = $oldrec->{$field} if !$args{$field} && defined($oldrec->{$field});
    504508  }
     509  # stamp has special handling when blank or 0.  "undefined" from the caller should mean "don't change"
     510  $args{stamp} = $oldrec->{stamp} if !defined($args{stamp}) && defined($oldrec->{stamp});
    505511
    506512  # allow passing text types rather than DNS integer IDs
     
    510516  # parent_id is the "primary" zone we're updating;  necessary for forward/reverse voodoo
    511517  my ($code, $msg) = $dnsdb->updateRec($args{defrec}, $args{revrec}, $args{id}, $args{parent_id},
    512         \$args{name}, \$args{type}, \$args{address}, $args{ttl}, $args{location},
     518        \$args{host}, \$args{type}, \$args{val}, $args{ttl}, $args{location},
     519        $args{expires}, $args{stamp},
    513520        $args{distance}, $args{weight}, $args{port});
    514521
  • trunk/dns.cgi

    r533 r543  
    3030use Net::DNS;
    3131use DBI;
     32
    3233use Data::Dumper;
    3334
     
    721722
    722723    my @recargs = ($webvar{defrec}, $webvar{revrec}, $webvar{parentid},
    723         \$webvar{name}, \$webvar{type}, \$webvar{address}, $webvar{ttl}, $webvar{location});
     724        \$webvar{name}, \$webvar{type}, \$webvar{address}, $webvar{ttl}, $webvar{location},
     725        $webvar{expires}, $webvar{stamp});
    724726    if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV}) {
    725727      push @recargs, $webvar{distance};
     
    769771    $page->param(ttl            => $recdata->{ttl});
    770772    $page->param(typelist       => $dnsdb->getTypelist($webvar{revrec}, $recdata->{type}));
    771 
     773    if ($recdata->{stampactive}) {
     774      $page->param(stamp => $recdata->{stamp});
     775      $page->param(stamp_until => $recdata->{expires});
     776    }
    772777    if ($webvar{defrec} eq 'n') {
    773778      fill_loclist($curgroup, $recdata->{location});
     
    785790    my ($code,$msg) = $dnsdb->updateRec($webvar{defrec}, $webvar{revrec}, $webvar{id}, $webvar{parentid},
    786791        \$webvar{name}, \$webvar{type}, \$webvar{address}, $webvar{ttl}, $webvar{location},
     792        $webvar{expires}, $webvar{stamp},
    787793        $webvar{distance}, $webvar{weight}, $webvar{port});
    788794
     
    20022008    $rec->{record_delete} = ($permissions{admin} || $permissions{record_delete});
    20032009    $rec->{locname} = '' unless ($permissions{admin} || $permissions{location_view});
     2010# Timestamps
     2011    if ($rec->{expires}) {
     2012      $rec->{stamptype} = $rec->{ispast} ? 'expired at' : 'expires at';
     2013    } else {
     2014      $rec->{stamptype} = 'valid after';
     2015    }
     2016    # strip seconds and timezone?  no, not yet.  could probably offer a config knob on this display at some point.
     2017#    $rec->{stamp} =~ s/:\d\d-\d+$//;
     2018    delete $rec->{expires};
     2019    delete $rec->{ispast};
    20042020  }
    20052021  $page->param(reclist => $foo2);
     
    20322048  my $soa = $dnsdb->getSOA($webvar{defrec}, $webvar{revrec}, $webvar{parentid});
    20332049  $page->param(ttl      => ($webvar{ttl} ? $webvar{ttl} : $soa->{minttl}));
     2050  $page->param(stamp_until => ($webvar{expires} eq 'until'));
     2051  $page->param(stamp => $webvar{stamp});
    20342052}
    20352053
  • trunk/dns.sql

    r507 r543  
    166166    description text,
    167167    rdns_id integer NOT NULL DEFAULT 0,
    168     location character varying (4) DEFAULT '' NOT NULL
     168    location character varying (4) DEFAULT '' NOT NULL,
     169    stamp TIMESTAMP WITH TIME ZONE DEFAULT 'epoch' NOT NULL,
     170    expires boolean DEFAULT 'n' NOT NULL,
     171    stampactive boolean DEFAULT 'n' NOT NULL
    169172);
    170173CREATE INDEX rec_domain_index ON records USING btree (domain_id);
  • trunk/templates/dns.css

    r532 r543  
    165165        text-align: center;
    166166        padding: 5px;
    167         width: 50%;
     167        width: 55%;
    168168}
    169169.warning {
  • trunk/templates/reclist.tmpl

    r538 r543  
    6161<tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
    6262<TMPL_IF fwdzone>
    63         <td><TMPL_IF record_edit><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR NAME=record_id>"><TMPL_VAR NAME=host></a><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=host><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF></TMPL_IF></td>
     63        <td><TMPL_IF record_edit><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR
     64 NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR
     65 NAME=record_id>"><TMPL_VAR NAME=host></a><TMPL_IF locname> (<TMPL_VAR
     66 NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=host><TMPL_IF locname> (<TMPL_VAR
     67 NAME=locname>)</TMPL_IF></TMPL_IF><TMPL_IF stampactive><br />(<TMPL_VAR NAME=stamptype> <TMPL_VAR
     68 NAME=stamp>)</TMPL_IF></td>
    6469        <td><TMPL_VAR NAME=type></td>
    6570        <td><TMPL_VAR NAME=val></td>
     
    6873        <td><TMPL_VAR NAME=port></td>
    6974<TMPL_ELSE>
    70         <td><TMPL_IF record_edit><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR NAME=record_id>"><TMPL_VAR NAME=val></a><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=val><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF></TMPL_IF></td>
     75        <td><TMPL_IF record_edit><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR
     76 NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR
     77 NAME=record_id>"><TMPL_VAR NAME=val></a><TMPL_IF locname> (<TMPL_VAR
     78 NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=val><TMPL_IF locname> (<TMPL_VAR
     79 NAME=locname>)</TMPL_IF></TMPL_IF><TMPL_IF stampactive><br />(<TMPL_VAR NAME=stamptype> <TMPL_VAR
     80 NAME=stamp>)</TMPL_IF></td>
    7181        <td><TMPL_VAR NAME=type></td>
    7282        <td><TMPL_VAR NAME=host></td>
  • trunk/templates/record.tmpl

    r532 r543  
    1818<input type="hidden" name="recact" value="<TMPL_VAR NAME=recact>" />
    1919
    20 <table class="container" width="450">
     20<table class="container" width="520">
    2121<tr><td>
    2222
     
    8181        </tr>
    8282</TMPL_IF>
     83<TMPL_UNLESS is_default>
     84        <tr class="datalinelight">
     85                <td>Timestamp<br />(blank or 0 disables timestamp)</td>
     86                <td>Valid <input type="radio" name="expires" value="until"<TMPL_IF stamp_until> checked="checked"</TMPL_IF>>until
     87                <input type="radio" name="expires" value="after"<TMPL_UNLESS stamp_until> checked="checked"</TMPL_UNLESS>>after:
     88                <input type="text" name="stamp" value="<TMPL_VAR NAME=stamp>" />
     89                </td>
     90        </tr>
     91</TMPL_UNLESS>
    8392        <tr class="datalinelight">
    8493                <td colspan="2" align="center"><input type="submit" value=" <TMPL_VAR NAME=todo> " /></td>
  • trunk/tiny-import.pl

    r528 r543  
    2525use strict;
    2626use warnings;
     27use POSIX;
     28use Time::TAI64 qw(:tai);
    2729
    2830use lib '.';    ##uselib##
     
    125127  }
    126128
    127   our $recsth = $dbh->prepare("INSERT INTO records (domain_id,rdns_id,host,type,val,distance,weight,port,ttl,location) ".
    128         " VALUES (?,?,?,?,?,?,?,?,?,?)");
     129  our $recsth = $dbh->prepare("INSERT INTO records (domain_id,rdns_id,host,type,val,distance,weight,port,ttl,location,stamp,expires,stampactive) ".
     130        " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
    129131
    130132  my %deleg;
     
    248250  }
    249251
     252  sub calcstamp {
     253    my $stampin = shift;
     254    my $ttl = shift;
     255    my $pzone = shift;
     256    my $revrec = shift;
     257
     258    return ($ttl, 'n', 'n', '1970-01-01 00:00:00 -0') if !$stampin;
     259
     260##fixme  Yes, this fails for records in 2038 sometime.  No, I'm not going to care for a while.
     261    $stampin = "\@$stampin";    # Time::TAI64 needs the leading @.  Feh.
     262    my $u = tai2unix($stampin);
     263    $stampin = strftime("%Y-%m-%d %H:%M:%S %z", localtime($u));
     264    my $expires = 'n';
     265    if ($ttl) {
     266      # TTL can stay put.
     267    } else {
     268      # TTL on import is 0, almost certainly wrong.  Get the parent zone's SOA and use the minttl.
     269      my $soa = $dnsdb->getSOA('n', $revrec, $pzone);
     270      $ttl = $soa->{minttl};
     271      $expires = 'y';
     272    }
     273    return ($ttl, 'y', $expires, $stampin);
     274  }
    250275
    251276  sub recslurp {
     
    274299      my $fparent = $dnsdb->_hostparent($host);
    275300      my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($ip));
     301
     302      my $stampactive = 'n';
     303      my $expires = 'n';
     304
     305      # can't set a timestamp on an orphaned record.  we'll actually fail import of this record a little later.
     306      if ($fparent || $rparent) {
     307        if ($fparent) {
     308          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     309        } else {
     310          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     311        }
     312      }
     313
    276314      if ($fparent && $rparent) {
    277         $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc);
     315        $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    278316      } else {
    279317        if ($importcfg{legacy}) {
     
    282320          $rparent = 0 if !$rparent;
    283321          if ($fparent || $rparent) {
    284             $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc);
     322            $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    285323          } else {
    286324            # No parents found, cowardly refusing to add a dangling record
     
    290328        } elsif ($importcfg{conv}) {
    291329          # downconvert A+PTR if forward zone is not found
    292           $recsth->execute(0, $rparent, $host, 12, $ip, 0, 0, 0, $ttl, $loc);
     330          $recsth->execute(0, $rparent, $host, 12, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    293331          $converted++;
    294332        } else {
     
    310348      $loc = '' if !$loc;
    311349      $loc = '' if $loc =~ /^:+$/;
     350
     351      my $stampactive = 'n';
     352      my $expires = 'n';
     353
    312354      if ($host =~ /\.arpa$/) {
    313355        ($code,$msg) = DNSDB::_zone2cidr($host);
    314356        my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    315         $recsth->execute(0, $rparent, $targ, 5, $msg->addr, 0, 0, 0, $ttl, $loc);
     357        if ($rparent) {
     358          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     359          $recsth->execute(0, $rparent, $targ, 5, $msg->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     360        } else {
     361          push @deferred, $rec unless $nodefer;
     362          $impok = 0;
     363          #  print "$tmporig deferred;  can't find parent zone\n";
     364        }
    316365
    317366##fixme:  automagically convert manually maintained sub-/24 delegations
     
    324373        my $fparent = $dnsdb->_hostparent($host);
    325374        if ($fparent) {
    326           $recsth->execute($fparent, 0, $host, 5, $targ, 0, 0, 0, $ttl, $loc);
     375          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     376          $recsth->execute($fparent, 0, $host, 5, $targ, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    327377        } else {
    328378          push @deferred, $rec unless $nodefer;
     
    344394      $loc = '' if !$loc;
    345395      $loc = '' if $loc =~ /^:+$/;
     396
     397      my $stampactive = 'n';
     398      my $expires = 'n';
     399
    346400      if ($zone =~ /\.arpa$/) {
    347401        ($code,$msg) = DNSDB::_zone2cidr($zone);
     
    352406#       if !$rparent;
    353407        if ($rparent) {
    354           $recsth->execute(0, $rparent, $ns, 2, $msg, 0, 0, 0, $ttl, $loc);
     408          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     409          $recsth->execute(0, $rparent, $ns, 2, $msg, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    355410        } else {
    356411          push @deferred, $rec unless $nodefer;
     
    360415        my $fparent = $dnsdb->_hostparent($zone);
    361416        if ($fparent) {
    362           $recsth->execute($fparent, 0, $zone, 2, $ns, 0, 0, 0, $ttl, $loc);
    363           $recsth->execute($fparent, 0, $ns, 2, $ip, 0, 0, 0, $ttl, $loc) if $ip;
     417          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     418          $recsth->execute($fparent, 0, $zone, 2, $ns, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     419          $recsth->execute($fparent, 0, $ns, 2, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive) if $ip;
    364420        } else {
    365421          push @deferred, $rec unless $nodefer;
     
    378434      $loc = '' if !$loc;
    379435      $loc = '' if $loc =~ /^:+$/;
     436
     437      my $stampactive = 'n';
     438      my $expires = 'n';
     439
    380440      my $rparent;
    381441      if (my ($i, $z) = ($rip =~ /^(\d+)\.(\d+-(?:\d+\.){4}in-addr.arpa)$/) ) {
     
    392452      }
    393453      if ($rparent) {
    394         $recsth->execute(0, $rparent, $host, 12, $msg->addr, 0, 0, 0, $ttl, $loc);
     454        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     455        $recsth->execute(0, $rparent, $host, 12, $msg->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    395456      } else {
    396457        push @deferred, $rec unless $nodefer;
     
    410471      $loc = '' if $loc =~ /^:+$/;
    411472
     473      my $stampactive = 'n';
     474      my $expires = 'n';
     475
    412476      my $domid = $dnsdb->_hostparent($host);
    413477      if ($domid) {
    414         $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc);
     478        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     479        $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    415480      } else {
    416481        push @deferred, $rec unless $nodefer;
     
    430495      $loc = '' if !$loc;
    431496      $loc = '' if $loc =~ /^:+$/;
     497
     498      my $stampactive = 'n';
     499      my $expires = 'n';
     500
     501##fixme er... what do we do with an SOA with a timestamp?  O_o
     502# fail for now, since there's no clean way I can see to handle this (yet)
     503# maybe (ab)use the -l flag to import as-is?
     504      if ($stamp) {
     505        push @deferred, $rec unless $nodefer;
     506        return 0;
     507      }
     508
     509##fixme: need more magic on TTL, so we can decide whether to use the minttl or newttl
     510#      my $newttl;
     511#      ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'n');
     512#      $ttl = $newttl if !$ttl;
     513
    432514      if ($zone =~ /\.arpa$/) {
    433515        ($code,$msg) = DNSDB::_zone2cidr($zone);
     
    435517                undef, ($msg, $loc));
    436518        my ($rdns) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
    437         $recsth->execute(0, $rdns, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl, $loc);
     519        my $newttl;
     520        ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'y');
     521        $ttl = $newttl if !$ttl;
     522        $recsth->execute(0, $rdns, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl,
     523                $loc, $stamp, $expires, $stampactive);
    438524      } else {
    439525        $dbh->do("INSERT INTO domains (domain,group_id,status,default_location) VALUES (?,1,1,?)",
    440526                undef, ($zone, $loc));
    441527        my ($domid) = $dbh->selectrow_array("SELECT currval('domains_domain_id_seq')");
    442         $recsth->execute($domid, 0, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl, $loc);
     528        my $newttl;
     529        ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'n');
     530        $ttl = $newttl if !$ttl;
     531        $recsth->execute($domid, 0, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl,
     532                $loc, $stamp, $expires, $stampactive);
    443533      }
    444534
     
    457547      $loc = '' if $loc =~ /^:+$/;
    458548
     549      my $stampactive = 'n';
     550      my $expires = 'n';
     551
    459552# note we don't check for reverse domains here, because MX records don't make any sense in reverse zones.
    460553# if this really ever becomes an issue for someone it can be expanded to handle those weirdos
     
    463556      my $domid = $dnsdb->_hostparent($zone);
    464557      if ($domid) {
    465         $recsth->execute($domid, 0, $zone, 15, $host, $dist, 0, 0, $ttl, $loc);
    466         $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc) if $ip;
     558        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     559        $recsth->execute($domid, 0, $zone, 15, $host, $dist, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     560        $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive) if $ip;
    467561      } else {
    468562        push @deferred, $rec unless $nodefer;
     
    482576      $loc = '' if $loc =~ /^:+$/;
    483577
     578      my $stampactive = 'n';
     579      my $expires = 'n';
     580
    484581      if ($fqdn =~ /\.arpa$/) {
    485582        ($code,$msg) = DNSDB::_zone2cidr($fqdn);
    486583        my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    487         $recsth->execute(0, $rparent, $rdata, 16, "$msg", 0, 0, 0, $ttl, $loc);
     584        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     585        $recsth->execute(0, $rparent, $rdata, 16, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    488586      } else {
    489587        my $domid = $dnsdb->_hostparent($fqdn);
    490588        if ($domid) {
    491           $recsth->execute($domid, 0, $fqdn, 16, $rdata, 0, 0, 0, $ttl, $loc);
     589          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     590          $recsth->execute($domid, 0, $fqdn, 16, $rdata, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    492591        } else {
    493592          push @deferred, $rec unless $nodefer;
     
    508607      $loc = '' if !$loc;
    509608      $loc = '' if $loc =~ /^:+$/;
     609
     610      my $stampactive = 'n';
     611      my $expires = 'n';
     612
     613##fixme er... what do we do with an SOA with a timestamp?  O_o
     614# fail for now, since there's no clean way I can see to handle this (yet)
     615# maybe (ab)use the -l flag to import as-is?
     616      if ($stamp) {
     617        push @deferred, $rec unless $nodefer;
     618        return 0;
     619      }
     620
     621##fixme: need more magic on TTL, so we can decide whether to use the minttl or newttl
     622#      my $newttl;
     623#      ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'n');
    510624
    511625      if ($fqdn =~ /\.arpa$/) {
     
    517631                undef, ($msg, $loc));
    518632          ($rdns) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
     633          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, 2560, 0, 'y');
    519634# this would probably make a lot more sense to do hostmaster.$config{admindomain}
    520           $recsth->execute(0, $rdns, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560", $loc);
    521         }
    522         $recsth->execute(0, $rdns, $ns, 2, "$msg", 0, 0, 0, $ttl, $loc);
     635# otherwise, it's as per the tinydns defaults that work tolerably well on a small scale
     636# serial -> modtime of data file, ref -> 16384, ret -> 2048, exp -> 1048576, min -> 2560
     637          $recsth->execute(0, $rdns, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560",
     638                $loc, $stamp, $expires, $stampactive);
     639        }
     640        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, 2560, $rdns, 'y') if !$stamp;
     641        $recsth->execute(0, $rdns, $ns, 2, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    523642##fixme:  (?)  implement full conversion of tinydns . records?
    524643# -> problem:  A record for NS must be added to the appropriate *forward* zone, not the reverse
    525 #$recsth->execute(0, $rdns, $ns, 1, $ip, 0, 0, 0, $ttl)
     644#$recsth->execute(0, $rdns, $ns, 1, $ip, 0, 0, 0, $ttl, $stamp, $expires, $stampactive)
    526645# ...  auto-A-record simply does not make sense in reverse zones.  Functionally
    527646# I think it would work, sort of, but it's a nasty mess and anyone hosting reverse
     
    537656                undef, ($fqdn, $loc));
    538657          ($domid) = $dbh->selectrow_array("SELECT currval('domains_domain_id_seq')");
    539           $recsth->execute($domid, 0, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560", $loc);
    540         }
    541         $recsth->execute($domid, 0, $fqdn, 2, $ns, 0, 0, 0, $ttl, $loc);
    542         $recsth->execute($domid, 0, $ns, 1, $ip, 0, 0, 0, $ttl, $loc) if $ip;
     658          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, 2560, 0, 'n');
     659          $recsth->execute($domid, 0, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560",
     660                $loc, $stamp, $expires, $stampactive);
     661        }
     662        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n') if !$stamp;
     663        $recsth->execute($domid, 0, $fqdn, 2, $ns, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     664        $recsth->execute($domid, 0, $ns, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive) if $ip;
    543665      }
    544666
     
    576698      $loc = '' if !$loc;
    577699      $loc = '' if $loc =~ /^:+$/;
     700
     701      my $stampactive = 'n';
     702      my $expires = 'n';
    578703
    579704      if ($type == 33) {
     
    604729        my $domid = $dnsdb->_hostparent($fqdn);
    605730        if ($domid) {
    606           $recsth->execute($domid, 0, $fqdn, 33, $target, $prio, $weight, $port, $ttl, $loc) if $domid;
     731          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     732          $recsth->execute($domid, 0, $fqdn, 33, $target, $prio, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive) if $domid;
    607733        } else {
    608734          push @deferred, $rec unless $nodefer;
     
    621747
    622748        my $fparent = $dnsdb->_hostparent($fqdn);
     749
    623750        if ($fparent) {
    624           $recsth->execute($fparent, 0, $fqdn, 28, $val->addr, 0, 0, 0, $ttl, $loc);
     751          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     752          $recsth->execute($fparent, 0, $fqdn, 28, $val->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    625753        } else {
    626754          push @deferred, $rec unless $nodefer;
     
    636764          my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    637765          if ($rparent) {
    638             $recsth->execute(0, $rparent, $txtstring, 16, "$msg", 0, 0, 0, $ttl, $loc);
     766            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     767            $recsth->execute(0, $rparent, $txtstring, 16, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    639768          } else {
    640769            push @deferred, $rec unless $nodefer;
     
    644773          my $domid = $dnsdb->_hostparent($fqdn);
    645774          if ($domid) {
    646             $recsth->execute($domid, 0, $fqdn, 16, $txtstring, 0, 0, 0, $ttl, $loc);
     775            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     776            $recsth->execute($domid, 0, $fqdn, 16, $txtstring, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    647777          } else {
    648778            push @deferred, $rec unless $nodefer;
     
    664794          my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    665795          if ($rparent) {
    666             $recsth->execute(0, $rparent, "$email $txtrec", 17, "$msg", 0, 0, 0, $ttl, $loc);
     796            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     797            $recsth->execute(0, $rparent, "$email $txtrec", 17, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive );
    667798          } else {
    668799            push @deferred, $rec unless $nodefer;
     
    672803          my $domid = $dnsdb->_hostparent($fqdn);
    673804          if ($domid) {
    674             $recsth->execute($domid, 0, $fqdn, 17, "$email $txtrec", 0, 0, 0, $ttl, $loc);
     805            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     806            $recsth->execute($domid, 0, $fqdn, 17, "$email $txtrec", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    675807          } else {
    676808            push @deferred, $rec unless $nodefer;
     
    688820        my $domid = $dnsdb->_hostparent($fqdn);
    689821        if ($domid) {
    690           $recsth->execute($domid, 0, $fqdn, 44, $sshfp, 0, 0, 0, $ttl, $loc);
     822          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     823          $recsth->execute($domid, 0, $fqdn, 44, $sshfp, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    691824        } else {
    692825          push @deferred, $rec unless $nodefer;
Note: See TracChangeset for help on using the changeset viewer.