File Coverage

blib/lib/JCM/Net/Patricia.pm
Criterion Covered Total %
statement 174 229 75.9
branch 64 174 36.7
condition 13 65 20.0
subroutine 37 47 78.7
pod 7 9 77.7
total 295 524 56.3


line stmt bran cond sub pod time code
1             # JCM::Net::Patricia - Patricia Trie perl module for fast IP address lookups
2             # Copyright (C) 2000-2005 Dave Plonka
3             # Copyright (C) 2009 Dave Plonka & Philip Prindeville
4             # Parts Copyright(C) 2016 Joelle Maslak
5             # Parts Copyright(C) 2021 Joelle Maslak
6             #
7             # This program 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             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with this program; if not, write to the Free Software
19             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20             # MA 02110-1301, USA.
21              
22             # Joelle Maslak
23             # Dave Plonka
24             # Philip Prindeville
25             # Anton Berezin
26              
27             package JCM::Net::Patricia;
28              
29 3     3   295929 use strict;
  3         19  
  3         97  
30 3     3   16 use warnings;
  3         6  
  3         115  
31              
32             require 5.008;
33              
34 3     3   1503 use version;
  3         5976  
  3         17  
35 3     3   366 use Carp;
  3         6  
  3         296  
36 3     3   20 use vars qw($VERSION @ISA @EXPORT);
  3         5  
  3         154  
37 3     3   1815 use Socket qw(AF_INET AF_INET6);
  3         10746  
  3         583  
38              
39             BEGIN {
40 3     3   25 require Exporter;
41 3         12 require DynaLoader;
42 3         58 @ISA = qw(Exporter DynaLoader);
43 3         1904 @EXPORT = qw(AF_INET AF_INET6);
44             }
45              
46             '$Revision: 1.05 $' =~ m/(\d+)\.(\d+)((_\d+)|)/ && ( $VERSION = "$1.$2$3");
47              
48             bootstrap JCM::Net::Patricia $VERSION;
49              
50             sub new {
51 7     7 1 6706 my ($class, $type) = @_;
52              
53 7   100     38 $type ||= AF_INET;
54              
55 7 100       21 if ($type == AF_INET) {
56 4         49 return bless _new(32), 'JCM::Net::Patricia::AF_INET';
57             }
58              
59 3 50       13 if ($type == AF_INET6) {
60 3 50       18 if (JCM::Net::Patricia::have_ipv6()) {
61 3         27 return bless _new(128), 'JCM::Net::Patricia::AF_INET6';
62             } else {
63 0         0 croak('IPv6 is not available on this platform');
64             }
65             }
66              
67 0         0 croak "new: unimplemented type";
68             }
69              
70             ##
71             ## Compat functions
72             ##
73              
74             sub _ip_bits {
75 69     69   125 my ($self, $str) = @_;
76 69         92 my $bits;
77              
78 69 100       166 if (ref ($self) eq 'JCM::Net::Patricia::AF_INET6') {
79 15 100       96 $bits = ($str =~ s|/(\d+)$||) ? $1 : 128;
80             } else {
81 54 100       287 $bits = ($str =~ s|/(\d+)$||) ? $1 : 32;
82             }
83 69         247 ($str,$bits);
84             }
85              
86             sub add_string {
87 38 50 33 38 1 6509 croak "add_string: wrong number of args" if (@_ < 2 || @_ > 3);
88 38         82 my ($self,$str,$data) = @_;
89 38 100       80 $data = $str unless @_ > 2;
90 38         94 $self->add($self->_ip_bits($str),$data);
91             }
92              
93             sub match_string {
94 17 50   17 1 4204 croak "match_string: wrong number of args" if (@_ != 2);
95 17         40 my ($self,$str) = @_;
96 17         47 $self->match($self->_ip_bits($str))
97             }
98              
99             sub matching_prefix_string {
100 8 50   8 1 2960 croak "matching_prefix_string: wrong number of args" if (@_ != 2);
101 8         18 my ($self,$str) = @_;
102 8         20 $self->matching_prefix($self->_ip_bits($str))
103             }
104              
105             sub match_exact_string {
106 2 50   2 1 8 croak "match_exact_string: wrong number of args" if (@_ != 2);
107 2         7 my ($self,$str) = @_;
108 2         7 $self->exact($self->_ip_bits($str))
109             }
110              
111             sub match_exact_integer {
112 6     6 1 3103 shift->exact_integer(@_)
113             }
114              
115             sub remove_string {
116 4 50   4 1 550 croak "remove_string: wrong number of args" if (@_ != 2);
117 4         11 my ($self,$str) = @_;
118 4         12 $self->remove($self->_ip_bits($str))
119             }
120              
121 0         0 BEGIN {
122 3     3   13 eval {
123 3         6 my $class = 'Net::CIDR::Lite';
124 3         3835 eval "require $class";
125             };
126 3 50       12990 last if (@_);
127              
128             sub add_cidr {
129 1 50   1 0 306 croak "add_cidr: wrong number of args" if (@_ != 3);
130 1         9 my ($self, $range, $data) = @_;
131 1         9 my $cidr = Net::CIDR::Lite->new();
132 1         17 $cidr->add_range($range);
133              
134 1         371 my @list = ();
135 1         7 for ($cidr->list()) {
136 2 50       158 push(@list, $_) if ($self->add_string($_, $data));
137             }
138 1         11 @list;
139             }
140              
141             sub remove_cidr {
142 1 50   1 0 539 croak "remove_cidr: wrong number of args" if (@_ != 2);
143 1         5 my ($self, $range) = @_;
144 1         4 my $cidr = Net::CIDR::Lite->new();
145 1         14 $cidr->add_range($range);
146              
147 1         181 my @list = ();
148 1         5 for ($cidr->list()) {
149 2 50       130 push(@list, $_) if ($self->remove_string($_));
150             }
151 1         8 @list;
152             }
153             }
154              
155             ##
156             ## AF_INET
157             ##
158              
159             package JCM::Net::Patricia::AF_INET;
160              
161 3     3   27 use Carp;
  3         8  
  3         164  
162 3     3   18 use Socket qw(AF_INET inet_aton inet_ntoa);
  3         7  
  3         135  
163 3     3   18 use vars qw(@ISA @EXPORT);
  3         5  
  3         209  
164              
165             BEGIN {
166 3     3   18 require Exporter;
167 3         10 require DynaLoader;
168 3         67 @ISA = qw(Exporter DynaLoader JCM::Net::Patricia);
169 3         3994 @EXPORT = qw(AF_INET);
170             }
171              
172             sub add {
173 29 50 33 29   144 croak "add: wrong number of args" if (@_ < 2 || @_ > 4);
174 29         64 my ($self, $ip, $bits, $data) = @_;
175 29 100       374 croak("invalid key") if ( scalar(split( /[\/.]/, $ip )) != 4);
176 28 50       94 croak("invalid key") if ( grep { /^0[0-9]/ } split( /[\/.]/, $ip ) );
  112         224  
177 28 0       79 $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 4);
    50          
178 28         91 my $packed = inet_aton($ip);
179 28 50       75 croak("invalid key") unless (defined $packed);
180 28 50       49 $bits = 32 if (@_ < 3);
181 28         177 $self->SUPER::_add(AF_INET, $packed, $bits, $data);
182             }
183              
184             sub add_integer {
185 0 0 0 0   0 croak "add_integer: wrong number of args" if (@_ < 2 || @_ > 4);
186 0         0 my ($self, $num, $bits, $data) = @_;
187 0         0 my $packed = pack("N", $num);
188 0         0 my $ip = inet_ntoa($packed);
189 0 0       0 croak("invalid address") unless (defined $ip);
190 0 0       0 $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 4);
    0          
191 0 0       0 $bits = 32 if (@_ < 3);
192 0         0 $self->SUPER::_add(AF_INET, $packed, $bits, $data);
193             }
194              
195             sub match_integer {
196 6 50 33 6   1129 croak "match_integer: wrong number of args" if (@_ < 2 || @_ > 3);
197 6         13 my ($self, $num, $bits) = @_;
198 6 50       16 $bits = 32 if (@_ < 3);
199 6         66 $self->SUPER::_match(AF_INET, pack("N",$num), $bits);
200             }
201              
202             sub matching_prefix_integer {
203 0 0 0 0   0 croak "matching_integer: wrong number of args" if (@_ < 2 || @_ > 3);
204 0         0 my ($self, $num, $bits) = @_;
205 0 0       0 $bits = 32 if (@_ < 3);
206 0         0 $self->SUPER::_matching_prefix(AF_INET, pack("N",$num), $bits);
207             }
208              
209             sub exact_integer {
210 6 50 33 6   34 croak "exact_integer: wrong number of args" if (@_ < 2 || @_ > 3);
211 6         14 my ($self, $num, $bits) = @_;
212 6 100       14 $bits = 32 if (@_ < 3);
213 6         66 $self->SUPER::_exact(AF_INET, pack("N",$num), $bits);
214             }
215              
216             sub match {
217 15 50 33 15   73 croak "match: wrong number of args" if (@_ < 2 || @_ > 3);
218 15         30 my ($self, $ip, $bits) = @_;
219 15 50       73 croak("invalid key") if ( scalar(split( /[\/.]/, $ip )) != 4);
220 15 50       53 croak("invalid key") if ( grep { /^0[0-9]/ } split( /[\/.]/, $ip ) );
  60         131  
221 15         60 my $packed = inet_aton($ip);
222 15 50       36 croak("invalid key") unless (defined $packed);
223 15 50       33 $bits = 32 if (@_ < 3);
224 15         115 $self->SUPER::_match(AF_INET, $packed, $bits);
225             }
226              
227             sub matching_prefix {
228 4 50 33 4   18 croak "matching_prefix: wrong number of args" if (@_ < 2 || @_ > 3);
229 4         9 my ($self, $ip, $bits) = @_;
230 4 50       19 croak("invalid key") if ( scalar(split( /[\/.]/, $ip )) != 4);
231 4 50       14 croak("invalid key") if ( grep { /^0[0-9]/ } split( /[\/.]/, $ip ) );
  16         34  
232 4         17 my $packed = inet_aton($ip);
233 4 50       9 croak("invalid key") unless (defined $packed);
234 4 50       9 $bits = 32 if (@_ < 3);
235 4         35 $self->SUPER::_matching_prefix(AF_INET, $packed, $bits);
236             }
237              
238             sub exact {
239 2 50 33 2   13 croak "exact: wrong number of args" if (@_ < 2 || @_ > 3);
240 2         7 my ($self, $ip, $bits) = @_;
241 2 50       9 croak("invalid key") if ( scalar(split( /[\/.]/, $ip )) != 4);
242 2 50       9 croak("invalid key") if ( grep { /^0[0-9]/ } split( /[\/.]/, $ip ) );
  8         20  
243 2         9 my $packed = inet_aton($ip);
244 2 50       8 croak("invalid key") unless (defined $packed);
245 2 50       4 $bits = 32 if (@_ < 3);
246 2         18 $self->SUPER::_exact(AF_INET, $packed, $bits);
247             }
248              
249             sub remove {
250 4 50 33 4   26 croak "remove: wrong number of args" if (@_ < 2 || @_ > 3);
251 4         9 my ($self, $ip, $bits) = @_;
252 4 50       27 croak("invalid key") if ( scalar(split( /[\/.]/, $ip )) != 4);
253 4 50       16 croak("invalid key") if ( grep { /^0[0-9]/ } split( /[\/.]/, $ip ) );
  16         38  
254 4         18 my $packed = inet_aton($ip);
255 4 50       17 croak("invalid key") unless (defined $packed);
256 4 50       10 $bits = 32 if (@_ < 3);
257 4         37 $self->SUPER::_remove(AF_INET, $packed, $bits);
258             }
259              
260             sub remove_integer {
261 0 0 0 0   0 croak "remote_integer: wrong number of args" if (@_ < 2 || @_ > 3);
262 0         0 my ($self, $num, $bits) = @_;
263 0 0       0 $bits = 32 if (@_ < 3);
264 0         0 $self->SUPER::_remove(AF_INET, pack("N",$num), $bits);
265             }
266              
267             ##
268             ## AF_INET6
269             ##
270              
271             package JCM::Net::Patricia::AF_INET6;
272              
273 3     3   24 use Carp;
  3         6  
  3         199  
274 3     3   21 use Socket qw(AF_INET6);
  3         5  
  3         147  
275 3     3   1578 use Socket6 qw(inet_pton inet_ntop);
  3         3418  
  3         316  
276 3     3   22 use vars qw(@ISA @EXPORT);
  3         6  
  3         249  
277              
278             BEGIN {
279 3     3   22 require Exporter;
280 3         10 require DynaLoader;
281 3         61 @ISA = qw(Exporter DynaLoader JCM::Net::Patricia);
282 3         2872 @EXPORT = qw(AF_INET6);
283             }
284              
285             sub add {
286 9 50 33 9   43 croak "add: wrong number of args" if (@_ < 2 || @_ > 4);
287 9         38 my ($self, $ip, $bits, $data) = @_;
288 9 0       34 $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 3);
    50          
289 9         62 my $packed = inet_pton(AF_INET6, $ip);
290 9 50       25 croak("invalid key") unless (defined $packed);
291 9 50       18 $bits = 128 if (@_ < 4);
292 9         75 $self->SUPER::_add(AF_INET6, $packed, $bits, $data);
293             }
294              
295             sub add_integer {
296 0 0 0 0   0 croak "add_integer: wrong number of args" if (@_ < 2 || @_ > 4);
297 0         0 my ($self, $num, $bits, $data) = @_;
298 0         0 my $packed = pack("N", $num);
299 0         0 my $ip = inet_ntop(AF_INET6, $packed);
300 0 0       0 croak("invalid address") unless (defined $ip);
301 0 0       0 $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 3);
    0          
302 0 0       0 $bits = 128 if (@_ < 4);
303 0         0 $self->SUPER::_add(AF_INET6, $packed, $bits, $data);
304             }
305              
306             sub match_integer {
307 0 0 0 0   0 croak "match_integer: wrong number of args" if (@_ < 2 || @_ > 3);
308 0         0 my ($self, $num, $bits) = @_;
309 0 0       0 $bits = 128 if (@_ < 3);
310 0         0 $self->SUPER::_match(AF_INET6, pack("N",$num), $bits);
311             }
312              
313             sub matching_prefix_integer {
314 0 0 0 0   0 croak "matching_prefix_integer: wrong number of args" if (@_ < 2 || @_ > 3);
315 0         0 my ($self, $num, $bits) = @_;
316 0 0       0 $bits = 128 if (@_ < 3);
317 0         0 $self->SUPER::_matching_prefix(AF_INET6, pack("N",$num), $bits);
318             }
319              
320             sub exact_integer {
321 0 0 0 0   0 croak "exact_integer: wrong number of args" if (@_ < 2 || @_ > 3);
322 0         0 my ($self, $num, $bits) = @_;
323 0 0       0 $bits = 128 if (@_ < 3);
324 0         0 $self->SUPER::_exact(AF_INET6, pack("N",$num), $bits);
325             }
326              
327             sub match {
328 2 50 33 2   13 croak "match: wrong number of args" if (@_ < 2 || @_ > 3);
329 2         19 my ($self, $ip, $bits) = @_;
330 2         8 my $packed = inet_pton(AF_INET6, $ip);
331 2 50       6 croak("invalid key") unless (defined $packed);
332 2 50       6 $bits = 128 if (@_ < 3);
333 2         21 $self->SUPER::_match(AF_INET6, $packed, $bits);
334             }
335              
336             sub matching_prefix {
337 4 50 33 4   17 croak "matching_prefix: wrong number of args" if (@_ < 2 || @_ > 3);
338 4         9 my ($self, $ip, $bits) = @_;
339 4         14 my $packed = inet_pton(AF_INET6, $ip);
340 4 50       10 croak("invalid key") unless (defined $packed);
341 4 50       8 $bits = 128 if (@_ < 3);
342 4         59 $self->SUPER::_matching_prefix(AF_INET6, $packed, $bits);
343             }
344              
345             sub exact {
346 0 0 0 0     croak "exact: wrong number of args" if (@_ < 2 || @_ > 3);
347 0           my ($self, $ip, $bits) = @_;
348 0           my $packed = inet_pton(AF_INET6, $ip);
349 0 0         croak("invalid key") unless (defined $packed);
350 0 0         $bits = 128 if (@_ < 3);
351 0           $self->SUPER::_exact(AF_INET6, $packed, $bits);
352             }
353              
354             sub remove {
355 0 0 0 0     croak "remove: wrong number of args" if (@_ < 2 || @_ > 3);
356 0           my ($self, $ip, $bits) = @_;
357 0           my $packed = inet_pton(AF_INET6, $ip);
358 0 0         croak("invalid key") unless (defined $packed);
359 0 0         $bits = 128 if (@_ < 3);
360 0           $self->SUPER::_remove(AF_INET6, $packed, $bits);
361             }
362              
363             sub remove_integer {
364 0 0 0 0     croak "remote_integer: wrong number of args" if (@_ < 2 || @_ > 3);
365 0           my ($self, $num, $bits) = @_;
366 0 0         $bits = 128 if (@_ < 3);
367 0           $self->SUPER::_remove(AF_INET6, pack("N",$num), $bits);
368             }
369              
370             1;
371             __END__