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   4802 use strict;
  2         14  
  2         56  
4 2     2   10 use warnings;
  2         4  
  2         46  
5              
6 2     2   48 use 5.008;
  2         7  
7              
8             our $VERSION = '0.30';
9              
10 2     2   1297 use NetAddr::IP 4;
  2         62796  
  2         12  
11 2     2   273 use Scalar::Util qw( blessed );
  2         4  
  2         190  
12              
13 2     2   14 use base 'Exporter';
  2         4  
  2         694  
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   9 local $@ = undef;
28             $HAS_SOCKET = (!$ENV{DVI_NO_SOCKET})
29 2   66     10 && 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       17 if ($HAS_SOCKET) {
45 1         3 *is_ipv4 = \&_fast_is_ipv4;
46 1         11 *is_ipv6 = \&_fast_is_ipv6;
47 1         3 *is_ip = \&_fast_is_ip;
48 1         2475 *_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         2485 *_build_is_X_ip_subs = \&_build_slow_is_X_ip_subs;
55             }
56             }
57              
58             sub new {
59 2     2 0 136691 my $class = shift;
60              
61 2         13 return bless {}, $class;
62             }
63              
64             sub _fast_is_ip {
65 14 100   14   3429 shift if ref $_[0];
66 14         26 my $value = shift;
67              
68 14 50       33 return undef unless defined $value;
69 14 100       64 return $value =~ /:/ ? _fast_is_ipv6($value) : _fast_is_ipv4($value);
70             }
71              
72             sub _fast_is_ipv4 {
73 46 100   46   5602 shift if ref $_[0];
74 46         80 my $value = shift;
75              
76 46 100       95 return undef unless _fast_is_ipv4_packed($value);
77              
78             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
79 23         99 $value =~ /(.+)/;
80 23         99 return $1;
81             }
82              
83             sub _fast_is_ipv4_packed {
84 796     796   1186 my $value = shift;
85              
86 796 50       1625 return undef unless defined $value;
87 796 100       2102 return undef if $value =~ /\0/;
88 778         15313 return inet_pton(Socket::AF_INET(), $value);
89             }
90              
91             sub _slow_is_ip {
92 14 100   14   3289 shift if ref $_[0];
93 14         29 my $value = shift;
94              
95 14   100     31 return _slow_is_ipv4($value) || _slow_is_ipv6($value);
96             }
97              
98             sub _slow_is_ipv4 {
99 806 100   806   7159 shift if ref $_[0];
100 806         1231 my $value = shift;
101              
102 806 50       1569 return undef unless defined($value);
103              
104 806         3336 my (@octets) = $value =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
105 806 100       7008 return undef unless (@octets == 4);
106 487         1006 foreach (@octets) {
107 1804 100 66     5932 return undef if $_ < 0 || $_ > 255;
108 1786 100       3996 return undef if $_ =~ /^0\d{1,2}$/;
109             }
110              
111 433         8510 return join('.', @octets);
112             }
113              
114             sub _fast_is_ipv6 {
115 66 100   66   15354 shift if ref $_[0];
116 66         111 my $value = shift;
117              
118 66 100       139 return undef unless _fast_is_ipv6_packed($value);
119              
120             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
121 36         128 $value =~ /(.+)/;
122 36         181 return $1;
123             }
124              
125             sub _fast_is_ipv6_packed {
126 1392     1392   2242 my $value = shift;
127              
128 1392 50       2971 return undef unless defined $value;
129 1392 100       3488 return undef if $value =~ /\0/;
130 1366 100       3889 return undef if $value =~ /0[[:xdigit:]]{4}/;
131 1342         26540 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   17355 shift if ref $_[0];
142 1394         2237 my $value = shift;
143              
144 1394 50       2896 return undef unless defined($value);
145              
146 1394 100       2746 return '::' if $value eq '::';
147 1392 100       20672 return undef unless $value =~ /^$ipv6_re$/;
148              
149             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
150 1024         2512 $value =~ /(.+)/;
151 1024         20366 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 15704 shift if ref $_[0];
163 28         55 my $value = shift;
164 28         42 my $network = shift;
165              
166 28 50       70 return undef unless defined($value);
167              
168 28         85 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     472 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         35 my $orig = $network;
181 16 100       324 if ($network =~ /^($ip_re)[:\-]($ip_re)$/) {
    100          
    100          
    50          
182 4         18 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         14 my ($net, $hostmask) = ($1, $2);
191              
192 2 50       13 my $bits = _hostmask_to_bits($hostmask)
193             or return undef;
194              
195 2         8 $network = "$net/$bits";
196             }
197             elsif ($network =~ m{^($partial_ip_re)/(\d\d?)$}) {
198 4         17 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         9 $network = $net;
207 4         16 $network .= '.0' x (4 - $octets);
208 4         14 $network .= "/$bits";
209             }
210             elsif ($network =~ /^$partial_ip_re$/) {
211              
212             ## no critic(Variables::ProhibitUnusedVarsStricter)
213 6         25 my $octets = scalar(my @tmp = split /\./, $network);
214 6 50       15 if ($octets < 4) {
215 6         20 $network .= '.0' x (4 - $octets);
216 6         16 $network .= '/' . $octets * 8;
217             }
218             }
219              
220 16 50       41 if ($orig ne $network) {
221 16         35 _deprecation_warn(
222             'Use of non-CIDR notation for networks with is_innet_ipv4() is deprecated'
223             );
224             }
225              
226 16 50       82 $network = NetAddr::IP->new($network) or return undef;
227             }
228              
229 26 50       10781 my $netaddr_ip = NetAddr::IP->new($ip) or return undef;
230              
231 26 100       9394 return $ip if $network->contains($netaddr_ip);
232 2         49 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   19 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   13 return $hostmasks{ $_[0] };
316             }
317             }
318              
319             {
320             my %warned_at;
321              
322             sub _deprecation_warn {
323 16     16   27 my $warning = shift;
324 16         112 my @caller = caller(2);
325              
326 16         60 my $caller_info
327             = "at line $caller[2] of $caller[0] in sub $caller[3]";
328              
329 16 100       85 return undef if $warned_at{$warning}{$caller_info}++;
330              
331 2         31 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   412545 my $network = shift;
401 1232         2079 my $other = shift;
402              
403 1232   100     6395 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         3 my $ip_number = shift;
411              
412 2 100       5 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         4 my @all_nets;
416              
417 2         3 local $@ = undef;
418 2         2 for my $type (keys %{$networks}) {
  2         14  
419             my @nets
420 25         1003 = map { NetAddr::IP->$netaddr_new($_) }
421             ref $networks->{$type}{networks}
422 3         13 ? @{ $networks->{$type}{networks} }
423 17 100       59 : $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       2546 unless ($networks->{$type}{subnet_of}) {
429 15         35 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   2696 my $sub = eval sprintf( <<'EOF', $is_ip_sub, $netaddr_new);
  150 50       6910  
  150 100       255  
  150 100       300  
  150 100       426  
  150 50       563  
  110 100       363  
  110 100       28306  
  110 100       340  
  94 50       4126  
  98 100       1601  
  98 100       183  
  98 100       232  
  98 50       285  
  98 100       366  
  74 100       256  
  74 100       19104  
  74 50       250  
  68 100       2794  
  150 100       5783  
  150 100       275  
  150 50       319  
  150 100       402  
  150 100       581  
  110 100       409  
  110 50       28455  
  110 100       340  
  106 100       4347  
  98 100       6341  
  98 50       171  
  98 100       200  
  98 100       275  
  98 100       359  
  74 50       256  
  74 100       18933  
  74 100       229  
  68 100       2736  
  114 50       5526  
  114 100       201  
  114 100       267  
  114 100       285  
  114 50       1546  
  58 100       208  
  58 100       7928  
  162 100       3805  
  46 50       1982  
  98 100       6283  
  98 100       182  
  98 100       212  
  98 50       278  
  98 100       338  
  74 100       252  
  74 100       19237  
  74 50       235  
  68 100       2811  
  98 100       7009  
  98 100       174  
  98 50       215  
  98 100       299  
  98 100       391  
  74 100       249  
  74 50       19449  
  74 100       235  
  66 100       2693  
  60 100       5304  
  60 50       104  
  60 100       118  
  60 100       154  
  60         205  
  40         145  
  40         5738  
  114         2672  
  34         1193  
  98         6382  
  98         170  
  98         200  
  98         265  
  98         337  
  74         255  
  74         19015  
  74         230  
  56         2215  
  94         6676  
  94         178  
  94         199  
  94         272  
  94         328  
  70         253  
  70         18071  
  70         216  
  64         2595  
  60         625  
  60         100  
  60         127  
  60         154  
  60         235  
  40         156  
  40         5557  
  40         142  
  38         1520  
  114         5029  
  114         196  
  114         262  
  114         310  
  114         1528  
  58         203  
  58         7851  
  58         192  
  54         2635  
  60         6953  
  60         111  
  60         124  
  60         146  
  60         243  
  40         138  
  40         5398  
  180         4660  
  26         896  
  150         6459  
  150         329  
  150         323  
  150         425  
  150         562  
  110         369  
  110         28432  
  110         359  
  98         4081  
  146         6838  
  146         256  
  146         306  
  146         402  
  146         541  
  110         352  
  110         28239  
  110         355  
  94         3926  
  114         4671  
  114         205  
  114         237  
  114         309  
  114         1577  
  58         209  
  58         8025  
  58         184  
  54         2697  
  114         4894  
  114         216  
  114         248  
  114         292  
  114         1595  
  58         218  
  58         7809  
  58         192  
  54         2742  
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       57 die $@ if $@;
453              
454 17         48 my $sub_name = 'is_' . $type . '_ipv' . $ip_number;
455             {
456 2     2   18 no strict 'refs';
  2         3  
  2         212  
  17         28  
457 17         21 *{$sub_name} = $sub;
  17         83  
458             }
459 17         57 push @EXPORT, $sub_name;
460             }
461              
462 2 100   146   285 my $sub = eval sprintf( <<'EOF', $is_ip_sub, $netaddr_new);
  146 50       8496  
  146 100       255  
  146 100       332  
  146 100       384  
  146 50       534  
  110 100       389  
  110 100       28212  
  544         14474  
  24         866  
  114         5456  
  114         227  
  114         234  
  114         292  
  114         1552  
  58         211  
  58         7811  
  582         16778  
  12         528  
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   16 no strict 'refs';
  2         4  
  2         503  
  2         3  
485 2         2 *{$sub_name} = $sub;
  2         10  
486             }
487 2         9 push @EXPORT, $sub_name;
488             }
489              
490             sub _build_fast_is_X_ip_subs {
491 2     2   4 my $networks = shift;
492 2         3 my $ip_number = shift;
493              
494 2 100       5 my $family = $ip_number == 4 ? Socket::AF_INET() : Socket::AF_INET6();
495              
496 2         4 my @all_nets;
497              
498 2         7 local $@ = undef;
499 2         4 for my $type (keys %{$networks}) {
  2         11  
500             my @nets
501 25         45 = map { _packed_network_and_netmask($family, $_) }
502             ref $networks->{$type}{networks}
503 3         8 ? @{ $networks->{$type}{networks} }
504 17 100       59 : $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       48 unless ($networks->{$type}{subnet_of}) {
510 15         27 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   114   2821 my $sub = eval sprintf( <<'EOF', $ip_number);
  114 100       5545  
  114 100       219  
  114 100       271  
  114 100       1555  
  58 100       146  
  162 100       432  
  12 100       34  
  12 100       219  
  46 100       563  
  146 100       6896  
  146 100       255  
  146 100       386  
  146 100       522  
  110 100       219  
  110 100       391  
  16 100       43  
  16 100       110  
  94 100       449  
  60 100       621  
  60 100       104  
  60 100       157  
  60 100       218  
  40 100       96  
  40 100       139  
  2 100       7  
  2 100       19  
  38 100       160  
  98 100       6291  
  98 100       174  
  98 100       244  
  98 100       340  
  74 100       172  
  74 100       274  
  6 100       21  
  6 100       37  
  68 100       300  
  98 100       1621  
  98 100       206  
  98 100       241  
  98 100       350  
  74 100       154  
  74 100       295  
  6 100       16  
  6 100       39  
  68 100       293  
  60 100       6902  
  60 100       107  
  60 100       182  
  60 100       213  
  40 100       90  
  180         408  
  14         34  
  14         79  
  26         101  
  114         4809  
  114         202  
  114         277  
  114         1572  
  58         133  
  58         204  
  4         19  
  4         58  
  54         735  
  114         4756  
  114         204  
  114         323  
  114         1599  
  58         127  
  58         222  
  4         15  
  4         60  
  54         723  
  150         7131  
  150         264  
  150         355  
  150         540  
  110         239  
  110         402  
  16         52  
  16         114  
  94         463  
  98         6446  
  98         173  
  98         257  
  98         358  
  74         198  
  74         285  
  6         15  
  6         37  
  68         291  
  98         7092  
  98         163  
  98         256  
  98         362  
  74         161  
  74         261  
  8         59  
  8         54  
  66         288  
  98         6347  
  98         179  
  98         249  
  98         336  
  74         158  
  74         268  
  18         92  
  18         115  
  56         251  
  94         6759  
  94         166  
  94         234  
  94         348  
  70         159  
  70         256  
  6         17  
  6         35  
  64         284  
  150         5986  
  150         278  
  150         365  
  150         566  
  110         240  
  110         405  
  4         15  
  4         29  
  106         498  
  150         6495  
  150         282  
  150         364  
  150         534  
  110         258  
  110         432  
  12         36  
  12         80  
  98         478  
  114         4847  
  114         194  
  114         286  
  114         1604  
  58         130  
  58         209  
  4         14  
  4         72  
  54         733  
  60         5410  
  60         142  
  60         157  
  60         253  
  40         86  
  114         293  
  6         16  
  6         35  
  34         146  
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   15 no strict 'refs';
  2         3  
  2         234  
  17         21  
539 17         27 *{$sub_name} = $sub;
  17         79  
540             }
541 17         60 push @EXPORT, $sub_name;
542             }
543              
544 2 100   146   308 my $sub = eval sprintf( <<'EOF', $ip_number);
  146 100       7890  
  146 100       263  
  146 100       359  
  146 100       574  
  110 100       259  
  622         1793  
  24         69  
  24         149  
  114         5486  
  114         248  
  114         279  
  114         1546  
  58         137  
  512         1671  
  12         35  
  12         168  
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       8 die $@ if $@;
562              
563 2         5 my $sub_name = 'is_public_ipv' . $ip_number;
564             {
565 2     2   14 no strict 'refs';
  2         3  
  2         494  
  2         3  
566 2         3 *{$sub_name} = $sub;
  2         9  
567             }
568 2         9 push @EXPORT, $sub_name;
569             }
570              
571             sub _packed_network_and_netmask {
572 25     25   39 my $family = shift;
573 25         35 my $network = shift;
574              
575 25         121 my ($ip, $bits) = split qr{/}, $network, 2;
576              
577             return [
578 25         100 inet_pton($family, $ip),
579             _packed_netmask($family, $bits)
580             ];
581             }
582              
583             sub _packed_netmask {
584 25     25   39 my $family = shift;
585 25         36 my $bits = shift;
586              
587 25 100       48 my $bit_length = $family == Socket::AF_INET() ? 32 : 128;
588              
589 25         148 my $bit_string
590             = join(q{}, (1) x $bits, (0) x ($bit_length - $bits));
591 25         146 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   16 no strict 'refs';
  2         4  
  2         222  
599 108 100   108   5086 *{$sub_name}
  108 100       6558  
  108 100       7294  
  108 100       8742  
  108 100       5837  
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__