Changeset 912


Ignore:
Timestamp:
08/13/25 15:25:38 (13 hours ago)
Author:
Kris Deugau
Message:

/trunk

Structural cleanup - move addRDNS() to below addDomain() for more coherent
ordering of similar/related subs

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/DNSDB.pm

    r908 r912  
    27832783
    27842784
     2785## DNSDB::addRDNS
     2786# Adds a reverse DNS zone
     2787# Takes a database handle, CIDR block, reverse DNS pattern, numeric group,
     2788# and boolean(ish) state (active/inactive)
     2789# Returns a status code and message
     2790sub addRDNS {
     2791  my $self = shift;
     2792  my $dbh = $self->{dbh};
     2793  my $zone = shift;
     2794
     2795  # Autodetect formal .arpa zones
     2796  if ($zone =~ /\.arpa\.?$/) {
     2797    my $code;
     2798    ($code,$zone) = _zone2cidr($zone);
     2799    return ('FAIL', $zone) if $code eq 'FAIL';
     2800  }
     2801  $zone = NetAddr::IP->new($zone);
     2802
     2803  return ('FAIL',"Zone name must be a valid CIDR netblock") unless ($zone && $zone->addr !~ /^0/);
     2804  my $revpatt = shift;  # construct a custom (A/AAAA+)? PTR template record
     2805  my $group = shift;
     2806  my $state = shift;
     2807  my $defloc = shift || '';
     2808
     2809  $state = 1 if $state =~ /^active$/;
     2810  $state = 1 if $state =~ /^on$/;
     2811  $state = 0 if $state =~ /^inactive$/;
     2812  $state = 0 if $state =~ /^off$/;
     2813
     2814  return ('FAIL',"Invalid zone status") if $state !~ /^\d+$/;
     2815
     2816# quick check to start to see if we've already got one
     2817  my ($rdns_id) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet = ? AND default_location = ?",
     2818        undef, ("$zone", $defloc));
     2819
     2820  return ('FAIL', "Zone already exists") if $rdns_id;
     2821
     2822  # Allow transactions, and raise an exception on errors so we can catch it later.
     2823  # Use local to make sure these get "reset" properly on exiting this block
     2824  local $dbh->{AutoCommit} = 0;
     2825  local $dbh->{RaiseError} = 1;
     2826
     2827  my $warnstr = '';
     2828  my $defttl = 3600;    # 1 hour should be reasonable.  And unless things have gone horribly
     2829                        # wrong, we should have a value to override this anyway.
     2830
     2831  # Wrap all the SQL in a transaction
     2832  eval {
     2833    # insert the zone...
     2834    $dbh->do("INSERT INTO revzones (revnet,group_id,status,default_location,zserial) VALUES (?,?,?,?,?)", undef,
     2835        ($zone, $group, $state, $defloc, scalar(time()) ) );
     2836
     2837    # get the ID...
     2838    ($rdns_id) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
     2839
     2840    $self->_log(rdns_id => $rdns_id, group_id => $group,
     2841        entry => "Added ".($state ? 'active' : 'inactive')." reverse zone $zone");
     2842
     2843    # ... and now we construct the standard records from the default set.  NB:  group should be variable.
     2844    my $sth = $dbh->prepare("SELECT host,type,val,ttl FROM default_rev_records WHERE group_id=?");
     2845    my $sth_in = $dbh->prepare("INSERT INTO records (rdns_id,domain_id,host,type,val,ttl,location)".
     2846        " VALUES ($rdns_id,?,?,?,?,?,?)");
     2847    $sth->execute($group);
     2848    while (my ($host,$type,$val,$ttl) = $sth->fetchrow_array()) {
     2849      # Silently skip v4/v6 mismatches.  This is not an error, this is expected.
     2850      if ($zone->{isv6}) {
     2851        next if ($type == 65280 || $type == 65283);
     2852      } else {
     2853        next if ($type == 65281 || $type == 65284);
     2854      }
     2855
     2856      $host =~ s/ADMINDOMAIN/$self->{domain}/g;
     2857
     2858      # Check to make sure the IP stubs will fit in the zone.  Under most usage failures here should be rare.
     2859      # On failure, tack a note on to a warning string and continue without adding this record.
     2860      # While we're at it, we substitute $zone for ZONE in the value.
     2861      if ($val eq 'ZONE') {
     2862        # If we've got a pattern, we skip the default record version on (A+)PTR-template types
     2863        next if $revpatt && ($type == 65282 || $type == 65283);
     2864##fixme?  do we care if we have multiple whole-zone templates?
     2865        $val = $zone->network;
     2866      } elsif ($val =~ /ZONE/) {
     2867        my $tmpval = $val;
     2868        $tmpval =~ s/ZONE//;
     2869        # Bend the rules and allow single-trailing-number PTR or PTR template records to be inserted
     2870        # as either v4 or v6.  May make this an off-by-default config flag
     2871        # Note that the origin records that may trigger this **SHOULD** already have ZONE,\d
     2872        if ($type == 12 || $type == 65282) {
     2873          $tmpval =~ s/[,.]/::/ if ($tmpval =~ /^[,.]\d+$/ && $zone->{isv6});
     2874          $tmpval =~ s/[,:]+/./ if ($tmpval =~ /^(?:,|::)\d+$/ && !$zone->{isv6});
     2875        }
     2876        my $addr;
     2877        if ($self->_ipparent('n', 'y', \$tmpval, $rdns_id, \$addr)) {
     2878          $val = $addr->addr;
     2879        } else {
     2880          $warnstr .= "\nDefault record '$val $typemap{$type} $host' doesn't fit in $zone, skipping";
     2881          next;
     2882        }
     2883      }
     2884
     2885      # Substitute $zone for ZONE in the hostname, but only for non-NS records.
     2886      # NS records get this substitution on the value instead.
     2887      $host = _ZONE($zone, $host) if $type != 2;
     2888
     2889      # Fill in the forward domain ID if we can find it, otherwise:
     2890      # Coerce type down to PTR or PTR template if we can't
     2891      my $domid = 0;
     2892      if ($type >= 65280) {
     2893        if (!($domid = $self->_hostparent($host))) {
     2894          $warnstr .= "\nRecord added as PTR instead of $typemap{$type};  domain not found for $host";
     2895          $type = $reverse_typemap{PTR};
     2896          $domid = 0;   # just to be explicit.
     2897        }
     2898      }
     2899
     2900      _caseclean(\$type, \$host, \$val, 'n', 'y') if $self->{lowercase};
     2901
     2902      $sth_in->execute($domid,$host,$type,$val,$ttl,$defloc);
     2903
     2904      if ($typemap{$type} eq 'SOA') {
     2905        my @tmp1 = split /:/, $host;
     2906        my @tmp2 = split /:/, $val;
     2907        $self->_log(rdns_id => $rdns_id, group_id => $group,
     2908                entry => "[new $zone] Added SOA record [contact $tmp1[0]] [master $tmp1[1]] ".
     2909                "[refresh $tmp2[0]] [retry $tmp2[1]] [expire $tmp2[2]] [minttl $tmp2[3]], TTL $ttl");
     2910        $defttl = $tmp2[3];
     2911      } else {
     2912        my $logentry = "[new $zone] Added record '$host $typemap{$type} $val', TTL $ttl";
     2913        $logentry .= ", default location ".$self->getLoc($defloc)->{description} if $defloc;
     2914        $self->_log(rdns_id => $rdns_id, domain_id => $domid, group_id => $group, entry => $logentry);
     2915      }
     2916    }
     2917
     2918    # Generate record based on provided pattern. 
     2919    if ($revpatt) {
     2920      my $host;
     2921      my $type = ($zone->{isv6} ? 65284 : 65283);
     2922      my $val = $zone->network;
     2923
     2924      # Substitute $zone for ZONE in the hostname.
     2925      $host = _ZONE($zone, $revpatt);
     2926
     2927      my $domid = 0;
     2928      if (!($domid = $self->_hostparent($host))) {
     2929        $warnstr .= "\nDefault pattern added as PTR template instead of $typemap{$type};  domain not found for $host";
     2930        $type = 65282;
     2931        $domid = 0;     # just to be explicit.
     2932      }
     2933
     2934      $sth_in->execute($domid,$host,$type,$val,$defttl,$defloc);
     2935      my $logentry = "[new $zone] Added record '$host $typemap{$type}";
     2936      $self->_log(rdns_id => $rdns_id, domain_id => $domid, group_id => $group,
     2937        entry => $logentry." $val', TTL $defttl from pattern");
     2938    }
     2939
     2940    # If there are warnings (presumably about default records skipped for cause) log them
     2941    $self->_log(rdns_id => $rdns_id, group_id => $group, entry => "Warning(s) adding $zone:$warnstr")
     2942        if $warnstr;
     2943
     2944    # once we get here, we should have suceeded.
     2945    $dbh->commit;
     2946  }; # end eval
     2947
     2948  if ($@) {
     2949    my $msg = $@;
     2950    eval { $dbh->rollback; };
     2951    $self->_log(group_id => $group, entry => "Failed adding reverse zone $zone ($msg)")
     2952        if $self->{log_failures};
     2953    $dbh->commit;       # since we enabled transactions earlier
     2954    return ('FAIL',$msg);
     2955  } else {
     2956    my $retcode = 'OK';
     2957    if ($warnstr) {
     2958      $resultstr = $warnstr;
     2959      $retcode = 'WARN';
     2960    }
     2961    return ($retcode, $rdns_id);
     2962  }
     2963
     2964} # end addRDNS()
     2965
     2966
    27852967## DNSDB::delZone()
    27862968# Delete a forward or reverse zone.
     
    29803162  return $revid if $revid;
    29813163} # end revID()
    2982 
    2983 
    2984 ## DNSDB::addRDNS
    2985 # Adds a reverse DNS zone
    2986 # Takes a database handle, CIDR block, reverse DNS pattern, numeric group,
    2987 # and boolean(ish) state (active/inactive)
    2988 # Returns a status code and message
    2989 sub addRDNS {
    2990   my $self = shift;
    2991   my $dbh = $self->{dbh};
    2992   my $zone = shift;
    2993 
    2994   # Autodetect formal .arpa zones
    2995   if ($zone =~ /\.arpa\.?$/) {
    2996     my $code;
    2997     ($code,$zone) = _zone2cidr($zone);
    2998     return ('FAIL', $zone) if $code eq 'FAIL';
    2999   }
    3000   $zone = NetAddr::IP->new($zone);
    3001 
    3002   return ('FAIL',"Zone name must be a valid CIDR netblock") unless ($zone && $zone->addr !~ /^0/);
    3003   my $revpatt = shift;  # construct a custom (A/AAAA+)? PTR template record
    3004   my $group = shift;
    3005   my $state = shift;
    3006   my $defloc = shift || '';
    3007 
    3008   $state = 1 if $state =~ /^active$/;
    3009   $state = 1 if $state =~ /^on$/;
    3010   $state = 0 if $state =~ /^inactive$/;
    3011   $state = 0 if $state =~ /^off$/;
    3012 
    3013   return ('FAIL',"Invalid zone status") if $state !~ /^\d+$/;
    3014 
    3015 # quick check to start to see if we've already got one
    3016   my ($rdns_id) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet = ? AND default_location = ?",
    3017         undef, ("$zone", $defloc));
    3018 
    3019   return ('FAIL', "Zone already exists") if $rdns_id;
    3020 
    3021   # Allow transactions, and raise an exception on errors so we can catch it later.
    3022   # Use local to make sure these get "reset" properly on exiting this block
    3023   local $dbh->{AutoCommit} = 0;
    3024   local $dbh->{RaiseError} = 1;
    3025 
    3026   my $warnstr = '';
    3027   my $defttl = 3600;    # 1 hour should be reasonable.  And unless things have gone horribly
    3028                         # wrong, we should have a value to override this anyway.
    3029 
    3030   # Wrap all the SQL in a transaction
    3031   eval {
    3032     # insert the zone...
    3033     $dbh->do("INSERT INTO revzones (revnet,group_id,status,default_location,zserial) VALUES (?,?,?,?,?)", undef,
    3034         ($zone, $group, $state, $defloc, scalar(time()) ) );
    3035 
    3036     # get the ID...
    3037     ($rdns_id) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
    3038 
    3039     $self->_log(rdns_id => $rdns_id, group_id => $group,
    3040         entry => "Added ".($state ? 'active' : 'inactive')." reverse zone $zone");
    3041 
    3042     # ... and now we construct the standard records from the default set.  NB:  group should be variable.
    3043     my $sth = $dbh->prepare("SELECT host,type,val,ttl FROM default_rev_records WHERE group_id=?");
    3044     my $sth_in = $dbh->prepare("INSERT INTO records (rdns_id,domain_id,host,type,val,ttl,location)".
    3045         " VALUES ($rdns_id,?,?,?,?,?,?)");
    3046     $sth->execute($group);
    3047     while (my ($host,$type,$val,$ttl) = $sth->fetchrow_array()) {
    3048       # Silently skip v4/v6 mismatches.  This is not an error, this is expected.
    3049       if ($zone->{isv6}) {
    3050         next if ($type == 65280 || $type == 65283);
    3051       } else {
    3052         next if ($type == 65281 || $type == 65284);
    3053       }
    3054 
    3055       $host =~ s/ADMINDOMAIN/$self->{domain}/g;
    3056 
    3057       # Check to make sure the IP stubs will fit in the zone.  Under most usage failures here should be rare.
    3058       # On failure, tack a note on to a warning string and continue without adding this record.
    3059       # While we're at it, we substitute $zone for ZONE in the value.
    3060       if ($val eq 'ZONE') {
    3061         # If we've got a pattern, we skip the default record version on (A+)PTR-template types
    3062         next if $revpatt && ($type == 65282 || $type == 65283);
    3063 ##fixme?  do we care if we have multiple whole-zone templates?
    3064         $val = $zone->network;
    3065       } elsif ($val =~ /ZONE/) {
    3066         my $tmpval = $val;
    3067         $tmpval =~ s/ZONE//;
    3068         # Bend the rules and allow single-trailing-number PTR or PTR template records to be inserted
    3069         # as either v4 or v6.  May make this an off-by-default config flag
    3070         # Note that the origin records that may trigger this **SHOULD** already have ZONE,\d
    3071         if ($type == 12 || $type == 65282) {
    3072           $tmpval =~ s/[,.]/::/ if ($tmpval =~ /^[,.]\d+$/ && $zone->{isv6});
    3073           $tmpval =~ s/[,:]+/./ if ($tmpval =~ /^(?:,|::)\d+$/ && !$zone->{isv6});
    3074         }
    3075         my $addr;
    3076         if ($self->_ipparent('n', 'y', \$tmpval, $rdns_id, \$addr)) {
    3077           $val = $addr->addr;
    3078         } else {
    3079           $warnstr .= "\nDefault record '$val $typemap{$type} $host' doesn't fit in $zone, skipping";
    3080           next;
    3081         }
    3082       }
    3083 
    3084       # Substitute $zone for ZONE in the hostname, but only for non-NS records.
    3085       # NS records get this substitution on the value instead.
    3086       $host = _ZONE($zone, $host) if $type != 2;
    3087 
    3088       # Fill in the forward domain ID if we can find it, otherwise:
    3089       # Coerce type down to PTR or PTR template if we can't
    3090       my $domid = 0;
    3091       if ($type >= 65280) {
    3092         if (!($domid = $self->_hostparent($host))) {
    3093           $warnstr .= "\nRecord added as PTR instead of $typemap{$type};  domain not found for $host";
    3094           $type = $reverse_typemap{PTR};
    3095           $domid = 0;   # just to be explicit.
    3096         }
    3097       }
    3098 
    3099       _caseclean(\$type, \$host, \$val, 'n', 'y') if $self->{lowercase};
    3100 
    3101       $sth_in->execute($domid,$host,$type,$val,$ttl,$defloc);
    3102 
    3103       if ($typemap{$type} eq 'SOA') {
    3104         my @tmp1 = split /:/, $host;
    3105         my @tmp2 = split /:/, $val;
    3106         $self->_log(rdns_id => $rdns_id, group_id => $group,
    3107                 entry => "[new $zone] Added SOA record [contact $tmp1[0]] [master $tmp1[1]] ".
    3108                 "[refresh $tmp2[0]] [retry $tmp2[1]] [expire $tmp2[2]] [minttl $tmp2[3]], TTL $ttl");
    3109         $defttl = $tmp2[3];
    3110       } else {
    3111         my $logentry = "[new $zone] Added record '$host $typemap{$type} $val', TTL $ttl";
    3112         $logentry .= ", default location ".$self->getLoc($defloc)->{description} if $defloc;
    3113         $self->_log(rdns_id => $rdns_id, domain_id => $domid, group_id => $group, entry => $logentry);
    3114       }
    3115     }
    3116 
    3117     # Generate record based on provided pattern. 
    3118     if ($revpatt) {
    3119       my $host;
    3120       my $type = ($zone->{isv6} ? 65284 : 65283);
    3121       my $val = $zone->network;
    3122 
    3123       # Substitute $zone for ZONE in the hostname.
    3124       $host = _ZONE($zone, $revpatt);
    3125 
    3126       my $domid = 0;
    3127       if (!($domid = $self->_hostparent($host))) {
    3128         $warnstr .= "\nDefault pattern added as PTR template instead of $typemap{$type};  domain not found for $host";
    3129         $type = 65282;
    3130         $domid = 0;     # just to be explicit.
    3131       }
    3132 
    3133       $sth_in->execute($domid,$host,$type,$val,$defttl,$defloc);
    3134       my $logentry = "[new $zone] Added record '$host $typemap{$type}";
    3135       $self->_log(rdns_id => $rdns_id, domain_id => $domid, group_id => $group,
    3136         entry => $logentry." $val', TTL $defttl from pattern");
    3137     }
    3138 
    3139     # If there are warnings (presumably about default records skipped for cause) log them
    3140     $self->_log(rdns_id => $rdns_id, group_id => $group, entry => "Warning(s) adding $zone:$warnstr")
    3141         if $warnstr;
    3142 
    3143     # once we get here, we should have suceeded.
    3144     $dbh->commit;
    3145   }; # end eval
    3146 
    3147   if ($@) {
    3148     my $msg = $@;
    3149     eval { $dbh->rollback; };
    3150     $self->_log(group_id => $group, entry => "Failed adding reverse zone $zone ($msg)")
    3151         if $self->{log_failures};
    3152     $dbh->commit;       # since we enabled transactions earlier
    3153     return ('FAIL',$msg);
    3154   } else {
    3155     my $retcode = 'OK';
    3156     if ($warnstr) {
    3157       $resultstr = $warnstr;
    3158       $retcode = 'WARN';
    3159     }
    3160     return ($retcode, $rdns_id);
    3161   }
    3162 
    3163 } # end addRDNS()
    31643164
    31653165
Note: See TracChangeset for help on using the changeset viewer.