File Coverage

blib/lib/FusionInventory/Agent/Tools/Network.pm
Criterion Covered Total %
statement 89 106 83.9
branch 26 46 56.5
condition 9 21 42.8
subroutine 18 19 94.7
pod 11 11 100.0
total 153 203 75.3


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Tools::Network;
2              
3 100     100   14516310 use strict;
  100         186  
  100         2741  
4 100     100   563 use warnings;
  100         204  
  100         2935  
5 100     100   522 use base 'Exporter';
  100         423  
  100         9200  
6              
7 100     100   1317 use English qw(-no_match_vars);
  100         5735  
  100         730  
8 100     100   157471 use Net::IP qw(:PROC);
  100         4763758  
  100         53833  
9 100     100   89709 use Net::hostent;
  100         195294  
  100         531  
10 100     100   80048 use Socket;
  100         325859  
  100         75016  
11              
12 100     100   2665 use FusionInventory::Agent::Tools;
  100         228  
  100         162743  
13              
14             our @EXPORT = qw(
15             $mac_address_pattern
16             $ib_mac_address_pattern
17             $any_mac_address_pattern
18             $ip_address_pattern
19             $alt_mac_address_pattern
20             $hex_ip_address_pattern
21             $network_pattern
22             getSubnetAddress
23             getSubnetAddressIPv6
24             getNetworkMask
25             getNetworkMaskIPv6
26             isSameNetwork
27             isSameNetworkIPv6
28             hex2canonical
29             alt2canonical
30             resolve
31             compile
32             isPartOf
33             );
34              
35             my $dec_byte = qr/[0-9]{1,3}/;
36             my $hex_byte = qr/[0-9A-F]{1,2}/i;
37             my $padded_hex_byte = qr/[0-9A-F]{2}/i;
38              
39             our $mac_address_pattern = qr/
40             $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte
41             /x;
42              
43             our $ib_mac_address_pattern = qr/
44             $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte :
45             $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte :
46             $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte : $hex_byte :
47             $hex_byte : $hex_byte
48             /x;
49              
50             our $any_mac_address_pattern = qr/
51             (?:$ib_mac_address_pattern|$mac_address_pattern)
52             /x;
53              
54             our $ip_address_pattern = qr/
55             $dec_byte \. $dec_byte \. $dec_byte \. $dec_byte
56             /x;
57              
58             our $alt_mac_address_pattern = qr/
59             $padded_hex_byte
60             $padded_hex_byte
61             $padded_hex_byte
62             $padded_hex_byte
63             $padded_hex_byte
64             $padded_hex_byte
65             /x;
66              
67             our $hex_ip_address_pattern = qr/
68             $padded_hex_byte
69             $padded_hex_byte
70             $padded_hex_byte
71             $padded_hex_byte
72             /x;
73              
74             our $network_pattern = qr/
75             $dec_byte (?:\. $dec_byte (?:\. $dec_byte (?:\. $dec_byte)?)?)? \/ \d{1,2}
76             /x;
77              
78             sub getSubnetAddress {
79 32     32 1 52 my ($address, $mask) = @_;
80              
81 32 100 100     164 return undef unless $address && $mask; ## no critic (ExplicitReturnUndef)
82              
83 29         91 my $binaddress = ip_iptobin($address, 4);
84 29         390 my $binmask = ip_iptobin($mask, 4);
85 29         272 my $binsubnet = $binaddress & $binmask; ## no critic (ProhibitBitwise)
86              
87 29         78 return ip_bintoip($binsubnet, 4);
88             }
89              
90             sub getSubnetAddressIPv6 {
91 17     17 1 32 my ($address, $mask) = @_;
92              
93 17 50 33     87 return undef unless $address && $mask; ## no critic (ExplicitReturnUndef)
94              
95 17         44 my $binaddress = ip_iptobin(ip_expand_address($address, 6), 6);
96 17         996 my $binmask = ip_iptobin(ip_expand_address($mask, 6), 6);
97 17         958 my $binsubnet = $binaddress & $binmask; ## no critic (ProhibitBitwise)
98              
99 17         50 return ip_compress_address(ip_bintoip($binsubnet, 6), 6);
100             }
101              
102             sub isSameNetwork {
103 2     2 1 1763 my ($address1, $address2, $mask) = @_;
104              
105             ## no critic (ExplicitReturnUndef)
106 2 50 33     23 return undef unless $address1 && $address2 && $mask;
      33        
107              
108 2         8 my $binaddress1 = ip_iptobin($address1, 4);
109 2         40 my $binaddress2 = ip_iptobin($address2, 4);
110 2         21 my $binmask = ip_iptobin($mask, 4);
111              
112             ## no critic (ProhibitBitwise)
113 2         30 return ($binaddress1 & $binmask) eq ($binaddress2 & $binmask);
114             }
115              
116             sub isSameNetworkIPv6 {
117 0     0 1 0 my ($address1, $address2, $mask) = @_;
118              
119             ## no critic (ExplicitReturnUndef)
120 0 0 0     0 return undef unless $address1 && $address2 && $mask;
      0        
121              
122 0         0 my $binaddress1 = ip_iptobin(ip_expand_address($address1, 6), 6);
123 0         0 my $binaddress2 = ip_iptobin(ip_expand_address($address2, 6), 6);
124 0         0 my $binmask = ip_iptobin(ip_expand_address($mask, 6), 6);
125              
126             ## no critic (ProhibitBitwise)
127 0         0 return ($binaddress1 & $binmask) eq ($binaddress2 & $binmask);
128             }
129              
130             sub hex2canonical {
131 31     31 1 1416 my ($address) = @_;
132 31 50       82 return unless $address;
133              
134 31         203 my @bytes = $address =~ /^(?:0x)?(..)(..)(..)(..)$/;
135 31         66 return join('.', map { hex($_) } @bytes);
  124         376  
136             }
137              
138             sub alt2canonical {
139 118     118 1 579 my ($address) = @_;
140 118 50       238 return unless $address;
141              
142 118         586 my @bytes = $address =~ /^(?:0x)?(..)(..)(..)(..)(..)(..)$/;
143 118         1047 return join(':', @bytes);
144             }
145              
146             sub getNetworkMask {
147 17     17 1 813 my ($prefix) = @_;
148              
149 17 100       39 return undef unless $prefix; ## no critic (ExplicitReturnUndef)
150              
151 15         47 return ip_bintoip(ip_get_mask($prefix, 4), 4);
152             }
153              
154             sub getNetworkMaskIPv6 {
155 17     17 1 32 my ($prefix) = @_;
156              
157 17 50       39 return undef unless $prefix; ## no critic (ExplicitReturnUndef)
158              
159 17         51 return ip_compress_address(ip_bintoip(ip_get_mask($prefix, 6), 6), 6);
160             }
161              
162             sub resolve {
163 6     6 1 15 my ($name, $logger) = @_;
164              
165 6         15 my @addresses;
166              
167 6 50       42 if ($Socket::VERSION >= 1.94) {
168             # IPv6 compatible version
169 6         38970 my ($error, @results) = Socket::getaddrinfo(
170             $name, undef, {
171             family => Socket::AF_UNSPEC(),
172             protocol => Socket::IPPROTO_TCP()
173             }
174             );
175 6 100       96 if ($error) {
176 3 50       33 $logger->error(
177             "unable to get address for '$name': $error"
178             ) if $logger;
179 3         60 return;
180             }
181              
182             # and push all of their addresses in the list
183 3         42 foreach my $result (@results) {
184             my ($error, $address) = Socket::getnameinfo(
185 6         2577 $result->{addr}, Socket::NI_NUMERICHOST()
186             );
187 6 50       42 if ($error) {
188 0 0       0 $logger->error(
189             "unable to translate binary address for '$name': $error"
190             ) if $logger;
191 0         0 next;
192             }
193              
194             # Drop the zone index, as Net::IP does not support it
195 6         45 $address =~ s/%.*$//;
196              
197 6         39 push @addresses, $address;
198             }
199             } else {
200             # IPv4-only version
201 0         0 my $result = gethostbyname($name);
202 0 0       0 if (!$result) {
203 0 0       0 $logger->error(
204             "unable to get address for '$name': $ERRNO"
205             ) if $logger;
206 0         0 return;
207             }
208 0         0 foreach my $packed_address (@{$result->addr_list()}) {
  0         0  
209 0         0 push @addresses, inet_ntoa($packed_address);
210             }
211             }
212              
213 3         21 return map { Net::IP->new($_) } @addresses;
  6         4548  
214             }
215              
216             sub compile {
217 15     15 1 33 my ($string, $logger) = @_;
218              
219 15 50       48 return unless $string;
220              
221             # that's already an IP address, just convert it
222 15 100       450 return Net::IP->new($string)
223             if $string =~ /^$ip_address_pattern/;
224              
225             # otherwise resolve the name
226 6         45 return resolve($string, $logger);
227             }
228              
229             sub isPartOf {
230 18     18 1 63 my ($string, $ranges, $logger) = @_;
231              
232 18 50       195 return unless $string;
233 18 50       84 return unless $ranges;
234              
235 18         135 my $address = Net::IP->new($string);
236              
237 18 50       20637 if (!$address) {
238 0         0 $logger->error("Not well formatted source IP: $string");
239 0         0 return;
240             }
241              
242 18         180 foreach my $range (@{$ranges}) {
  18         90  
243 24         147 my $result = $address->overlaps($range);
244              
245 24 100 100     4173 if (!$result && Net::IP::Error()) {
246 12         153 $logger->debug("Server: ".Net::IP::Error());
247 12         57 next;
248             }
249              
250             # included in trusted range
251 12 100       123 return 1 if $result == $IP_A_IN_B_OVERLAP;
252              
253             # equals trusted address
254 9 100       375 return 1 if $result == $IP_IDENTICAL;
255             }
256              
257 9         108 return 0;
258             }
259              
260             1;
261             __END__