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