File Coverage

blib/lib/Data/Validate/IP.pm
Criterion Covered Total %
statement 535 535 100.0
branch 343 382 89.7
condition 16 20 80.0
subroutine 35 35 100.0
pod 1 2 50.0
total 930 974 95.4


line stmt bran cond sub pod time code
1             package Data::Validate::IP;
2              
3 2     2   5376 use strict;
  2         15  
  2         62  
4 2     2   10 use warnings;
  2         4  
  2         45  
5              
6 2     2   36 use 5.008;
  2         6  
7              
8             our $VERSION = '0.29';
9              
10 2     2   1277 use NetAddr::IP 4;
  2         64579  
  2         12  
11 2     2   291 use Scalar::Util qw( blessed );
  2         6  
  2         213  
12              
13 2     2   15 use base 'Exporter';
  2         6  
  2         695  
14              
15             ## no critic (Modules::ProhibitAutomaticExportation)
16             our @EXPORT = qw(
17             is_ip
18             is_ipv4
19             is_ipv6
20             is_innet_ipv4
21             );
22             ## use critic
23              
24             our $HAS_SOCKET;
25              
26             BEGIN {
27 2     2   7 local $@ = undef;
28             $HAS_SOCKET = (!$ENV{DVI_NO_SOCKET})
29 2   66     16 && eval {
30             require Socket;
31             Socket->import(qw( AF_INET AF_INET6 inet_pton ));
32              
33             # On some platforms, Socket.pm exports an inet_pton that just dies
34             # when it is called. On others, inet_pton accepts various forms of
35             # invalid input.
36             defined &Socket::inet_pton
37             && !defined inet_pton(Socket::AF_INET(), '016.17.184.1')
38             && !defined inet_pton(Socket::AF_INET6(), '2067::1:')
39              
40             # Some old versions of Socket are hopelessly broken
41             && length(inet_pton(Socket::AF_INET(), '1.1.1.1')) == 4;
42             };
43              
44 2 100       18 if ($HAS_SOCKET) {
45 1         4 *is_ipv4 = \&_fast_is_ipv4;
46 1         12 *is_ipv6 = \&_fast_is_ipv6;
47 1         4 *is_ip = \&_fast_is_ip;
48 1         2459 *_build_is_X_ip_subs = \&_build_fast_is_X_ip_subs;
49             }
50             else {
51 1         5 *is_ipv4 = \&_slow_is_ipv4;
52 1         3 *is_ipv6 = \&_slow_is_ipv6;
53 1         2 *is_ip = \&_slow_is_ip;
54 1         2543 *_build_is_X_ip_subs = \&_build_slow_is_X_ip_subs;
55             }
56             }
57              
58             sub new {
59 2     2 0 135827 my $class = shift;
60              
61 2         12 return bless {}, $class;
62             }
63              
64             sub _fast_is_ip {
65 14 100   14   3315 shift if ref $_[0];
66 14         25 my $value = shift;
67              
68 14 50       30 return undef unless defined $value;
69 14 100       66 return $value =~ /:/ ? _fast_is_ipv6($value) : _fast_is_ipv4($value);
70             }
71              
72             sub _fast_is_ipv4 {
73 46 100   46   5617 shift if ref $_[0];
74 46         79 my $value = shift;
75              
76 46 100       104 return undef unless _fast_is_ipv4_packed($value);
77              
78             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
79 23         92 $value =~ /(.+)/;
80 23         97 return $1;
81             }
82              
83             sub _fast_is_ipv4_packed {
84 796     796   1163 my $value = shift;
85              
86 796 50       1675 return undef unless defined $value;
87 796 100       1999 return undef if $value =~ /\0/;
88 778         14477 return inet_pton(Socket::AF_INET(), $value);
89             }
90              
91             sub _slow_is_ip {
92 14 100   14   3737 shift if ref $_[0];
93 14         26 my $value = shift;
94              
95 14   100     30 return _slow_is_ipv4($value) || _slow_is_ipv6($value);
96             }
97              
98             sub _slow_is_ipv4 {
99 806 100   806   7434 shift if ref $_[0];
100 806         1191 my $value = shift;
101              
102 806 50       1695 return undef unless defined($value);
103              
104 806         3455 my (@octets) = $value =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
105 806 100       7173 return undef unless (@octets == 4);
106 487         1004 foreach (@octets) {
107 1804 100 66     5942 return undef if $_ < 0 || $_ > 255;
108 1786 100       4070 return undef if $_ =~ /^0\d{1,2}$/;
109             }
110              
111 433         8631 return join('.', @octets);
112             }
113              
114             sub _fast_is_ipv6 {
115 66 100   66   15045 shift if ref $_[0];
116 66         112 my $value = shift;
117              
118 66 100       130 return undef unless _fast_is_ipv6_packed($value);
119              
120             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
121 36         119 $value =~ /(.+)/;
122 36         176 return $1;
123             }
124              
125             sub _fast_is_ipv6_packed {
126 1392     1392   2166 my $value = shift;
127              
128 1392 50       2981 return undef unless defined $value;
129 1392 100       3582 return undef if $value =~ /\0/;
130 1366 100       4027 return undef if $value =~ /0[[:xdigit:]]{4}/;
131 1342         26433 return inet_pton(Socket::AF_INET6(), $value);
132             }
133              
134             {
135             # This comes from Regexp::IPv6
136             ## no critic (RegularExpressions::ProhibitComplexRegexes)
137             my $ipv6_re
138             = qr/(?-xism::(?::[0-9a-fA-F]{1,4}){0,5}(?:(?::[0-9a-fA-F]{1,4}){1,2}|:(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})))|[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}|:)|(?::(?:[0-9a-fA-F]{1,4})?|(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))))|:(?:(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4})?|))|(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|:[0-9a-fA-F]{1,4}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){0,2})|:))|(?:(?::[0-9a-fA-F]{1,4}){0,2}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){1,2})|:))|(?:(?::[0-9a-fA-F]{1,4}){0,3}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){1,2})|:))|(?:(?::[0-9a-fA-F]{1,4}){0,4}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){1,2})|:)))/;
139              
140             sub _slow_is_ipv6 {
141 1394 100   1394   17760 shift if ref $_[0];
142 1394         2079 my $value = shift;
143              
144 1394 50       2766 return undef unless defined($value);
145              
146 1394 100       2979 return '::' if $value eq '::';
147 1392 100       20530 return undef unless $value =~ /^$ipv6_re$/;
148              
149             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
150 1024         2579 $value =~ /(.+)/;
151 1024         20530 return $1;
152             }
153             }
154              
155             # This is just a quick test - we'll let NetAddr::IP decide if the address is
156             # valid.
157             my $ip_re = qr/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
158             my $partial_ip_re = qr/\d{1,3}(?:\.\d{1,3}){0,2}/;
159              
160             ## no critic (Subroutines::ProhibitExcessComplexity, ControlStructures::ProhibitCascadingIfElse)
161             sub is_innet_ipv4 {
162 28 50   28 1 16026 shift if ref $_[0];
163 28         59 my $value = shift;
164 28         44 my $network = shift;
165              
166 28 50       72 return undef unless defined($value);
167              
168 28         68 my $ip = is_ipv4($value);
169 28 100       71 return undef unless defined $ip;
170              
171             # Backwards compatibility hacks to make it accept things that Net::Netmask
172             # accepts.
173 26 100 100     428 if ( $network eq 'default'
    50 100        
      33        
174             || $network =~ /^$ip_re$/
175             || $network =~ m{^$ip_re/\d\d?$}) {
176              
177 10 50       74 $network = NetAddr::IP->new($network) or return undef;
178             }
179             elsif (!(blessed $network && $network->isa('NetAddr::IP'))) {
180 16         36 my $orig = $network;
181 16 100       352 if ($network =~ /^($ip_re)[:\-]($ip_re)$/) {
    100          
    100          
    50          
182 4         19 my ($net, $netmask) = ($1, $2);
183              
184 4 50       12 my $bits = _netmask_to_bits($netmask)
185             or return undef;
186              
187 4         15 $network = "$net/$bits";
188             }
189             elsif ($network =~ /^($ip_re)\#($ip_re)$/) {
190 2         10 my ($net, $hostmask) = ($1, $2);
191              
192 2 50       13 my $bits = _hostmask_to_bits($hostmask)
193             or return undef;
194              
195 2         12 $network = "$net/$bits";
196             }
197             elsif ($network =~ m{^($partial_ip_re)/(\d\d?)$}) {
198 4         18 my ($net, $bits) = ($1, $2);
199              
200             # This is a hack to avoid a deprecation warning (Use of implicit
201             # split to @_ is deprecated) that shows up on 5.10.1 but not on
202             # newer Perls.
203             #
204             ## no critic(Variables::ProhibitUnusedVarsStricter)
205 4         21 my $octets = scalar(my @tmp = split /\./, $net);
206 4         8 $network = $net;
207 4         15 $network .= '.0' x (4 - $octets);
208 4         13 $network .= "/$bits";
209             }
210             elsif ($network =~ /^$partial_ip_re$/) {
211              
212             ## no critic(Variables::ProhibitUnusedVarsStricter)
213 6         27 my $octets = scalar(my @tmp = split /\./, $network);
214 6 50       16 if ($octets < 4) {
215 6         37 $network .= '.0' x (4 - $octets);
216 6         19 $network .= '/' . $octets * 8;
217             }
218             }
219              
220 16 50       39 if ($orig ne $network) {
221 16         43 _deprecation_warn(
222             'Use of non-CIDR notation for networks with is_innet_ipv4() is deprecated'
223             );
224             }
225              
226 16 50       90 $network = NetAddr::IP->new($network) or return undef;
227             }
228              
229 26 50       10513 my $netaddr_ip = NetAddr::IP->new($ip) or return undef;
230              
231 26 100       9265 return $ip if $network->contains($netaddr_ip);
232 2         47 return undef;
233             }
234             ## use critic;
235              
236             {
237             my %netmasks = (
238             '128.0.0.0' => '1',
239             '192.0.0.0' => '2',
240             '224.0.0.0' => '3',
241             '240.0.0.0' => '4',
242             '248.0.0.0' => '5',
243             '252.0.0.0' => '6',
244             '254.0.0.0' => '7',
245             '255.0.0.0' => '8',
246             '255.128.0.0' => '9',
247             '255.192.0.0' => '10',
248             '255.224.0.0' => '11',
249             '255.240.0.0' => '12',
250             '255.248.0.0' => '13',
251             '255.252.0.0' => '14',
252             '255.254.0.0' => '15',
253             '255.255.0.0' => '16',
254             '255.255.128.0' => '17',
255             '255.255.192.0' => '18',
256             '255.255.224.0' => '19',
257             '255.255.240.0' => '20',
258             '255.255.248.0' => '21',
259             '255.255.252.0' => '22',
260             '255.255.254.0' => '23',
261             '255.255.255.0' => '24',
262             '255.255.255.128' => '25',
263             '255.255.255.192' => '26',
264             '255.255.255.224' => '27',
265             '255.255.255.240' => '28',
266             '255.255.255.248' => '29',
267             '255.255.255.252' => '30',
268             '255.255.255.254' => '31',
269             '255.255.255.255' => '32',
270             );
271              
272             sub _netmask_to_bits {
273 4     4   18 return $netmasks{ $_[0] };
274             }
275             }
276              
277             {
278             my %hostmasks = (
279             '255.255.255.255' => 0,
280             '127.255.255.255' => 1,
281             '63.255.255.255' => 2,
282             '31.255.255.255' => 3,
283             '15.255.255.255' => 4,
284             '7.255.255.255' => 5,
285             '3.255.255.255' => 6,
286             '1.255.255.255' => 7,
287             '0.255.255.255' => 8,
288             '0.127.255.255' => 9,
289             '0.63.255.255' => 10,
290             '0.31.255.255' => 11,
291             '0.15.255.255' => 12,
292             '0.7.255.255' => 13,
293             '0.3.255.255' => 14,
294             '0.1.255.255' => 15,
295             '0.0.255.255' => 16,
296             '0.0.127.255' => 17,
297             '0.0.63.255' => 18,
298             '0.0.31.255' => 19,
299             '0.0.15.255' => 20,
300             '0.0.7.255' => 21,
301             '0.0.3.255' => 22,
302             '0.0.1.255' => 23,
303             '0.0.0.255' => 24,
304             '0.0.0.127' => 25,
305             '0.0.0.63' => 26,
306             '0.0.0.31' => 27,
307             '0.0.0.15' => 28,
308             '0.0.0.7' => 29,
309             '0.0.0.3' => 30,
310             '0.0.0.1' => 31,
311             '0.0.0.0' => 32,
312             );
313              
314             sub _hostmask_to_bits {
315 2     2   11 return $hostmasks{ $_[0] };
316             }
317             }
318              
319             {
320             my %warned_at;
321              
322             sub _deprecation_warn {
323 16     16   38 my $warning = shift;
324 16         120 my @caller = caller(2);
325              
326 16         54 my $caller_info
327             = "at line $caller[2] of $caller[0] in sub $caller[3]";
328              
329 16 100       89 return undef if $warned_at{$warning}{$caller_info}++;
330              
331 2         30 warn "$warning $caller_info\n";
332             }
333             }
334              
335             {
336             my %ipv4_networks = (
337             loopback => { networks => '127.0.0.0/8' },
338             private => {
339             networks => [
340             qw(
341             10.0.0.0/8
342             172.16.0.0/12
343             192.168.0.0/16
344             )
345             ],
346             },
347             testnet => {
348             networks => [
349             qw(
350             192.0.2.0/24
351             198.51.100.0/24
352             203.0.113.0/24
353             )
354             ],
355             },
356             anycast => { networks => '192.88.99.0/24' },
357             multicast => { networks => '224.0.0.0/4' },
358             linklocal => { networks => '169.254.0.0/16' },
359             unroutable => {
360             networks => [
361             qw(
362             0.0.0.0/8
363             100.64.0.0/10
364             192.0.0.0/29
365             198.18.0.0/15
366             240.0.0.0/4
367             )
368             ],
369             },
370             );
371              
372             _build_is_X_ip_subs(\%ipv4_networks, 4);
373             }
374              
375             {
376             my %ipv6_networks = (
377             loopback => { networks => '::1/128' },
378             ipv4_mapped => { networks => '::ffff:0:0/96' },
379             discard => { networks => '100::/64' },
380             special => { networks => '2001::/23' },
381             teredo => {
382             networks => '2001::/32',
383             subnet_of => 'special',
384             },
385             orchid => {
386             networks => '2001:10::/28',
387             subnet_of => 'special',
388             },
389             documentation => { networks => '2001:db8::/32' },
390             private => { networks => 'fc00::/7' },
391             linklocal => { networks => 'fe80::/10' },
392             multicast => { networks => 'ff00::/8' },
393             );
394              
395             _build_is_X_ip_subs(\%ipv6_networks, 6);
396              
397             # This exists for the benefit of the test code.
398             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
399             sub _network_is_subnet_of {
400 1232     1232   409349 my $network = shift;
401 1232         2132 my $other = shift;
402              
403 1232   100     6271 return ($ipv6_networks{$network}{subnet_of} || q{}) eq $other;
404             }
405             }
406              
407             ## no critic (TestingAndDebugging::ProhibitNoStrict, BuiltinFunctions::ProhibitStringyEval)
408             sub _build_slow_is_X_ip_subs {
409 2     2   4 my $networks = shift;
410 2         2 my $ip_number = shift;
411              
412 2 100       7 my $is_ip_sub = $ip_number == 4 ? 'is_ipv4' : 'is_ipv6';
413 2 100       4 my $netaddr_new = $ip_number == 4 ? 'new' : 'new6';
414              
415 2         3 my @all_nets;
416              
417 2         4 local $@ = undef;
418 2         2 for my $type (keys %{$networks}) {
  2         15  
419             my @nets
420 25         1023 = map { NetAddr::IP->$netaddr_new($_) }
421             ref $networks->{$type}{networks}
422 3         13 ? @{ $networks->{$type}{networks} }
423 17 100       58 : $networks->{$type}{networks};
424              
425             # Some IPv6 networks (like TEREDO) are a subset of the special block
426             # so there's no point in checking for them in the is_public_ipv6()
427             # sub.
428 17 100       2504 unless ($networks->{$type}{subnet_of}) {
429 15         37 push @all_nets, @nets;
430             }
431              
432             # We're using code gen rather than just making an anon sub outright so
433             # we don't have to pay the cost of derefencing the $is_ip_sub and the
434             # dynamic dispatch cost for $netaddr_new
435 17 100   150   2608 my $sub = eval sprintf( <<'EOF', $is_ip_sub, $netaddr_new);
  150 50       7079  
  150 100       309  
  150 100       317  
  150 100       398  
  150 50       578  
  110 100       409  
  110 100       28135  
  110 100       353  
  94 50       3831  
  114 100       4835  
  114 100       225  
  114 100       303  
  114 50       343  
  114 100       1622  
  58 100       233  
  58 100       7784  
  58 50       230  
  54 100       2682  
  98 100       6837  
  98 100       206  
  98 50       248  
  98 100       292  
  98 100       357  
  74 100       281  
  74 50       19277  
  74 100       239  
  66 100       2642  
  98 100       6518  
  98 50       177  
  98 100       228  
  98 100       284  
  98 100       356  
  74 50       269  
  74 100       19318  
  74 100       237  
  68 100       2705  
  98 50       6444  
  98 100       200  
  98 100       221  
  98 100       281  
  98 50       341  
  74 100       270  
  74 100       18946  
  74 100       236  
  68 50       2679  
  60 100       5375  
  60 100       126  
  60 100       169  
  60 50       173  
  60 100       254  
  40 100       157  
  40 100       5407  
  114 50       2673  
  34 100       1240  
  146 100       6951  
  146 100       273  
  146 50       314  
  146 100       399  
  146 100       587  
  110 100       380  
  110 50       28399  
  110 100       374  
  94 100       3763  
  60 100       7040  
  60 50       161  
  60 100       172  
  60 100       179  
  60         247  
  40         175  
  40         5617  
  180         4650  
  26         905  
  114         4820  
  114         233  
  114         306  
  114         335  
  114         1581  
  58         234  
  58         8052  
  58         197  
  54         2717  
  94         6912  
  94         185  
  94         226  
  94         267  
  94         372  
  70         234  
  70         18272  
  70         221  
  64         2511  
  114         4748  
  114         208  
  114         258  
  114         310  
  114         1581  
  58         229  
  58         7981  
  58         238  
  54         2703  
  150         6581  
  150         288  
  150         323  
  150         412  
  150         598  
  110         383  
  110         28329  
  110         350  
  98         3885  
  150         5931  
  150         292  
  150         344  
  150         438  
  150         598  
  110         378  
  110         28420  
  110         365  
  106         4245  
  60         670  
  60         129  
  60         144  
  60         172  
  60         260  
  40         183  
  40         5618  
  40         179  
  38         1581  
  114         5709  
  114         276  
  114         297  
  114         301  
  114         1531  
  58         242  
  58         7904  
  162         3854  
  46         2079  
  98         6693  
  98         188  
  98         252  
  98         325  
  98         369  
  74         269  
  74         18924  
  74         238  
  56         2271  
  98         1689  
  98         203  
  98         248  
  98         333  
  98         479  
  74         278  
  74         19462  
  74         256  
  68         2748  
436             sub {
437             shift if ref $_[0];
438             my $value = shift;
439              
440             return undef unless defined $value;
441              
442             my $ip = %s($value);
443             return undef unless defined $ip;
444              
445             my $netaddr_ip = NetAddr::IP->%s($ip);
446             for my $net (@nets) {
447             return $ip if $net->contains($netaddr_ip);
448             }
449             return undef;
450             }
451             EOF
452 17 50       54 die $@ if $@;
453              
454 17         43 my $sub_name = 'is_' . $type . '_ipv' . $ip_number;
455             {
456 2     2   18 no strict 'refs';
  2         4  
  2         218  
  17         26  
457 17         21 *{$sub_name} = $sub;
  17         81  
458             }
459 17         56 push @EXPORT, $sub_name;
460             }
461              
462 2 100   146   288 my $sub = eval sprintf( <<'EOF', $is_ip_sub, $netaddr_new);
  146 50       8540  
  146 100       278  
  146 100       322  
  146 100       398  
  146 50       596  
  110 100       418  
  110 100       28624  
  596         16224  
  24         889  
  114         5742  
  114         232  
  114         264  
  114         323  
  114         1573  
  58         219  
  58         8277  
  566         16402  
  12         542  
463             sub {
464             shift if ref $_[0];
465             my $value = shift;
466              
467             return undef unless defined($value);
468              
469             my $ip = %s($value);
470             return undef unless defined $ip;
471              
472             my $netaddr_ip = NetAddr::IP->%s($ip);
473             for my $net (@all_nets) {
474             return undef if $net->contains($netaddr_ip);
475             }
476              
477             return $ip;
478             }
479             EOF
480 2 50       7 die $@ if $@;
481              
482 2         6 my $sub_name = 'is_public_ipv' . $ip_number;
483             {
484 2     2   14 no strict 'refs';
  2         5  
  2         514  
  2         3  
485 2         3 *{$sub_name} = $sub;
  2         10  
486             }
487 2         8 push @EXPORT, $sub_name;
488             }
489              
490             sub _build_fast_is_X_ip_subs {
491 2     2   3 my $networks = shift;
492 2         3 my $ip_number = shift;
493              
494 2 100       7 my $family = $ip_number == 4 ? Socket::AF_INET() : Socket::AF_INET6();
495              
496 2         2 my @all_nets;
497              
498 2         13 local $@ = undef;
499 2         3 for my $type (keys %{$networks}) {
  2         13  
500             my @nets
501 25         51 = map { _packed_network_and_netmask($family, $_) }
502             ref $networks->{$type}{networks}
503 3         8 ? @{ $networks->{$type}{networks} }
504 17 100       61 : $networks->{$type}{networks};
505              
506             # Some IPv6 networks (like TEREDO) are a subset of the special block
507             # so there's no point in checking for them in the is_public_ipv6()
508             # sub.
509 17 100       49 unless ($networks->{$type}{subnet_of}) {
510 15         24 push @all_nets, @nets;
511             }
512              
513             # We're using code gen rather than just making an anon sub outright so
514             # we don't have to pay the cost of derefencing the $is_ip_sub and the
515             # dynamic dispatch cost for $netaddr_new
516 17 100   98   2729 my $sub = eval sprintf( <<'EOF', $ip_number);
  98 100       1604  
  98 100       183  
  98 100       263  
  98 100       337  
  74 100       180  
  74 100       286  
  6 100       17  
  6 100       40  
  68 100       315  
  114 100       4765  
  114 100       202  
  114 100       296  
  114 100       1539  
  58 100       131  
  58 100       212  
  4 100       19  
  4 100       62  
  54 100       720  
  98 100       6310  
  98 100       182  
  98 100       278  
  98 100       333  
  74 100       162  
  74 100       262  
  6 100       20  
  6 100       38  
  68 100       292  
  60 100       5299  
  60 100       136  
  60 100       159  
  60 100       207  
  40 100       80  
  114 100       292  
  6 100       26  
  6 100       36  
  34 100       129  
  98 100       6422  
  98 100       186  
  98 100       256  
  98 100       340  
  74 100       208  
  74 100       266  
  18 100       59  
  18 100       130  
  56 100       231  
  114 100       4747  
  114 100       211  
  114 100       317  
  114 100       1556  
  58 100       131  
  58         208  
  4         13  
  4         58  
  54         706  
  60         6934  
  60         109  
  60         150  
  60         211  
  40         80  
  180         412  
  14         35  
  14         81  
  26         132  
  146         6784  
  146         239  
  146         382  
  146         488  
  110         253  
  110         395  
  16         43  
  16         112  
  94         433  
  60         618  
  60         106  
  60         155  
  60         215  
  40         96  
  40         135  
  2         7  
  2         19  
  38         157  
  150         6435  
  150         265  
  150         370  
  150         513  
  110         240  
  110         402  
  12         35  
  12         82  
  98         439  
  150         6892  
  150         292  
  150         372  
  150         539  
  110         256  
  110         382  
  16         42  
  16         111  
  94         440  
  114         4687  
  114         200  
  114         280  
  114         1531  
  58         131  
  58         238  
  4         14  
  4         61  
  54         767  
  150         5711  
  150         304  
  150         376  
  150         557  
  110         238  
  110         399  
  4         14  
  4         27  
  106         496  
  98         6341  
  98         191  
  98         259  
  98         338  
  74         166  
  74         264  
  6         19  
  6         38  
  68         287  
  98         6644  
  98         191  
  98         263  
  98         331  
  74         176  
  74         255  
  8         24  
  8         52  
  66         281  
  114         5767  
  114         195  
  114         298  
  114         1520  
  58         130  
  162         469  
  12         32  
  12         184  
  46         552  
  94         6636  
  94         176  
  94         254  
  94         338  
  70         152  
  70         257  
  6         18  
  6         36  
  64         272  
517             sub {
518             shift if ref $_[0];
519             my $value = shift;
520              
521             my $ip = _fast_is_ipv%u_packed($value);
522              
523             return undef unless defined $ip;
524              
525             for my $net (@nets) {
526             if (($net->[1] & $ip) eq $net->[0]) {
527             $value =~ /(.+)/;
528             return $1;
529             }
530             }
531             return undef;
532             }
533             EOF
534 17 50       53 die $@ if $@;
535              
536 17         51 my $sub_name = 'is_' . $type . '_ipv' . $ip_number;
537             {
538 2     2   16 no strict 'refs';
  2         4  
  2         273  
  17         22  
539 17         22 *{$sub_name} = $sub;
  17         90  
540             }
541 17         57 push @EXPORT, $sub_name;
542             }
543              
544 2 100   114   326 my $sub = eval sprintf( <<'EOF', $ip_number);
  114 100       5521  
  114 100       198  
  114 100       312  
  114 100       1557  
  58 100       128  
  536         1633  
  12         37  
  12         162  
  146         7692  
  146         255  
  146         391  
  146         517  
  110         249  
  548         1562  
  24         66  
  24         179  
545             sub {
546             shift if ref $_[0];
547             my $value = shift;
548              
549             my $ip = _fast_is_ipv%u_packed($value);
550              
551             return undef unless defined $ip;
552              
553             for my $net (@all_nets) {
554             return undef if ($net->[1] & $ip) eq $net->[0];
555             }
556              
557             $value =~ /(.+)/;
558             return $1;
559             }
560             EOF
561 2 50       7 die $@ if $@;
562              
563 2         6 my $sub_name = 'is_public_ipv' . $ip_number;
564             {
565 2     2   15 no strict 'refs';
  2         3  
  2         498  
  2         3  
566 2         3 *{$sub_name} = $sub;
  2         10  
567             }
568 2         10 push @EXPORT, $sub_name;
569             }
570              
571             sub _packed_network_and_netmask {
572 25     25   35 my $family = shift;
573 25         33 my $network = shift;
574              
575 25         124 my ($ip, $bits) = split qr{/}, $network, 2;
576              
577             return [
578 25         97 inet_pton($family, $ip),
579             _packed_netmask($family, $bits)
580             ];
581             }
582              
583             sub _packed_netmask {
584 25     25   37 my $family = shift;
585 25         31 my $bits = shift;
586              
587 25 100       48 my $bit_length = $family == Socket::AF_INET() ? 32 : 128;
588              
589 25         115 my $bit_string
590             = join(q{}, (1) x $bits, (0) x ($bit_length - $bits));
591 25         144 return pack('B' . $bit_length, $bit_string);
592             }
593              
594             for my $sub (qw( linklocal loopback multicast private public )) {
595             my $sub_name = "is_${sub}_ip";
596              
597             {
598 2     2   15 no strict 'refs';
  2         4  
  2         227  
599 108 100   108   7420 *{$sub_name}
  108 100       6959  
  108 100       4552  
  108 100       5744  
  108 100       7831  
600             = eval "sub { ${sub_name}v4(\@_) || ${sub_name}v6(\@_) }";
601             die $@ if $@;
602             }
603              
604             push @EXPORT, $sub_name;
605             }
606             ## use critic
607              
608             1;
609              
610             # ABSTRACT: IPv4 and IPv6 validation methods
611              
612             __END__