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   572 use strict;
  69         90  
  69         1630  
18 69     69   223 use warnings;
  69         81  
  69         1557  
19              
20 69     69   224 use base qw(Class::Accessor::Chained::Fast);
  69         88  
  69         6154  
21              
22             __PACKAGE__->mk_accessors(qw(name loid));
23              
24 69     69   3703 use Net::DRI::Util;
  69         89  
  69         65262  
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 1729 my ($class,@args)=@_;
151 14         31 my $self={ list => [] }; ## list=>[['',[ipv4],[ipv6],{}]+],options=>{}
152 14         20 bless $self,$class;
153 14 100       36 $self->add(@args) if @args;
154 14         65 return $self;
155             }
156              
157             sub new_set
158             {
159 1     1 1 2 my ($class,@args)=@_;
160 1         2 my $s=$class->new();
161 1         2 foreach (@args) { $s->add($_); }
  2         3  
162 1         3 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 428 my ($s,@args)=@_;
175 5         6 $s->{list}=[];
176 5         11 foreach (@args) { $s->add($_); }
  5         10  
177 5         33 return $s;
178             }
179              
180             sub add
181             {
182 18     18 1 2590 my ($self,$in,$e1,$e2,$ipall,$rextra)=@_;
183 18 100 66     46 ($ipall,$rextra)=(undef,$ipall) if (defined($ipall) && ref($ipall));
184 18 50 33     62 return unless (defined($in) && $in);
185              
186 18 100       28 if (ref $in eq 'ARRAY')
187             {
188 1         27 return $self->add(@$in);
189             }
190              
191 17 100 66     36 if (defined $e2 && $e2)
192             {
193 3         5 $self->_push($in,$e1,$e2,$ipall,$rextra);
194 3         3 return $self;
195             }
196              
197 14 100 66     37 if (defined $e1 && $e1)
198             {
199 8         14 $self->_push($in,_separate_ips($e1,$ipall),$ipall,$rextra);
200 8         16 return $self;
201             }
202              
203 6         10 $self->_push($in,[],[],1,$rextra);
204 6         9 return $self;
205             }
206              
207             sub _separate_ips
208             {
209 8     8   11 my (@args)=@_;
210 8         7 my (@ip4,@ip6);
211 8         9 my $ipall=pop @args;
212 8 50       17 $ipall=0 unless defined $ipall;
213 8 50       11 foreach my $ip (map {ref($_)? @{$_} : $_} @args)
  8         19  
  8         19  
214             {
215             ## We keep only the public ips
216 11 50       31 push @ip4,$ip if Net::DRI::Util::is_ipv4($ip,1-$ipall);
217 11 50       26 push @ip6,$ip if Net::DRI::Util::is_ipv6($ip,1-$ipall);
218             }
219 8         25 return (\@ip4,\@ip6);
220             }
221              
222             sub _push
223             {
224 17     17   29 my ($self,$name,$ipv4,$ipv6,$ipall,$rextra)=@_;
225 17 100       28 $ipall=0 unless defined $ipall;
226 17 50 33     82 chop($name) if (defined $name && $name && $name=~m/\.$/);
      33        
227 17 50       36 return unless Net::DRI::Util::is_hostname($name);
228 17         23 $name=lc($name); ## by default, hostnames are case insensitive
229              
230             ## We keep only the public ips
231 17 50       34 my @ipv4=grep { Net::DRI::Util::is_ipv4($_,1-$ipall) } ref $ipv4 ? @$ipv4 : ($ipv4);
  11         22  
232 17 50       30 my @ipv6=grep { Net::DRI::Util::is_ipv6($_,1-$ipall) } ref $ipv6 ? @$ipv6 : ($ipv6);
  1         2  
233              
234 17 100 100     32 if ($self->count() && defined $self->get_details($name)) ## name already here, we append IP
235             {
236 4         3 foreach my $el (@{$self->{list}})
  4         6  
237             {
238 5 100       9 next unless ($el->[0] eq $name);
239 4         3 unshift @ipv4,@{$el->[1]};
  4         7  
240 4         3 unshift @ipv6,@{$el->[2]};
  4         3  
241 4         6 $el->[1]=_remove_dups_ip(\@ipv4);
242 4         4 $el->[2]=_remove_dups_ip(\@ipv6);
243 4 100 66     15 if (defined $el->[3] || defined $rextra)
244             {
245 1 50 33     3 $el->[3]={ defined $el->[3] ? %{$el->[3]} : (), (defined $rextra && ref $rextra eq 'HASH')? %$rextra : () };
  1 50       8  
246             }
247 4         4 last;
248             }
249             } else
250             {
251 13         17 push @{$self->{list}},[$name,_remove_dups_ip(\@ipv4),_remove_dups_ip(\@ipv6),$rextra];
  13         29  
252             }
253 17         27 return;
254             }
255              
256             sub _remove_dups_ip
257             {
258 34     34   28 my $ip=shift;
259 34         27 my @r;
260             my %tmp;
261 34 50       68 @r=ref($ip)? grep { ! $tmp{$_}++ } @$ip : ($ip) if defined $ip;
  19 50       38  
262 34         63 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 3540 my ($self,$limit)=@_;
269 10 50 33     39 return unless (defined $self && ref $self);
270 10         19 my $c=$self->count();
271 10 100 100     37 $c=$limit if ($limit && ($limit <= $c));
272 10         8 my @r;
273 10         23 foreach (0..($c-1))
274             {
275 13         23 push @r,$self->{list}->[$_]->[0];
276             }
277 10         28 return @r;
278             }
279              
280             sub count
281             {
282 55     55 1 534 my $self=shift;
283 55 50 33     152 return unless (defined $self && ref $self);
284 55         43 return scalar(@{$self->{list}});
  55         126  
285             }
286              
287             sub is_empty
288             {
289 10     10 1 9 my $self=shift;
290 10         12 my $c=$self->count();
291 10 100 66     56 return (defined $c && ($c > 0))? 0 : 1;
292             }
293              
294             sub get_details
295             {
296 16     16 1 2061 my ($self,$pos)=@_;
297 16 50 33     116 return unless (defined $self && ref $self && defined $pos && $pos);
      33        
      33        
298 16         23 my $c=$self->count();
299              
300 16 100       52 if ($pos=~m/^\d+$/)
301             {
302 5 50 33     18 return unless ($c && ($pos <= $c));
303 5         8 my $el=$self->{list}->[$pos-1];
304 5 50       24 return wantarray()? @$el : $el->[0];
305             } else
306             {
307 11         14 $pos=lc($pos);
308 11         10 foreach my $el (@{$self->{list}})
  11         17  
309             {
310 12 100       24 next unless ($el->[0] eq $pos);
311 8 100       34 return wantarray()? @$el : $el->[0];
312             }
313 3         8 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;