Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add case insensitive searching for zone names #58

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Netdot/Model.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use Time::Local;
use Net::DNS;
use Digest::MD5 qw(md5_hex);
use Scalar::Util qw(blessed);
use Class::DBI::AbstractSearch;

=head1 NAME

Expand Down
112 changes: 60 additions & 52 deletions lib/Netdot/Model/Zone.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,43 @@ DNS Zone Class

We override the base method to add functionality:

- Return the most specific domain name.

If given:
- Return the most specific domain name.
- Perform case-insensitive searches for the name of the zone.

If given:

name=>dns.cs.local.domain
name => dns.cs.local.domain

we will look for a Zone object in this order:
we will look for a Zone object in this order:

dns.cs.local.domain (not found)
cs.local.domain (not found)
local.domain (found)

return 'local.domain'

- Search the ZoneAlias table in addition to the Zone table.

Arguments:
Hash with key/value pairs
Returns:
See Class::DBI search
Examples:
Zone->search(name=>'some.domain.name')
Arguments:
Hash with key/value pairs
Returns:
See Class::DBI search
Examples:
my $zone_obj = Zone->search(name => 'some.domain.name');

=cut

sub search {
my ($class, @args) = @_;
$class->isa_class_method('search');

@args = %{ $args[0] } if ref $args[0] eq "HASH";
my $opts = @args % 2 ? pop @args : {};
my $opts = @args % 2 ? pop @args : {};
my %argv = @args;

if ( exists $argv{id} && $argv{id} =~ /\D+/ ){
# No use searching for non-digits in id field
$argv{id} = 0;
if (exists $argv{id} && $argv{id} =~ /\D+/) {
# No use searching for non-digits in id field
$argv{id} = 0;
}

my (@result, $result);
Expand All @@ -69,42 +71,48 @@ sub search {
}

if (@result || $result) {
return wantarray ? @result : $result;
}elsif ( defined $argv{name} ){
if ( my $alias = ZoneAlias->search(name=>$argv{name})->first ) {
return $class->SUPER::search(id => $alias->zone->id, $opts);
}elsif ( $argv{name} =~ /\./ && !Ipblock->matches_v4($argv{name}) ){
my @sections = split '\.', $argv{name};

# first try to search for the RFC2317 reverse if it exists
if ( $argv{name} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\.in-addr\.arpa$/o ) {
my $address = join('.',($4, $3, $2, $1));
if ( my $ipb = Ipblock->search(address=>$address)->first ){
if ( my $subnet = $ipb->parent ){
my $subnetaddr = $subnet->address;
my $prefix = $subnet->prefix;
my @octs = split('\.', $subnetaddr);
$argv{name} = $octs[3]."-".$prefix.".$octs[2].$octs[1].$octs[0].in-addr.arpa";
$logger->debug(sub{ "Zone::search: $argv{name}" });
if ( $class->SUPER::search(%argv, $opts) ){
$logger->debug(sub{ "Zone::search: found: ", $argv{name} });
return $class->SUPER::search(%argv, $opts);
}
}
}
}
while ( @sections ){
$argv{name} = join '.', @sections;
$logger->debug(sub{ "Zone::search: $argv{name}" });
if ( $class->SUPER::search(%argv, $opts) ){
# We call the method again to not mess
# with CDBI's wantarray checks
$logger->debug(sub{ "Zone::search: found: ", $argv{name} });
return $class->SUPER::search(%argv, $opts);
}
shift @sections;
}
}
return wantarray ? @result : $result;
}
elsif (defined $argv{name}) {
if (my $alias = ZoneAlias->search_where(name => ['lower(name) = ?', lc($argv{name})])->first) {
return $class->SUPER::search(id => $alias->zone->id, $opts);
}
elsif ($argv{name} =~ /\./ && !Ipblock->matches_v4($argv{name})) {
my @sections = split '\.', $argv{name};

# first try to search for the RFC2317 reverse if it exists
if ($argv{name} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\.in-addr\.arpa$/) {
my $address = join('.',($4, $3, $2, $1));
if (my $ipb = Ipblock->search(address => $address)->first) {
if (my $subnet = $ipb->parent) {
my $subnetaddr = $subnet->address;
my $prefix = $subnet->prefix;
my @octs = split('\.', $subnetaddr);
$argv{name} = $octs[3]."-".$prefix.".$octs[2].$octs[1].$octs[0].in-addr.arpa";
$logger->debug(sub{ "Zone::search: $argv{name}" });
if ($class->SUPER::search(%argv, $opts)) {
$logger->debug(sub{ "Zone::search: found: ", $argv{name} });
return $class->SUPER::search(%argv, $opts);
}
}
}
}
while (@sections) {
my $new_name = join '.', @sections;
$argv{name} = [
'lower(name) = ?',
lc($new_name),
];
$logger->debug(sub{ "Zone::search: $new_name" });
if ($class->SUPER::search_where(\%argv, $opts)) {
# We call the method again to not mess
# with CDBI's wantarray checks
$logger->debug(sub{ "Zone::search: found: $new_name" });
return $class->SUPER::search_where(\%argv, $opts);
}
shift @sections;
}
}
}
return wantarray ? @result : $result;
}
Expand Down
23 changes: 21 additions & 2 deletions t/Zone.t
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,20 @@ use lib "lib";

BEGIN { use_ok('Netdot::Model::Zone'); }

my $obj = Zone->insert({name=>'domain.name'});
isa_ok($obj, 'Netdot::Model::Zone', 'insert');
my $obj = Zone->insert(
{
name => 'domain.name',
},
);
isa_ok($obj, 'Netdot::Model::Zone', 'insert zone');

my $alias = ZoneAlias->insert(
{
name => 'alias.name',
zone => $obj->id,
},
);
isa_ok($alias, 'Netdot::Model::ZoneAlias', 'insert zone alias');

lives_and { is(Zone->_dot_arpa_to_ip('1.in-addr.arpa'), '1.0.0.0/8', 'IPv4 /8 .arpa zone to address') };
lives_and { is(Zone->_dot_arpa_to_ip('2.1.in-addr.arpa'), '1.2.0.0/16', 'IPv4 /16 .arpa zone to address') };
Expand All @@ -28,9 +40,16 @@ lives_and { is(Zone->_dot_arpa_to_ip('c.b.a.9.8.7.6.5.4.3.2.1.ip6.arpa'), '1234:

is(Zone->search(name=>'sub.domain.name')->first, $obj, 'search scalar' );
is_deeply([Zone->search(name=>'sub.domain.name')], [$obj], 'search array' );
is(Zone->search(name=>'sub.DoMaIn.NaMe')->first, $obj, 'case insensitive search scalar' );
is_deeply([Zone->search(name=>'sub.DoMaIn.NaMe')], [$obj], 'case insensitive search array' );
is(Zone->search(name=>'fake')->first, undef, 'search empty scalar' );
is_deeply([Zone->search(name=>'fake')], [], 'search empty array' );

is(Zone->search(name=>'alias.name')->first, $obj, 'alias search scalar' );
is_deeply([Zone->search(name=>'alias.name')], [$obj], 'alias search array' );
is(Zone->search(name=>'AlIaS.NaMe')->first, $obj, 'case insensitive alias search scalar' );
is_deeply([Zone->search(name=>'AlIaS.NaMe')], [$obj], 'case insensitive alias search array' );

is(Zone->search_like(name=>'domain.name')->first, $obj, 'search_like scalar' );
is_deeply([Zone->search_like(name=>'domain.name')], [$obj], 'search_like array' );
is(Zone->search_like(name=>'fake')->first, undef, 'search_like empty scalar' );
Expand Down