File Coverage

blib/lib/DNS/BL.pm
Criterion Covered Total %
statement 93 103 90.2
branch 36 46 78.2
condition 15 15 100.0
subroutine 24 26 92.3
pod 9 9 100.0
total 177 199 88.9


line stmt bran cond sub pod time code
1             package DNS::BL;
2              
3 5     5   6458 use 5.006001;
  5         17  
  5         238  
4 5     5   1114 use strict;
  5         10  
  5         178  
5 4     4   31 use warnings;
  4         8  
  4         149  
6              
7 4     4   21 use Carp;
  4         6  
  4         420  
8              
9             # These constans are used to specify specific error condition / result
10             # codes.
11              
12             =pod
13              
14             =head1 NAME
15              
16             DNS::BL - Manage DNS black lists
17              
18             =head1 SYNOPSIS
19              
20             use DNS::BL;
21              
22             =head1 DESCRIPTION
23              
24             This class provides the services required to manage DNSBL data using
25             this module hierarchy. It does so by implementing a series of methods,
26             that perform the required function and when called in array context,
27             return a two-element list, whose first element is a return code and
28             its second element, is a diagnostic message.
29              
30             In scalar context, only the constant is returned.
31              
32             The following constants are defined:
33              
34             =over
35              
36             =item B
37              
38             Denotes a succesful operation.
39              
40             =item B
41              
42             A problem related to the connection or lack of, to the backend.
43              
44             =item B
45              
46             When inserting entries in the backend, a previous entry conflicts with
47             this one.
48              
49             =item B
50              
51             When looking up entries in the backend, no suitable entry has been
52             found.
53              
54             =item B
55              
56             A syntax error was detected by a callback handler.
57              
58             =item B
59              
60             Some other kind of error.
61              
62             =back
63              
64             =cut
65              
66 4     4   22 use constant DNSBL_OK => 0;
  4         13  
  4         269  
67 4     4   20 use constant DNSBL_ECONNECT => 1;
  4         8  
  4         168  
68 4     4   19 use constant DNSBL_ECOLLISSION => 2;
  4         7  
  4         190  
69 4     4   19 use constant DNSBL_ENOTFOUND => 4;
  4         6  
  4         180  
70 4     4   19 use constant DNSBL_ESYNTAX => 8;
  4         6  
  4         154  
71 4     4   25 use constant DNSBL_EOTHER => 16;
  4         16  
  4         196  
72              
73 4     4   19 use constant ERR_MSG => "Must issue a 'connect' first";
  4         9  
  4         1692  
74              
75             our $VERSION = '0.03';
76             $VERSION = eval $VERSION; # see L
77              
78             # Preloaded methods go here.
79              
80             =pod
81              
82             The following methods are implemented by this module:
83              
84             =over
85              
86             =item C<-Enew()>
87              
88             This method creates a new C object. No parameters are
89             required.
90              
91             =cut
92              
93             sub new($)
94             {
95 4     4 1 1609 my $class = shift;
96 4         33 return bless
97             {
98             k => {}, # Storage
99             }, $class;
100             }
101              
102              
103             =pod
104              
105             =item C<-Eparse($command)>
106              
107             This method tokenizes each line given in C<$command>, loading and
108             calling the appropiate modules to satisfy the request. As shipped,
109             each command verb, usually the first word of a C<$command>, will
110             invoke a class from the C hierarchy, which handles
111             such commands. A summary of those is included in
112             L. Likely, you can provide your own commands by
113             subclassing C in your own classes.
114              
115             Note that this method supports comments, by prepending a pound
116             sign. Most Perl-ish way.
117              
118             When a command is invoked for the first time, the class is
119             Cd. For example, the "foo" command would involve loading the
120             C class.
121              
122             After this loading process, the class' C method is
123             invoked. This is documented in L.
124              
125             =cut
126              
127             sub parse($$)
128             {
129 758     758 1 1942536 my $self = shift;
130 758         1300 my $comm = shift;
131              
132 758         3021 $comm =~ s/^\s+//; # Remove leading whitespace
133 758         3107 $comm =~ s/\s+$//; # Remove trailing whitespace
134              
135 758         1337 my @tok = (); # List of tokens
136 758         1141 my $proto = undef; # A proto-token
137 758         1161 my $in_string = 0; # State: Are we within a quoted string?
138            
139             # Iterate through characters in a simple automaton
140              
141 758         6033 for my $c (split //, $comm)
142             {
143 18329 100 100     101105 if ($c eq '"')
    100 100        
    100 100        
144             {
145 2578 100 100     11431 push @tok, $proto if defined $proto || $in_string;
146 2578         3592 $proto = undef;
147 2578         3452 $in_string = ! $in_string;
148 2578         4354 next;
149             }
150             elsif ($c eq '#' and ! $in_string)
151             {
152 512         915 last;
153             }
154             elsif ($c =~ /\s/s and ! $in_string and defined $proto)
155             {
156 1219         2035 push @tok, $proto;
157 1219         1765 $proto = undef;
158             }
159             else
160             {
161 14020 100 100     41320 next if $c =~ /\s/s and ! $in_string;
162 13014         20135 $proto .= $c;
163             }
164             }
165              
166             # Flag trailing quoted strings
167 758 100       3599 if ($in_string)
168             {
169 80 50       426 return wantarray?(DNSBL_ESYNTAX,
170             "End of command within a quoted string")
171             :DNSBL_ESYNTAX
172             }
173              
174             # The ending token must be considered too
175 678 100       1753 push @tok, $proto if defined $proto;
176              
177             # Trivial case: An empty line...
178 678 100       1636 unless (@tok)
179             {
180 60 50       288 return wantarray?(DNSBL_OK, "-- An empty line, huh?")
181             : DNSBL_OK;
182             }
183              
184 618         1267 my $verb = shift @tok;
185              
186 618         785 do {
187 4     4   23 no strict 'refs';
  4         7  
  4         3028  
188 618 100       1099 unless (*{ __PACKAGE__ . "::cmds::${verb}::execute"}{CODE})
  618         4332  
189             {
190 4     2   319 eval "use " . __PACKAGE__ . "::cmds::${verb};";
  2         1692  
  1         2  
  1         19  
191 4 100       23 if ($@)
192             {
193 1 50       11 return wantarray?(DNSBL_ESYNTAX, "Verb $verb undefined: $@")
194             :DNSBL_ESYNTAX;
195             }
196             }
197              
198 617 50       838 if (*{ __PACKAGE__ . "::cmds::${verb}::execute"}{CODE})
  617         2621  
199             { # Handler exists
200 617         921 return &{ __PACKAGE__
  617         3167  
201             . "::cmds::${verb}::execute"}($self, $verb, @tok);
202             }
203             };
204            
205 0 0       0 return wantarray?(DNSBL_ESYNTAX, "Verb $verb is undefined")
206             :DNSBL_ESYNTAX;
207             }
208              
209             =pod
210              
211             =item C<-Eset($key, $value)>
212              
213             Set the value of a C<$key> which is stored in the object itself, to
214             the scalar C<$value>.
215              
216             =cut
217              
218 24     24 1 10680 sub set { my $ret = $_[0]->{k}->{$_[1]}; $_[0]->{k}->{$_[1]} = $_[2];
  24         62  
219 24         68 return $ret; }
220              
221             =pod
222              
223             =item C<-Eget($key)>
224              
225             Retrieve the scalar value associated to the given C<$key>.
226              
227             =cut
228              
229 5     5 1 12 sub get { return $_[0]->{k}->{$_[1]}; }
230              
231             =pod
232              
233             =back
234              
235             The following methods are really pointers meant to be replaced by the
236             L classes invoked at runtime. The specific
237             function of each function is discussed below (briefly) and in
238             L.
239              
240             The L classes must replace them by using
241             the the accessors to store the reference to the function (or clusure),
242             using the same name of the method, prepending an underscore.
243              
244             =over
245              
246             =item C<-Eread($entry)>
247              
248             Given an C<$entry>, retrieve all the L objects
249             contained in the IP address range denoted in its C<-Eaddr()>
250             method, stored in the Ced backend. Its return value, is a
251             list where the first element is the result code, the second is a
252             message suitable for diagnostics. The rest of the elements, if any,
253             are the matching entries found.
254              
255             C<$entry> should be a L object.
256              
257             =item C<-Ematch($entry)>
258              
259             Given an C<$entry>, retrieve all the L objects that
260             contain the IP address range denoted in its C<-Eaddr()> method,
261             stored in the Ced backend. Its return value, is a list where
262             the first element is the result code, the second is a message suitable
263             for diagnostics. The rest of the elements, if any, are the matching
264             entries found.
265              
266             C<$entry> should be a L object.
267              
268             =item C<-Ewrite($entry)>
269              
270             Store the given L object in the connected backend.
271              
272             =item C<-Eerase($entry)>
273              
274             Delete all the C from the connected backend, whose
275             C<-Eaddr()> network range falls entirely within the one given in
276             C<$entry>.
277              
278             =item C<-Ecommit()>
279              
280             Commit all the changes to the backend. In some backends this is a
281             no-op, but it should be invoked at the end of each command block.
282              
283             =back
284              
285             =cut
286              
287 38 100   38 1 1755 sub read { &{$_[0]->{k}->{_read} || *{_io}{CODE}}(@_); }
  38         314  
288 8 100   8 1 6976 sub match { &{$_[0]->{k}->{_match} || *{_io}{CODE}}(@_); }
  8         64  
289 8 100   8 1 5594 sub write { &{$_[0]->{k}->{_write} || *{_io}{CODE}}(@_); }
  8         83  
290 6 100   6 1 5497 sub erase { &{$_[0]->{k}->{_erase} || *{_io}{CODE}}(@_); }
  6         40  
291 6 100   6 1 8744 sub commit { &{$_[0]->{k}->{_commit} || *{_io}{CODE}}(@_); }
  6         130  
292 56 100   56   391 sub _io { wantarray?(&DNSBL_ECONNECT, &ERR_MSG):&DNSBL_ECONNECT }
293              
294 0     0   0 sub DNS::BL::cmds::commit::execute { $_[0]->commit(@_); }
295              
296             sub DNS::BL::cmds::_dump::execute
297             {
298 4     4   4736 use Data::Dumper;
  4         28802  
  4         836  
299 0     0   0 my $self = shift;
300              
301 0         0 print "*** Current object $self:\n";
302 0         0 print Data::Dumper->Dump([$self]);
303              
304 0 0       0 if (@_)
305             {
306 0         0 print "*** Arguments:\n";
307 0         0 print " '$_'\n" for @_;
308             }
309             else
310             {
311 0         0 print "*** No arguments\n";
312             }
313 0 0       0 return wantarray ? (DNSBL_OK, "Debug dump done") : DNSBL_OK;
314             }
315              
316             1;
317             __END__