Changeset 996 for branches


Ignore:
Timestamp:
01/22/26 18:21:37 (6 days ago)
Author:
Kris Deugau
Message:

/branches/cname-collision

Significant rewrite of most of the collision lookups due to edge cases
checking for various overlapping expiry/valid-after timestamps and
optionally resetting them to non-overlapping values.
Includes a truth table in a .ods spreadsheet.
See #72

Location:
branches/cname-collision
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • branches/cname-collision/DNSDB.pm

    r995 r996  
    645645  foreach my $tcompare ('<>', '=') {
    646646    next if $tcompare eq '<>' && ${$args{rectype}} != 5;
     647
     648# Merging these sets of SQL statements is far too messy and doesn't reasonably
     649# allow for more fine-grained error/warning messages to be returned
     650
     651    # First lookup fails out collisions with records without timestamps or default records (which can not have timestamps)
    647652    my $sql = "SELECT count(*) FROM "._rectable($args{defrec}, $args{revrec}).
    648653        " WHERE "._recparent($args{defrec}, $args{revrec})." = ? AND type $tcompare 5 AND $hfield = ?";
    649     if ($args{defrec} eq 'n') {
    650       # Expired records
    651       $sql .= " AND (stampactive = 'f' OR (stampactive = 't' AND (expires = 't' AND stamp >= now())))";
    652     }
    653654    my @lookupargs = ($args{id}, $hcheck);
     655    $sql .= " AND stampactive = 'f'" if $args{defrec} eq 'n';
    654656    if ($args{update}) {
    655657      $sql .= " AND record_id <> ?";
     
    667669    }
    668670
    669     # Check timestamps of pending active-after records.  Coerce expires-at timestamp down to soonest match if overlapping.
    670     if ($args{defrec} eq 'n') {
    671       $sql = "SELECT extract(epoch from stamp),stamp < now() FROM "._rectable($args{defrec}, $args{revrec}).
     671    # By this point, all failure cases for default records have been checked.
     672    # Default records cannot carry timestamps, so cannot have timestamp-based collisions
     673    return ('OK','OK') if $args{defrec} ne 'n';
     674
     675    # Second lookup fails out various timestamp-exists collision cases when adding/updating with a timestamp
     676    $sql = "SELECT count(*) FROM "._rectable($args{defrec}, $args{revrec}).
    672677        " WHERE "._recparent($args{defrec}, $args{revrec})." = ? AND type $tcompare 5 AND $hfield = ?".
    673         " AND stampactive = 't' AND expires = 'f'";
    674       my @lookupargs = ($args{id}, $hcheck);
    675       if ($args{update}) {
    676         $sql .= " AND record_id <> ?";
    677         push @lookupargs, $args{update};
     678        " AND stampactive = 't'";
     679    @lookupargs = ($args{id}, $hcheck);
     680    if (${$args{stamp}} && ${$args{expires}}) {
     681      $sql .= " AND expires = ?";
     682      push @lookupargs, ${$args{expires}};
     683      if ($self->{coerce_cname_timestamp} eq 'none') {
     684        # no coercion means new valid-after < existing expires or new expires > existing valid-after will fail
     685        $sql .= " AND ". (${$args{expires}} eq 'f' ? "expires = 't' AND stamp <= ?)" : "expires = 'f' AND stamp >= ?");
     686        push @lookupargs, ${$args{stamp}};
    678687      }
     688    }
     689    if ($args{update}) {
     690      $sql .= " AND record_id <> ?";
     691      push @lookupargs, $args{update};
     692    }
     693    @t = $dbh->selectrow_array($sql, undef, @lookupargs);
     694    if ($t[0] > 0) {
     695      if ($tcompare eq '<>') {
     696        return ('FAIL', "One or more non-CNAME records with timestamps already exist for ".($args{revrec} eq 'y' ? $arpaname : $hcheck).
     697                ".  CNAME records must expire before or become valid after any records with the same name.");
     698      } else {
     699        return ('FAIL', "There is already a CNAME with a timestamp present for ".($args{revrec} eq 'y' ? $arpaname : $hcheck).
     700                ".  Records with a matching name must expire before or become valid after this CNAME.");
     701      }
     702    }
     703
     704    # Third check starts retrieving actual timestamps to see if we need to,
     705    # and then if we can, coerce the new/updated record's timestamp to match
     706    $sql = "SELECT extract(epoch from stamp),expires,stamp < now() FROM "._rectable($args{defrec}, $args{revrec}).
     707        " WHERE "._recparent($args{defrec}, $args{revrec})." = ? AND type $tcompare 5 AND $hfield = ?".
     708        " AND stampactive = 't'";
     709    @lookupargs = ($args{id}, $hcheck);
     710    if ($args{update}) {
     711      $sql .= " AND record_id <> ?";
     712      push @lookupargs, $args{update};
     713    }
     714    if (${$args{stamp}}) {
     715      $sql .= " ORDER BY stamp ".(${$args{expires}} eq 'f' ? 'ASC' : 'DESC' )." LIMIT 1";
     716    } else {
    679717      $sql .= " ORDER BY stamp LIMIT 1";
    680       my @t = $dbh->selectrow_array($sql, undef, @lookupargs);
    681       if (@t) {
    682         # existing record with valid-after stamp is present
    683         if (${$args{stamp}}) {
    684           # caller requested an expiry time
    685           if (${$args{expires}} eq 'f') {
    686             # valid-after can't be used together with expires-at, so we can't coerce
    687             # the new record to expire as well as keeping valid-after
    688             return ('FAIL', "Cannot add CNAME, another record with a later valid-after time already exists");
    689           }
    690           my $reqstamp = str2time(${$args{stamp}});
    691           if ($reqstamp < $t[0]) {
    692             # do nothing, new record will expire before the one we found
    693           } else {
     718    }
     719    @t = $dbh->selectrow_array($sql, undef, @lookupargs);
     720    if (@t) {
     721      # caller requested an expiry time
     722      if (${$args{expires}} eq 'f') {
     723        # valid-after can't be used together with expires-at on one record, so we can't
     724        # coerce the new record to expire as well as keeping valid-after
     725        if ($tcompare eq '<>') {
     726          return ('FAIL', "Cannot ".($args{update} ? 'update' : 'add')." CNAME, another record".
     727                " with a valid-after time already exists for this name");
     728        } else {
     729          return ('FAIL', "Cannot ".($args{update} ? 'update' : 'add')." ".$typemap{${$args{rectype}}}.
     730                ", a CNAME with a valid-after time already exists for this name");
     731        }
     732      } else {
     733        my $reqstamp = str2time(${$args{stamp}});
     734        if ($reqstamp < $t[0]) {
     735          # do nothing, new record will expire before the one we found
     736        } else {
     737          if ($self->{coerce_cname_timestamp} eq 'adjust') {
    694738            # coerce the expiry timestamp
    695739            ${$args{stamp}} = strftime('%Y-%m-%d %H:%M:%S', localtime($t[0]));
    696             return ('WARN', "CNAME added with modified expiry time;  conflicting valid-after record found");
    697           }
    698         } else {
    699           if ($t[1]) {
    700             return('FAIL', "Cannot add CNAME, a record with a valid-after time in the past is already present");
     740            return ('WARN', $typemap{${$args{rectype}}}." ".($args{update} ? 'updated' : 'added').
     741                " with modified expiry time;  conflicting valid-after record found");
    701742          } else {
    702             # no expiry requested, but we found a valid-after, so coerce the new record down to expiring at that time
    703             ${$args{stamp}} = strftime('%Y-%m-%d %H:%M:%S', localtime($t[0]));
    704             ${$args{expires}} = 't';
    705             return ('WARN', "CNAME added with expiry time;  conflicting valid-after record found");
     743            return ('FAIL', "Cannot ".($args{update} ? 'update' : 'add')." ".$typemap{${$args{rectype}}}.
     744                ", another record with an overlapping valid-after timestamp already exists for this name");
    706745          }
    707746        }
    708       }
    709     } # args{defrecs} eq 'n'
     747      } # args{expires} ne 'f'
     748    } # if @t
     749
    710750  } # each $tcompare
    711751
Note: See TracChangeset for help on using the changeset viewer.