Changeset 298 for trunk


Ignore:
Timestamp:
04/09/12 18:08:51 (13 years ago)
Author:
Kris Deugau
Message:

/trunk

Checkpoint updating importAXFR() action logging (see #35):

  • convert _ZONE() to handle the conversion from CIDR to formal .arpa zone name
  • add _zone2cidr() for the inverse operation; convert the .arpa zone name to the CIDR netblock
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/DNSDB.pm

    r297 r298  
    736736##
    737737
    738 # Replace ZONE in hostname
     738# Replace ZONE in hostname, or create (most of) the actual proper zone name
    739739sub _ZONE {
    740740  my $zone = shift;
    741741  my $string = shift;
    742742  my $fr = shift || 'f';        # flag for forward/reverse order?  nb: ignored for IP
    743 
    744   my $prefix = $zone->network->addr;    # Just In Case someone managed to slip in
    745                                         # a funky subnet that had host bits set.
     743  my $sep = shift || '-';       # Separator character - unlikely we'll ever need more than . or -
     744
     745  my $prefix;
    746746
    747747  $string =~ s/,/./ if !$zone->{isv6};
    748748  $string =~ s/,/::/ if $zone->{isv6};
    749749
    750 #  if ($zone->{isv6} && ($zone->masklen % 4) != 0) {
    751 #    # grumpyfail, non-nibble zone.  shouldn't happen
    752 #    return;
    753 #  }
    754 
    755     # Subbing ZONE in the host.  We need to properly ID the netblock range
    756     # The subbed text should have "network IP with trailing zeros stripped" for
    757     # blocks lined up on octet (for v4) or 16-bit (for v6) boundaries
    758     # For blocks that do NOT line up on these boundaries, we tack on an extra "-0",
    759     # then take the most significant octet or 16-bit chunk of the "broadcast" IP and
    760     # append it after a double-dash
    761     # ie:
    762     # 8.0.0.0/6 -> 8.0.0.0 -> 11.255.255.255;  sub should be 8--11
    763     # 10.0.0.0/12 -> 10.0.0.0 -> 10.0.0.0 -> 10.15.255.255;  sub should be 10-0--15
    764     # 192.168.4.0/22 -> 192.168.4.0 -> 192.168.7.255;  sub should be 192-168-4--7
    765     # 192.168.0.8/29 -> 192.168.0.8 -> 192.168.0.15;  sub should be 192-168-0-8--15
    766     # Similar for v6
    767     if (!$zone->{isv6}) {
     750  # Subbing ZONE in the host.  We need to properly ID the netblock range
     751  # The subbed text should have "network IP with trailing zeros stripped" for
     752  # blocks lined up on octet (for v4) or hex-quad (for v6) boundaries
     753  # For blocks that do NOT line up on these boundaries, we take the most
     754  # significant octet or 16-bit chunk of the "broadcast" IP and append it
     755  # after a double-dash
     756  # ie:
     757  # 8.0.0.0/6 -> 8.0.0.0 -> 11.255.255.255;  sub should be 8--11
     758  # 10.0.0.0/12 -> 10.0.0.0 -> 10.0.0.0 -> 10.15.255.255;  sub should be 10-0--15
     759  # 192.168.4.0/22 -> 192.168.4.0 -> 192.168.7.255;  sub should be 192-168-4--7
     760  # 192.168.0.8/29 -> 192.168.0.8 -> 192.168.0.15;  sub should be 192-168-0-8--15
     761  # Similar for v6
     762
     763  if (!$zone->{isv6}) { # IPv4
     764
     765    $prefix = $zone->network->addr;     # Just In Case someone managed to slip in
     766                                        # a funky subnet that had host bits set.
     767    my $bc = $zone->broadcast->addr;
     768
     769    if ($zone->masklen > 24) {
     770      $bc =~ s/^\d+\.\d+\.\d+\.//;
     771    } elsif ($zone->masklen > 16) {
     772      $prefix =~ s/\.0$//;
     773      $bc =~ s/^\d+\.\d+\.//;
     774    } elsif ($zone->masklen > 8) {
     775      $bc =~ s/^\d+\.//;
     776      $prefix =~ s/\.0\.0$//;
     777    } else {
     778      $prefix =~ s/\.0\.0\.0$//;
     779    }
     780    if ($zone->masklen % 8) {
     781      $bc =~ s/(\.255)+$//;
     782      $prefix .= "--$bc";       #"--".zone->masklen;    # use range or mask length?
     783    }
     784    if ($fr eq 'f') {
     785      $prefix =~ s/\.+/$sep/g;
     786    } else {
     787      $prefix = join($sep, reverse(split(/\./, $prefix)));
     788    }
     789
     790  } else { # IPv6
     791
     792    if ($fr eq 'f') {
     793
     794      $prefix = $zone->network->addr;   # Just In Case someone managed to slip in
     795                                        # a funky subnet that had host bits set.
    768796      my $bc = $zone->broadcast->addr;
    769       if ($zone->masklen > 24) {
    770         $bc =~ s/^\d+\.\d+\.\d+\.//;
    771       } elsif ($zone->masklen > 16) {
    772         $prefix =~ s/\.0$//;
    773         $bc =~ s/^\d+\.\d+\.//;
    774       } elsif ($zone->masklen > 8) {
    775         $bc =~ s/^\d+\.//;
    776         $prefix =~ s/\.0\.0$//;
     797      if (($zone->masklen % 16) != 0) {
     798        # Strip trailing :0 off $prefix, and :ffff off the broadcast IP
     799        for (my $i=0; $i<(7-int($zone->masklen / 16)); $i++) {
     800          $prefix =~ s/:0$//;
     801          $bc =~ s/:ffff$//;
     802        }
     803        # Strip the leading 16-bit chunks off the front of the broadcast IP
     804        $bc =~ s/^([a-f0-9]+:)+//;
     805        # Append the remaining 16-bit chunk to the prefix after "--"
     806        $prefix .= "--$bc";
    777807      } else {
    778         $prefix =~ s/\.0\.0\.0$//;
     808        # Strip off :0 from the end until we reach the netblock length.
     809        for (my $i=0; $i<(8-$zone->masklen / 16); $i++) {
     810          $prefix =~ s/:0$//;
     811        }
    779812      }
    780       if ($zone->masklen % 8) {
    781         $bc =~ s/(\.255)+$//;
    782         $prefix .= "--$bc";     #"--".zone->masklen;    # use range or mask length?
     813      # Actually deal with the separator
     814      $prefix =~ s/:/$sep/g;
     815
     816    } else {    # $fr eq 'f'
     817
     818      $prefix = $zone->network->full;   # Just In Case someone managed to slip in
     819                                        # a funky subnet that had host bits set.
     820      my $bc = $zone->broadcast->full;
     821      $prefix =~ s/://g;        # clean these out since they're not spaced right for this case
     822      $bc =~ s/://g;
     823      # Strip trailing 0 off $prefix, and f off the broadcast IP, to match the mask length
     824      for (my $i=0; $i<(31-int($zone->masklen / 4)); $i++) {
     825        $prefix =~ s/0$//;
     826        $bc =~ s/f$//;
    783827      }
     828      # Split and reverse the order of the nibbles in the network/broadcast IPs
     829      my @nbits = reverse split //, $prefix;
     830      my @bbits = reverse split //, $bc;
     831      # Handle the sub-nibble case.  Eww.  I feel dirty supporting this...
     832      $nbits[0] = "$nbits[0]-$bbits[0]" if ($zone->masklen % 4) != 0;
     833      # Glue it back together
     834      $prefix = join($sep, @nbits);
     835
     836    }   # $fr ne 'f'
     837
     838  } # $zone->{isv6}
     839
     840  # Do the substitution, finally
     841  $string =~ s/ZONE/$prefix/;
     842  $string =~ s/--/-/ if $sep ne '-';    # - as separator needs extra help for sub-octet v4 netblocks
     843  return $string;
     844} # done _ZONE()
     845
     846# Not quite a substitution sub, but placed here as it's basically the inverse of above;
     847# given the .arpa zone name, return the CIDR netblock the zone is for.
     848# Supports v4 non-octet/non-classful netblocks as per the method outlined in the Cricket Book (2nd Ed p217-218)
     849# Does NOT support non-quad v6 netblocks via the same scheme;  it shouldn't ever be necessary.
     850# Takes a nominal .arpa zone name, returns a success code and NetAddr::IP, or a fail code and message
     851sub _zone2cidr {
     852  my $zone = shift;
     853
     854  my $cidr;
     855  my $warnmsg;
     856
     857  if ($zone =~ /\.in-addr\.arpa\.?$/) {
     858    # v4 revzone, formal zone name type
     859    my $tmpcidr;
     860    my $tmpzone = $zone;
     861    $tmpzone =~ s/\.in-addr\.arpa\.?//;
     862    return ('FAIL',"Non-numerics in apparent IPv4 reverse zone name") if $tmpzone !~ /^(?:\d+-)?[\d\.]+$/;
     863
     864    # Snag the octet pieces
     865    my @octs = split /\./, $tmpzone;
     866
     867    # Map result of a range manipulation to a mask length change.  Cheaper than finding the 2-root of $octets[0]+1.
     868    my %maskmap = (1 => 1, 3 => 2, 7 => 3, 15 => 4, 31 => 5, 63 => 6, 127 => 7);
     869
     870    # Handle "range" blocks, eg, 80-83.168.192.in-addr.arpa (192.168.80.0/22)
     871    # Need to take the size of the range to offset the basic octet-based mask length,
     872    # and make sure the first number in the range gets used as the network address for the block
     873    my $masklen = 0;
     874    if ($octs[0] =~ /(\d+)-\d+/) {      # take the range...
     875      $masklen -= $maskmap{-(eval $octs[0])};   # find the mask base...
     876      $octs[0] = $1;    # set the base octet of the range...
     877    }
     878    @octs = reverse @octs;      # We can reverse the octet pieces now that we've extracted and munged any ranges
     879
     880    # Now we find the "true" mask with the aid of the "base" calculated above
     881    if ($#octs == 0) {
     882      $masklen += 8;
     883      $tmpcidr = "$octs[0].0.0.0/$masklen";     # really hope we don't see one of these very often.
     884    } elsif ($#octs == 1) {
     885      $masklen += 16;
     886      $tmpcidr = "$octs[0].$octs[1].0.0/$masklen";
     887    } elsif ($#octs == 2) {
     888      $masklen += 24;
     889      $tmpcidr = "$octs[0].$octs[1].$octs[2].0/$masklen";
    784890    } else {
    785       if (($zone->masklen % 16) != 0) {
    786         # Strip trailing :0 off $prefix, and :ffff off the broadcast IP
    787         # Strip the leading 16-bit chunks off the front of the broadcast IP
    788         # Append the remaining 16-bit chunk to the prefix after "--"
    789         my $bc = $zone->broadcast->addr;
    790         for (my $i=0; $i<(7-int($zone->masklen / 16)); $i++) {
    791           $prefix =~ s/:0$//;
    792           $bc =~ s/:ffff$//;
    793         }
    794         $bc =~ s/^([a-f0-9]+:)+//;
    795         $prefix .= "--$bc";
    796       } else {
    797         # Strip off :0 from the end until we reach the netblock length.
    798         for (my $i=0; $i<(8-$zone->masklen / 16); $i++) {
    799           $prefix =~ s/:0$//;
    800         }
    801       }
    802     }
    803 
    804   # Replace . and : with -
    805   # If flagged for reverse-order, split on . or :, reverse, and join with -
    806     if ($fr eq 'f') {
    807       $prefix =~ s/[:.]+/-/g;
    808     } else {
    809       $prefix = join('-', reverse(split(/[:.]/, $prefix)));
    810     }
    811     $string =~ s/ZONE/$prefix/;
    812 #  }
    813   return $string;
    814 } # done _ZONE
    815 
     891      $masklen += 32;
     892      $tmpcidr = "$octs[0].$octs[1].$octs[2].$octs[3]/$masklen";
     893    }
     894
     895    # Just to be sure, use NetAddr::IP to validate.  Saves a lot of nasty regex watching for valid octet values.
     896    return ('FAIL', "Invalid zone $zone (apparent netblock $cidr)")
     897        unless $cidr = NetAddr::IP->new($tmpcidr);
     898
     899  } elsif ($zone =~ /\.ip6\.arpa$/) {
     900    # v6 revzone, formal zone name type
     901    my $tmpzone = $zone;
     902    $tmpzone =~ s/\.ip6\.arpa\.?//;
     903    return ('FAIL',"Non-hexadecimals in apparent IPv6 reverse zone name") if $tmpzone !~ /^[a-fA-F\d\.]+$/;
     904    my @quads = reverse(split(/\./, $tmpzone));
     905    $warnmsg .= "Apparent sub-/64 IPv6 reverse zone\n" if $#quads > 15;
     906    my $nc;
     907    foreach (@quads) {
     908      $cidr .= $_;
     909      $cidr .= ":" if ++$nc % 4 == 0;
     910    }
     911    my $nq = 1 if $nc % 4 != 0;
     912    my $mask = $nc * 4; # need to do this here because we probably increment it below
     913    while ($nc++ % 4 != 0) {
     914      $cidr .= "0";
     915    }
     916    $cidr .= ($nq ? '::' : ':')."/$mask";
     917  }
     918} # done _zone2cidr()
    816919
    817920
Note: See TracChangeset for help on using the changeset viewer.