1package Netdot::Model::RRPTR; 2 3use base 'Netdot::Model'; 4use warnings; 5use strict; 6use Scalar::Util qw(blessed); 7 8my $logger = Netdot->log->get_logger('Netdot::Model::DNS'); 9 10=head1 Netdot::Model::RRPTR - DNS PTR record Class 11 12=head1 CLASS METHODS 13=cut 14 15################################################################## 16 17=head2 insert 18 19 Override the base method to: 20 - Validate TTL 21 - If not given, figure out the name of the record, using the 22 zone and the IP address 23 - Check for conflicting record types 24 25 Arguments: 26 Any of RRPTRs fields 27 Returns: 28 RRPTR object 29 Examples: 30 $rrptr->insert(ipblock=>$ip); 31 32=cut 33 34sub insert { 35 my($class, $argv) = @_; 36 $class->isa_class_method('insert'); 37 38 $class->throw_fatal("Missing required arguments") 39 unless ( $argv->{ptrdname} && $argv->{ipblock} ); 40 41 # Convert ipblock into object 42 $argv->{ipblock} = $class->_convert_ipblock($argv->{ipblock}); 43 my $ipb = $argv->{ipblock}; 44 45 my $rr; 46 if ( !defined $argv->{rr} ){ 47 $class->throw_fatal("Figuring out the rr field requires passing zone") 48 unless ( $argv->{zone} ); 49 50 my $zone = blessed($argv->{zone}) ? $argv->{zone} : Zone->retrieve($argv->{zone}); 51 unless ( $zone->name =~ /(?:\.in-addr)|(?:\.ip6)\.arpa$/o ){ 52 $class->throw_user(sprintf("Zone %s is not a reverse zone", $zone->name)); 53 } 54 55 # This gets us the the FQDN 56 my $name = $class->get_name(ipblock=>$ipb); 57 58 $rr = RR->search(name=>$name)->first; 59 unless ( $rr ){ 60 my $domain = $zone->name; 61 if ( $domain =~ /^\d+\-\d+\./ ) { 62 # Transform RFC2317 domain name to allowed one 63 $name =~ s/^(\d+).*/$1/; 64 } else { 65 $name =~ s/\.$domain\.?$//i; 66 } 67 68 $rr = RR->insert({zone=>$zone, name=>$name}); 69 $logger->debug("Netdot::Model::RRPTR: Created owner RR for IP: ". 70 $ipb->get_label." as: ".$rr->get_label); 71 } 72 $argv->{rr} = $rr; 73 } 74 75 $rr = blessed($argv->{rr})? $argv->{rr} : RR->retrieve($argv->{rr}); 76 $class->throw_fatal("Invalid rr argument") unless $rr; 77 78 my %linksfrom = RR->meta_data->get_links_from; 79 foreach my $i ( keys %linksfrom ){ 80 if ( $rr->$i ){ 81 next if ( $i eq 'ptr_records' || $i eq 'txt_records' || $i eq 'loc_records' ); 82 $class->throw_user($rr->name.": Cannot add PTR records when other conflicting types exist."); 83 } 84 } 85 86 # Sanitize TTL 87 if ( defined $argv->{ttl} && length($argv->{ttl}) ){ 88 $argv->{ttl} = $class->ttl_from_text($argv->{ttl}); 89 }else{ 90 $argv->{ttl} = $rr->zone->default_ttl; 91 } 92 93 $class->_sanitize_ptrdname($argv); 94 95 delete $argv->{zone}; 96 return $class->SUPER::insert($argv); 97 98} 99 100################################################################## 101 102=head2 get_name - Figure out record name given IP 103 104 Arguments: 105 Hash containing: 106 ipblock - ipblock object 107 Returns: 108 String 109 Examples: 110 my $name = RRPTR->get_name(ipblock=>$ipb); 111=cut 112 113sub get_name { 114 my ($class, %argv) = @_; 115 116 my $ipblock = $argv{ipblock}; 117 unless ( $ipblock ){ 118 $class->throw_fatal("RRPTR::get_name: Missing required arguments"); 119 } 120 121 my $name; 122 if ( $ipblock->version eq '4' ){ 123 $name = join('.', reverse(split(/\./, $ipblock->address)), 'in-addr.arpa'); 124 }elsif ( $ipblock->version eq '6' ){ 125 $name = $ipblock->full_address; 126 $name =~ s/://g; 127 $name = join('.', reverse(split(//, $name)), 'ip6.arpa'); 128 }else { 129 $class->throw_fatal("RRPTR::get_name: Unknown IP version"); 130 } 131 return $name; 132} 133 134=head1 INSTANCE METHODS 135=cut 136 137############################################################################ 138 139=head2 update 140 141 We override the base method to: 142 - Validate TTL 143 144 Arguments: 145 Hash with field/value pairs 146 Returns: 147 Number of rows updated or -1 148 Example: 149 $record->update(\%args) 150 151=cut 152 153sub update { 154 my($self, $argv) = @_; 155 $self->isa_object_method('update'); 156 157 if ( exists $argv->{ipblock} ){ 158 unless ( $argv->{ipblock} ){ 159 # Make sure it is not null or 0 if passed 160 $self->throw_user("IP cannot be null"); 161 } 162 # Convert ipblock into object 163 $argv->{ipblock} = $self->_convert_ipblock($argv->{ipblock}); 164 } 165 166 if ( defined $argv->{ttl} && length($argv->{ttl}) ){ 167 $argv->{ttl} = $self->ttl_from_text($argv->{ttl}); 168 }else{ 169 # keep what we have 170 delete $argv->{ttl}; 171 } 172 173 $self->_sanitize_ptrdname($argv); 174 175 return $self->SUPER::update($argv); 176} 177 178############################################################################ 179 180=head2 delete - Delete object 181 182 We override the delete method for extra functionality: 183 - When removing a PTR record, most likely the RR (name) 184 associated with it needs to be deleted too. 185 186 Arguments: 187 None 188 Returns: 189 True if successful. 190 Example: 191 $rrptr->delete; 192 193=cut 194 195sub delete { 196 my ($self, $argv) = @_; 197 $self->isa_object_method('delete'); 198 199 my $rr = $self->rr; 200 $self->SUPER::delete(); 201 202 # If RR has no more associated records 203 # it should be deleted 204 unless ( $rr->sub_records ){ 205 $rr->delete; 206 } 207 return 1; 208} 209 210 211################################################################## 212 213=head2 as_text 214 215 Returns the text representation of this record 216 217 Arguments: 218 None 219 Returns: 220 string 221 Examples: 222 print $rr->as_text(); 223 224=cut 225 226sub as_text { 227 my $self = shift; 228 $self->isa_object_method('as_text'); 229 230 return $self->_net_dns->string(); 231} 232 233 234############################################################################ 235# PRIVATE METHODS 236############################################################################ 237 238############################################################################ 239# _sanitize_ptrdname 240# 241# Args: 242# hashref 243# Returns: 244# True, or throws exception if validation fails 245# Examples: 246# $class->_sanitize_ptrdname($argv); 247# 248sub _sanitize_ptrdname { 249 my ($self, $argv) = @_; 250 251 # Sanitize ptrdname 252 if ( exists $argv->{ptrdname} ){ 253 if ( length($argv->{ptrdname}) < 1 || length($argv->{ptrdname}) > 255 ){ 254 $self->throw_user("Invalid domain name size"); 255 } 256 $argv->{ptrdname} =~ s/\s+//g; # remove spaces 257 $argv->{ptrdname} = lc($argv->{ptrdname}); # lowercase 258 } 259 1; 260} 261 262################################################################## 263# check if IP is an address string, if so then convert into object 264sub _convert_ipblock { 265 my ($self, $ip) = @_; 266 267 if ( blessed($ip) ){ 268 return $ip; 269 }elsif ( $ip =~ /\D/ ){ 270 # value has non-digits 271 if ( my $ipblock = Ipblock->search(address=>$ip)->first ){ 272 return $ipblock; 273 }else { 274 return Ipblock->insert({address=>$ip, status=>'Static'}); 275 } 276 }else{ 277 # Value must be an integer, so it might be an id 278 if ( my $ipblock = Ipblock->retrieve($ip) ) { 279 return $ipblock; 280 }else{ 281 $self->throw_user("Invalid ipblock value: $ip"); 282 } 283 } 284} 285 286################################################################## 287sub _net_dns { 288 my $self = shift; 289 290 my $ndo = Net::DNS::RR->new( 291 name => $self->rr->get_label, 292 ttl => $self->ttl, 293 class => 'IN', 294 type => 'PTR', 295 ptrdname => $self->ptrdname, 296 ); 297 298 return $ndo; 299} 300 301 302=head1 AUTHOR 303 304Carlos Vicente, C<< <cvicente at ns.uoregon.edu> >> 305 306=head1 COPYRIGHT & LICENSE 307 308Copyright 2012 University of Oregon, all rights reserved. 309 310This program is free software; you can redistribute it and/or modify 311it under the terms of the GNU General Public License as published by 312the Free Software Foundation; either version 2 of the License, or 313(at your option) any later version. 314 315This program is distributed in the hope that it will be useful, but 316WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 317or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 318License for more details. 319 320You should have received a copy of the GNU General Public License 321along with this program; if not, write to the Free Software Foundation, 322Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 323 324=cut 325 326#Be sure to return 1 3271; 328 329