File Coverage

blib/lib/Net/DRI/Data/Hosts.pm
Criterion Covered Total %
statement 109 128 85.1
branch 46 68 67.6
condition 26 54 48.1
subroutine 15 17 88.2
pod 9 10 90.0
total 205 277 74.0


line stmt bran cond sub pod time code
1             ## Domain Registry Interface, Implements a list of host (names+ip) with order preserved
2             ##
3             ## Copyright (c) 2005-2009,2013-2014 Patrick Mevzek . All rights reserved.
4             ##
5             ## This file is part of Net::DRI
6             ##
7             ## Net::DRI is free software; you can redistribute it and/or modify
8             ## it under the terms of the GNU General Public License as published by
9             ## the Free Software Foundation; either version 2 of the License, or
10             ## (at your option) any later version.
11             ##
12             ## See the LICENSE file that comes with this distribution for more details.
13             ####################################################################################################
14              
15             package Net::DRI::Data::Hosts;
16              
17 69     69   809 use strict;
  69         91  
  69         1611  
18 69     69   224 use warnings;
  69         74  
  69         1507  
19              
20 69     69   256 use base qw(Class::Accessor::Chained::Fast);
  69         104  
  69         6349  
21              
22             __PACKAGE__->mk_accessors(qw(name loid));
23              
24 69     69   4074 use Net::DRI::Util;
  69         94  
  69         66416  
25              
26             =pod
27              
28             =head1 NAME
29              
30             Net::DRI::Data::Hosts - Handle ordered list of nameservers (name, IPv4 addresses, IPv6 addresses) for Net::DRI
31              
32             =head1 SYNOPSIS
33              
34             use Net::DRI::Data::Hosts;
35              
36             my $dh=Net::DRI::Data::Hosts->new();
37             $dh->add('ns.example.foo',['1.2.3.4','1.2.3.5']);
38             $dh->add('ns2.example.foo',['10.1.1.1']); ## Third element can be an array ref of IPv6 addresses
39             ## ->add() returns the object itself, and thus can be chained
40              
41             ## Number of nameservers
42             print $dh->count(); ## Gives 2
43              
44             ## List of names, either all without arguments, or the amount given by the argument
45             my @a=$dh->get_names(2); ## Gives ('ns.example.foo','ns2.example.foo')
46              
47             ## Details for the nth nameserver (the list starts at 1 !)
48             my @d=$dh->get_details(2); ## Gives ('ns2.example.foo',['10.1.1.1'])
49              
50             ## Details by name is possible also
51             my @d=$dh->get_details('ns2.example.foo');
52              
53             =head1 DESCRIPTION
54              
55             Order of nameservers is preserved. Order of IP addresses is preserved, but no duplicate IP is allowed.
56              
57             If you try to add a nameserver that is already in the list, the IP
58             addresses provided will be added to the existing IP addresses (without duplicates)
59              
60             Hostnames are verified before being used with Net::DRI::Util::is_hostname().
61              
62             IP addresses are verified with Net::DRI::Util::is_ipv4() and Net::DRI::Util::is_ipv6().
63              
64             =head1 METHODS
65              
66             =head2 new(...)
67              
68             creates a new instance ; if parameters are given, add() is called with them all at once
69              
70             =head2 new_set(...)
71              
72             creates a new instance ; if parameters are given, add() is called once for each parameter
73              
74             =head2 clear()
75              
76             clears the current list of nameservers
77              
78             =head2 set(...)
79              
80             clears the current list of nameservers, and call add() once for each parameter passed
81              
82             =head2 add(name,[ipv4],[ipv6])
83              
84             adds a new nameserver with the given name and lists of IPv4 and IPv6 addresses
85              
86             =head2 name()
87              
88             name of this object (for example for registries having the notion of host groups) ;
89             this has nothing to do with the name(s) of the nameservers inside this object
90              
91             =head2 loid()
92              
93             local id of this object
94              
95             =head2 get_names(limit)
96              
97             returns a list of nameservers' names included in this object ; if limit is provided
98             we return only the number of names asked
99              
100             =head2 count()
101              
102             returns the number of nameservers currently stored in this object
103              
104             =head2 is_empty()
105              
106             returns 0 if this object has nameservers, 1 otherwise
107              
108             =head2 get_details(pos_or_name)
109              
110             given an integer (position in the list, we start to count at 1) or a name,
111             we return all details as a 3 element array in list context or only the first
112             element (the name) in scalar context for the nameserver stored at
113             the given position or with the given name ; returns undef if nothing found
114             at the position/with the name given.
115              
116             =head1 SUPPORT
117              
118             For now, support questions should be sent to:
119              
120             Enetdri@dotandco.comE
121              
122             Please also see the SUPPORT file in the distribution.
123              
124             =head1 SEE ALSO
125              
126             http://www.dotandco.com/services/software/Net-DRI/
127              
128             =head1 AUTHOR
129              
130             Patrick Mevzek, Enetdri@dotandco.comE
131              
132             =head1 COPYRIGHT
133              
134             Copyright (c) 2005-2009,2013-2014 Patrick Mevzek .
135             All rights reserved.
136              
137             This program is free software; you can redistribute it and/or modify
138             it under the terms of the GNU General Public License as published by
139             the Free Software Foundation; either version 2 of the License, or
140             (at your option) any later version.
141              
142             See the LICENSE file that comes with this distribution for more details.
143              
144             =cut
145              
146             ####################################################################################################
147              
148             sub new
149             {
150 14     14 1 1081 my ($class,@args)=@_;
151 14         41 my $self={ list => [] }; ## list=>[['',[ipv4],[ipv6],{}]+],options=>{}
152 14         19 bless $self,$class;
153 14 100       35 $self->add(@args) if @args;
154 14         76 return $self;
155             }
156              
157             sub new_set
158             {
159 1     1 1 2 my ($class,@args)=@_;
160 1         3 my $s=$class->new();
161 1         2 foreach (@args) { $s->add($_); }
  2         3  
162 1         7 return $s;
163             }
164              
165             sub clear
166             {
167 0     0 1 0 my $s=shift;
168 0         0 $s->{list}=[];
169 0         0 return;
170             }
171              
172             sub set
173             {
174 5     5 1 315 my ($s,@args)=@_;
175 5         8 $s->{list}=[];
176 5         11 foreach (@args) { $s->add($_); }
  5         9  
177 5         39 return $s;
178             }
179              
180             sub add
181             {
182 18     18 1 2023 my ($self,$in,$e1,$e2,$ipall,$rextra)=@_;
183 18 100 66     51 ($ipall,$rextra)=(undef,$ipall) if (defined($ipall) && ref($ipall));
184 18 50 33     63 return unless (defined($in) && $in);
185              
186 18 100       40 if (ref $in eq 'ARRAY')
187             {
188 1         28 return $self->add(@$in);
189             }
190              
191 17 100 66     41 if (defined $e2 && $e2)
192             {
193 3         7 $self->_push($in,$e1,$e2,$ipall,$rextra);
194 3         6 return $self;
195             }
196              
197 14 100 66     42 if (defined $e1 && $e1)
198             {
199 8         13 $self->_push($in,_separate_ips($e1,$ipall),$ipall,$rextra);
200 8         15 return $self;
201             }
202              
203 6         12 $self->_push($in,[],[],1,$rextra);
204 6         12 return $self;
205             }
206              
207             sub _separate_ips
208             {
209 8     8   12 my (@args)=@_;
210 8         6 my (@ip4,@ip6);
211 8         45 my $ipall=pop @args;
212 8 50       16 $ipall=0 unless defined $ipall;
213 8 50       10 foreach my $ip (map {ref($_)? @{$_} : $_} @args)
  8         18  
  8         21  
214             {
215             ## We keep only the public ips
216 11 50       32 push @ip4,$ip if Net::DRI::Util::is_ipv4($ip,1-$ipall);
217 11 50       25 push @ip6,$ip if Net::DRI::Util::is_ipv6($ip,1-$ipall);
218             }
219 8         32 return (\@ip4,\@ip6);
220             }
221              
222             sub _push
223             {
224 17     17   23 my ($self,$name,$ipv4,$ipv6,$ipall,$rextra)=@_;
225 17 100       25 $ipall=0 unless defined $ipall;
226 17 50 33     91 chop($name) if (defined $name && $name && $name=~m/\.$/);
      33        
227 17 50       40 return unless Net::DRI::Util::is_hostname($name);
228 17         30 $name=lc($name); ## by default, hostnames are case insensitive
229              
230             ## We keep only the public ips
231 17 50       35 my @ipv4=grep { Net::DRI::Util::is_ipv4($_,1-$ipall) } ref $ipv4 ? @$ipv4 : ($ipv4);
  11         22  
232 17 50       31 my @ipv6=grep { Net::DRI::Util::is_ipv6($_,1-$ipall) } ref $ipv6 ? @$ipv6 : ($ipv6);
  1         4  
233              
234 17 100 100     32 if ($self->count() && defined $self->get_details($name)) ## name already here, we append IP
235             {
236 4         5 foreach my $el (@{$self->{list}})
  4         9  
237             {
238 5 100       10 next unless ($el->[0] eq $name);
239 4         4 unshift @ipv4,@{$el->[1]};
  4         6  
240 4         4 unshift @ipv6,@{$el->[2]};
  4         5  
241 4         6 $el->[1]=_remove_dups_ip(\@ipv4);
242 4         8 $el->[2]=_remove_dups_ip(\@ipv6);
243 4 100 66     14 if (defined $el->[3] || defined $rextra)
244             {
245 1 50 33     4 $el->[3]={ defined $el->[3] ? %{$el->[3]} : (), (defined $rextra && ref $rextra eq 'HASH')? %$rextra : () };
  1 50       11  
246             }
247 4         5 last;
248             }
249             } else
250             {
251 13         18 push @{$self->{list}},[$name,_remove_dups_ip(\@ipv4),_remove_dups_ip(\@ipv6),$rextra];
  13         32  
252             }
253 17         30 return;
254             }
255              
256             sub _remove_dups_ip
257             {
258 34     34   23 my $ip=shift;
259 34         27 my @r;
260             my %tmp;
261 34 50       78 @r=ref($ip)? grep { ! $tmp{$_}++ } @$ip : ($ip) if defined $ip;
  19 50       39  
262 34         70 return \@r;
263             }
264              
265             ## Give back an array of all hostnames, or up to a limit if provided
266             sub get_names
267             {
268 10     10 1 2889 my ($self,$limit)=@_;
269 10 50 33     50 return unless (defined $self && ref $self);
270 10         17 my $c=$self->count();
271 10 100 100     42 $c=$limit if ($limit && ($limit <= $c));
272 10         11 my @r;
273 10         26 foreach (0..($c-1))
274             {
275 13         23 push @r,$self->{list}->[$_]->[0];
276             }
277 10         26 return @r;
278             }
279              
280             sub count
281             {
282 55     55 1 273 my $self=shift;
283 55 50 33     173 return unless (defined $self && ref $self);
284 55         35 return scalar(@{$self->{list}});
  55         136  
285             }
286              
287             sub is_empty
288             {
289 10     10 1 10 my $self=shift;
290 10         13 my $c=$self->count();
291 10 100 66     66 return (defined $c && ($c > 0))? 0 : 1;
292             }
293              
294             sub get_details
295             {
296 16     16 1 1124 my ($self,$pos)=@_;
297 16 50 33     102 return unless (defined $self && ref $self && defined $pos && $pos);
      33        
      33        
298 16         25 my $c=$self->count();
299              
300 16 100       49 if ($pos=~m/^\d+$/)
301             {
302 5 50 33     18 return unless ($c && ($pos <= $c));
303 5         9 my $el=$self->{list}->[$pos-1];
304 5 50       22 return wantarray()? @$el : $el->[0];
305             } else
306             {
307 11         14 $pos=lc($pos);
308 11         9 foreach my $el (@{$self->{list}})
  11         19  
309             {
310 12 100       29 next unless ($el->[0] eq $pos);
311 8 100       32 return wantarray()? @$el : $el->[0];
312             }
313 3         9 return;
314             }
315             }
316              
317             # Do not use this method for anything else than debugging. The output format is not guaranteed to remain stable
318             sub as_string
319             {
320 0     0 0   my ($self)=shift;
321 0           my @s;
322 0           foreach my $el (@{$self->{list}})
  0            
323             {
324 0           my $s=$el->[0];
325 0           my $ips=join(',',@{$el->[1]},@{$el->[2]});
  0            
  0            
326 0 0         $s.=' ['.$ips.']' if $ips;
327 0 0 0       $s.=' {'.join(' ',map { $_.'='.$el->[3]->{$_} } sort { $a cmp $b } keys %{$el->[3]}).'}' if (defined $el->[3] && %{$el->[3]});
  0            
  0            
  0            
  0            
328 0           push @s,$s;
329             }
330 0           return join(' ',@s);
331             }
332              
333             ####################################################################################################
334             1;