File Coverage

blib/lib/DNS/BL/cmds/connect/dbi.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package DNS::BL::cmds::connect::dbi;
2              
3 1     1   2552 use DNS::BL;
  1         4  
  1         30  
4              
5 1     1   33 use 5.006001;
  1         4  
  1         46  
6 1     1   7 use strict;
  1         2  
  1         17885  
7 1     1   29 use warnings;
  1         4  
  1         128  
8 1     1   7 use Fcntl qw(:DEFAULT);
  1         2  
  1         683  
9              
10 1     1   535 use DBI;
  0            
  0            
11              
12             use vars qw/@ISA/;
13              
14             @ISA = qw/DNS::BL::cmds/;
15              
16             use Carp;
17              
18             our $VERSION = '0.00_01';
19             $VERSION = eval $VERSION; # see L
20              
21             # Preloaded methods go here.
22              
23             =pod
24              
25             =head1 NAME
26              
27             DNS::BL::cmds::connect::dbi - Implement the DB connect command with DBI for DNS::BL
28              
29             =head1 SYNOPSIS
30              
31             use DNS::BL::cmds::connect::dbi;
32              
33             =head1 DESCRIPTION
34              
35             This module implements the connection to a DB backend where C
36             data will be stored. This backend is implemented through L.
37              
38             The following methods are implemented by this module:
39              
40             =over
41              
42             =item C<-Eexecute()>
43              
44             See L for information on this method's purpose.
45              
46             The connect command follows a syntax such as
47              
48             connect dbi ...
49              
50             Note that the 'connect' token must be removed by the calling class,
51             usually C. B are key - value pairs
52             specifying different parameters as described below. Unknown parameters
53             are reported as errors. The complete calling sequence is as
54              
55             connect dbi [user username] [password pwd] dsn dsn-string bl list
56              
57             Where each of the arguments mean the following:
58              
59             =over
60              
61             =item B
62              
63             The string that should be passed to DBI as the backend identifier.
64              
65             =item B
66              
67             The username for connecting to the server. If left unspecified,
68             defaults to "dnsbl-ro".
69              
70             =item B
71              
72             The password for connecting as the given user. Defaults to a blank
73             password.
74              
75             =item B
76              
77             The name of the list on which you want to operate. This is a local
78             convention and every site has its own set of lists.
79              
80             =back
81              
82             This class will be Cd and then, its C method invoked
83             following the same protocol outlined in L. Prior C
84             information is to be removed by the calling class.
85              
86             =cut
87              
88             sub execute
89             {
90             my $bl = shift;
91             my $command = shift; # Expect "dbi"
92             my %args = @_;
93              
94             my @known = qw/dsn user password bl/;
95              
96             unless ($command eq 'dbi')
97             {
98             return wantarray ?
99             (&DNS::BL::DNSBL_ESYNTAX(),
100             "'" . __PACKAGE__ . "' invoked by connect type '$command'")
101             : &DNS::BL::DNSBL_ESYNTAX();
102             }
103              
104             unless (exists $args{dsn} and length($args{dsn}))
105             {
106             return wantarray ? (&DNS::BL::DNSBL_ESYNTAX(),
107             "Missing argument 'dsn' for 'connect dbi'")
108             : &DNS::BL::DNSBL_ESYNTAX();
109             }
110              
111             unless (exists $args{bl} and length($args{bl}))
112             {
113             return wantarray ? (&DNS::BL::DNSBL_ESYNTAX(),
114             "Missing argument 'bl' for 'connect dbi'")
115             : &DNS::BL::DNSBL_ESYNTAX();
116             }
117              
118             for my $k (keys %args)
119             {
120             unless (grep { $k eq $_ } @known)
121             {
122             return wantarray ? (&DNS::BL::DNSBL_ESYNTAX(),
123             "Unknown argument '$k' to 'connect dbi'")
124             : &DNS::BL::DNSBL_ESYNTAX();
125             }
126             }
127              
128             my $dbh = DBI->connect($args{dsn},
129             (exists $args{user} ? $args{user} : ''),
130             (exists $args{password} ? $args{password} : ''),
131             { RaiseError => 0,
132             PrintWarn => 0,
133             Warn => 0,
134             PrintError => 0,
135             AutoCommit => 0 });
136              
137             unless ($dbh)
138             {
139             return wantarray ? (&DNS::BL::DNSBL_ECONNECT,
140             "Connect failed: DBI Error: $DBI::errstr")
141             : &DNS::BL::DNSBL_ECONNECT;
142             }
143              
144             # $sth is a hashref where all the prepared queries will be
145             # stored.
146             my $sth = {};
147              
148             # Prepare the query for inserting an entry into a given dnsbl. This
149             # is used by write. Must be called with the following positional
150             # arguments:
151             #
152             # Start_CIDR, End_CIDR, Created, Text, Return, dnsbl_name
153              
154             $sth->{add} = $dbh->prepare(<
155              
156             INSERT INTO entries (Bls_Id, Start_CIDR, End_CIDR, Created, Text, Return)
157             SELECT bls.Id, ?, ?, ?, ?, ?
158             FROM bls WHERE bls.Name = ?
159             ;
160             END_OF_SQL
161             );
162              
163             unless ($sth->{add})
164             {
165             return wantarray ? (&DNS::BL::DNSBL_ECONNECT,
166             "Connect failed: Error preparing 'add' " .
167             "SQL statement: "
168             . ($DBI::errstr || "No DBI error"))
169             : &DNS::BL::DNSBL_ECONNECT;
170             }
171              
172             # Prepare the query implementing the ->read() semantics. Must be
173             # called with the following arguments
174             #
175             # Start_CIDR, End_CIDR, dnsbl_name
176              
177             $sth->{read} = $dbh->prepare(<
178              
179             SELECT e.Start_CIDR, e.End_CIDR, e.Text, e.Return, e.Created
180             FROM entries e, bls b
181             WHERE
182             e.Start_CIDR >= ?
183             and e.End_CIDR <= ?
184             and b.Name = ?
185             and b.Id = e.Bls_Id
186             ;
187             END_OF_SQL
188             );
189             unless ($sth->{read})
190             {
191             return wantarray ? (&DNS::BL::DNSBL_ECONNECT,
192             "Connect failed: Error preparing 'read' " .
193             "SQL statement: "
194             . ($DBI::errstr || "No DBI error"))
195             : &DNS::BL::DNSBL_ECONNECT;
196             }
197              
198             # Prepare the query implementing the ->match() semantics. Must be
199             # called with the following arguments
200             #
201             # Start_CIDR, End_CIDR, dnsbl_name
202              
203             $sth->{match} = $dbh->prepare(<
204              
205             SELECT e.Start_CIDR, e.End_CIDR, e.Text, e.Return, e.Created
206             FROM entries e, bls b
207             WHERE
208             e.Start_CIDR <= ?
209             and e.End_CIDR >= ?
210             and b.Name = ?
211             and b.Id = e.Bls_Id
212             ;
213             END_OF_SQL
214             );
215             unless ($sth->{match})
216             {
217             return wantarray ? (&DNS::BL::DNSBL_ECONNECT,
218             "Connect failed: Error preparing 'match' " .
219             "SQL statement: "
220             . ($DBI::errstr || "No DBI error"))
221             : &DNS::BL::DNSBL_ECONNECT;
222             }
223              
224             # Prepare the query implementing the ->erase() semantics. Must be
225             # called with the following arguments
226             #
227             # Start_CIDR, End_CIDR, dnsbl_name
228              
229             $sth->{erase} = $dbh->prepare(<
230              
231             DELETE entries FROM entries, bls WHERE
232             entries.Start_CIDR >= ?
233             and entries.End_CIDR <= ?
234             and bls.Name = ?
235             and bls.Id = entries.Bls_Id
236             ;
237             END_OF_SQL
238             );
239             unless ($sth->{erase})
240             {
241             return wantarray ? (&DNS::BL::DNSBL_ECONNECT,
242             "Connect failed: Error preparing 'erase' " .
243             "SQL statement: "
244             . ($DBI::errstr || "No DBI error"))
245             : &DNS::BL::DNSBL_ECONNECT;
246             }
247              
248             # Store the private data
249             $args{_class} = __PACKAGE__;
250             $args{_sth} = $sth;
251             $args{_dbh} = $dbh;
252              
253             $bl->set("_connect", \%args);
254              
255             # Add I/O methods to the $bl object so that further calls can be
256             # processed
257              
258             $bl->set("_read", \&_read);
259             $bl->set("_match", \&_match);
260             $bl->set("_write", \&_write);
261             $bl->set("_erase", \&_delete);
262             $bl->set("_commit", \&_commit);
263            
264             return wantarray ? (&DNS::BL::DNSBL_OK, "Connected to DBI") :
265             &DNS::BL::DNSBL_OK;
266             };
267              
268             sub _write
269             {
270             my $bl = shift;
271             my $e = shift;
272              
273             my $data = $bl->get('_connect');
274             unless ($data or $data->{_class} eq __PACKAGE__)
275             {
276             return wantarray ?
277             (&DNS::BL::DNSBL_ESYNTAX(),
278             "->write can only be called while 'connect dbi' is in effect")
279             : &DNS::BL::DNSBL_ESYNTAX();
280             }
281              
282             if ($data->{_sth}->{add}->execute(scalar $e->addr->network->numeric,
283             scalar $e->addr->broadcast->numeric,
284             $e->time, $e->desc, $e->value,
285             $data->{bl})
286             and (my $rows = $data->{_sth}->{add}->rows) != 0)
287             {
288             return wantarray ? (&DNS::BL::DNSBL_OK, "OK - $rows inserted") :
289             &DNS::BL::DNSBL_OK;
290             }
291             else
292             {
293             return wantarray ? (&DNS::BL::DNSBL_EOTHER,
294             "Failed: (" . ($rows || '0') .
295             " rows inserted) "
296             . ($DBI::errstr || "No DBI error")) :
297             &DNS::BL::DNSBL_EOTHER;
298             }
299             }
300              
301             sub _read
302             {
303             my $bl = shift;
304             my $e = shift;
305              
306             my $data = $bl->get('_connect');
307             unless ($data or $data->{_class} eq __PACKAGE__)
308             {
309             return wantarray ?
310             (&DNS::BL::DNSBL_ESYNTAX(),
311             "->read can only be called while 'connect dbi' is in effect")
312             : &DNS::BL::DNSBL_ESYNTAX();
313             }
314              
315             my @ret = ();
316             if ($data->{_sth}->{read}->execute(scalar $e->addr->network->numeric,
317             scalar $e->addr->broadcast->numeric,
318             $data->{bl}))
319             {
320             while (my $r_ref = $data->{_sth}->{read}->fetchrow_arrayref)
321             {
322             my $ip = new NetAddr::IP (NetAddr::IP->new($r_ref->[0])->addr
323             . '-' .
324             NetAddr::IP->new($r_ref->[1])->addr);
325             # warn "** Read fetched IP: $ip\n";
326             my $ne = new DNS::BL::Entry;
327             $ne->addr($ip);
328             $ne->desc($r_ref->[2]);
329             $ne->value($r_ref->[3]);
330             $ne->time($r_ref->[4]);
331             push @ret, $ne;
332             }
333             }
334             else
335             {
336             return wantarray ? (&DNS::BL::DNSBL_EOTHER,
337             "Failed: to ->read: "
338             . ($DBI::errstr || "No DBI error")) :
339             &DNS::BL::DNSBL_EOTHER;
340             }
341              
342             return (&DNS::BL::DNSBL_OK, scalar @ret . " entries found",
343             @ret) if @ret;
344             return wantarray ? (&DNS::BL::DNSBL_ENOTFOUND, "No entries matched") :
345             &DNS::BL::DNSBL_ENOTFOUND;
346             }
347              
348             sub _match
349             {
350             my $bl = shift;
351             my $e = shift;
352              
353             my $data = $bl->get('_connect');
354             unless ($data or $data->{_class} eq __PACKAGE__)
355             {
356             return wantarray ?
357             (&DNS::BL::DNSBL_ESYNTAX(),
358             "->match can only be called while 'connect dbi' is in effect")
359             : &DNS::BL::DNSBL_ESYNTAX();
360             }
361              
362             my @ret = ();
363             if ($data->{_sth}->{match}->execute(scalar $e->addr->network->numeric,
364             scalar $e->addr->broadcast->numeric,
365             $data->{bl}))
366             {
367             while (my $r_ref = $data->{_sth}->{match}->fetchrow_arrayref)
368             {
369             my $ip = new NetAddr::IP (NetAddr::IP->new($r_ref->[0])->addr
370             . '-' .
371             NetAddr::IP->new($r_ref->[1])->addr);
372             # warn "** Match fetched IP: $ip\n";
373             my $ne = new DNS::BL::Entry;
374             $ne->addr($ip);
375             $ne->desc($r_ref->[2]);
376             $ne->value($r_ref->[3]);
377             $ne->time($r_ref->[4]);
378             push @ret, $ne;
379             }
380             }
381             else
382             {
383             return wantarray ? (&DNS::BL::DNSBL_EOTHER,
384             "Failed: to ->read: "
385             . ($DBI::errstr || "No DBI error")) :
386             &DNS::BL::DNSBL_EOTHER;
387             }
388              
389             return (&DNS::BL::DNSBL_OK, scalar @ret . " entries found",
390             @ret) if @ret;
391             return wantarray ? (&DNS::BL::DNSBL_ENOTFOUND, "No entries matched") :
392             &DNS::BL::DNSBL_ENOTFOUND;
393             }
394              
395             sub _commit
396             {
397             my $bl = shift;
398             my $e = shift;
399              
400             my $data = $bl->get('_connect');
401             unless ($data or $data->{_class} eq __PACKAGE__)
402             {
403             return wantarray ?
404             (&DNS::BL::DNSBL_ESYNTAX(),
405             "->commit can only be called while 'connect dbi' is in effect")
406             : &DNS::BL::DNSBL_ESYNTAX();
407             }
408              
409             if ($data->{_dbh}->commit)
410             {
411             return wantarray ? (&DNS::BL::DNSBL_OK, "OK - Committed") :
412             &DNS::BL::DNSBL_OK;
413             }
414             else
415             {
416             return wantarray ? (&DNS::BL::DNSBL_EOTHER,
417             "Failed: "
418             . ($DBI::errstr || "No DBI error")) :
419             &DNS::BL::DNSBL_EOTHER;
420             }
421             }
422              
423             sub _delete
424             {
425             my $bl = shift;
426             my $e = shift;
427              
428             my $data = $bl->get('_connect');
429             unless ($data or $data->{_class} eq __PACKAGE__)
430             {
431             return wantarray ?
432             (&DNS::BL::DNSBL_ESYNTAX(),
433             "->delete can only be called while 'connect dbi' is in effect")
434             : &DNS::BL::DNSBL_ESYNTAX();
435             }
436              
437             if ($data->{_sth}->{erase}->execute(scalar $e->addr->network->numeric,
438             scalar $e->addr->broadcast->numeric,
439             $data->{bl})
440             and (my $rows = $data->{_sth}->{erase}->rows) != 0)
441             {
442             return wantarray ? (&DNS::BL::DNSBL_OK,
443             "OK - $rows entries deleted") :
444             &DNS::BL::DNSBL_OK;
445             }
446             else
447             {
448             return wantarray ? (&DNS::BL::DNSBL_EOTHER,
449             "Failed: (" . ($rows || '0') .
450             " rows deleted) " .
451             ($DBI::errstr || "No DBI error")) :
452             &DNS::BL::DNSBL_EOTHER;
453             }
454             }
455              
456             1;
457             __END__