File Coverage

blib/lib/POE/Component/Client/DNSBL.pm
Criterion Covered Total %
statement 94 114 82.4
branch 24 42 57.1
condition 4 11 36.3
subroutine 12 13 92.3
pod 4 4 100.0
total 138 184 75.0


line stmt bran cond sub pod time code
1             package POE::Component::Client::DNSBL;
2             {
3             $POE::Component::Client::DNSBL::VERSION = '1.08';
4             }
5              
6             #ABSTRACT: A component that provides non-blocking DNSBL lookups
7              
8 3     3   672500 use strict;
  3         8  
  3         116  
9 3     3   17 use warnings;
  3         11  
  3         120  
10 3     3   3640 use Net::IP::Minimal qw(ip_is_ipv4 ip_get_version);
  3         2833  
  3         283  
11 3     3   22 use POE qw(Component::Client::DNS);
  3         7  
  3         36  
12              
13             sub spawn {
14 3     3 1 77 my $package = shift;
15 3         13 my %opts = @_;
16 3         19 $opts{lc $_} = delete $opts{$_} for keys %opts;
17 3 50       25 $opts{dnsbl} = 'zen.spamhaus.org' unless $opts{dnsbl};
18 3         10 my $options = delete $opts{options};
19 3         15 my $self = bless \%opts, $package;
20 3 50       66 $self->{session_id} = POE::Session->create(
21             object_states => [
22             $self => { shutdown => '_shutdown', lookup => '_resolve' },
23             $self => [qw(_start _resolve _lookup _reason)],
24             ],
25             heap => $self,
26             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
27             )->ID();
28 3         423 return $self;
29             }
30              
31             sub session_id {
32 3     3 1 5097 return $_[0]->{session_id};
33             }
34              
35             sub shutdown {
36 0     0 1 0 my $self = shift;
37 0         0 $poe_kernel->post( $self->{session_id}, 'shutdown' );
38             }
39              
40             sub lookup {
41 11     11 1 3046 my $self = shift;
42 11         65 $poe_kernel->post( $self->{session_id}, '_resolve', @_ );
43             }
44              
45             sub _start {
46 3     3   1514 my ($kernel,$self) = @_[KERNEL,OBJECT];
47 3         21 $self->{session_id} = $_[SESSION]->ID();
48 3 50       30 if ( $self->{alias} ) {
49 0         0 $kernel->alias_set( $self->{alias} );
50             }
51             else {
52 3         21 $kernel->refcount_increment( $self->{session_id} => __PACKAGE__ );
53             }
54 3 50 33     147 unless ( $self->{resolver} and $self->{resolver}->isa('POE::Component::Client::DNS') ) {
55 3         41 $self->{resolver} = POE::Component::Client::DNS->spawn(
56             Alias => __PACKAGE__ . $self->{session_id},
57             );
58 3         5996 $self->{_mydns} = 1;
59             }
60 3         17 return;
61             }
62              
63             sub _shutdown {
64 3     3   225 my ($kernel,$self) = @_[KERNEL,OBJECT];
65 3         24 $kernel->alarm_remove_all();
66 3         145 $kernel->alias_remove( $_ ) for $kernel->alias_list();
67 3 50       135 $kernel->refcount_decrement( $self->{session_id} => __PACKAGE__ ) unless $self->{alias};
68 3 50       126 $self->{resolver}->shutdown() if $self->{_mydns};
69 3         636 delete $self->{resolver};
70 3         244 return;
71             }
72              
73             sub _resolve {
74 11     11   2369 my ($kernel,$self,$state,$sender) = @_[KERNEL,OBJECT,STATE,SENDER];
75 11         38 my $sender_id = $sender->ID();
76 11         127 my $args;
77 11 50       39 if ( ref( $_[ARG0] ) eq 'HASH' ) {
78 0         0 $args = { %{ $_[ARG0] } };
  0         0  
79             }
80             else {
81 11         75 $args = { @_[ARG0..$#_] };
82             }
83 11         27 $args->{lc $_} = delete $args->{$_} for grep { !/^_/ } keys %{ $args };
  22         140  
  11         41  
84 11 50       49 unless ( $args->{event} ) {
85 0         0 warn "No 'event' specified for $state\n";
86 0         0 return;
87             }
88 11 50       40 unless ( $args->{address} ) {
89 0         0 warn "No 'address' specified for $state\n";
90 0         0 return;
91             }
92              
93 11         24 my $address = $args->{address};
94              
95 11 100       56 if ( ip_get_version( $args->{address} ) ) {
96 8 50       389 unless ( ip_is_ipv4( $args->{address} ) ) {
97 0         0 warn "Given 'address' is not an IPv4 address\n";
98 0         0 return;
99             }
100 8         217 $address = reverse split /\./, $args->{address};
101 8   33     76 $args->{dnsbl} ||= $self->{dnsbl};
102             }
103             else {
104 3   50     81 $args->{dnsbl} ||= 'dbl.spamhaus.org';
105             }
106              
107 11 50       40 if ( $args->{session} ) {
108 0 0       0 if ( my $ref = $kernel->alias_resolve( $args->{session} ) ) {
109 0         0 $sender_id = $ref->ID();
110             }
111             else {
112 0         0 warn "Could not resolve 'session' to a valid POE session\n";
113 0         0 return;
114             }
115             }
116 11   33     29 $args->{dnsbl} ||= $self->{dnsbl};
117 11         33 $args->{sender} = $sender_id;
118 11 100       64 $kernel->refcount_increment( $sender_id, __PACKAGE__ )
119             unless ref $args->{event} eq 'POE::Session::AnonEvent';
120 11         262 my $response = $self->{resolver}->resolve(
121             event => '_lookup',
122             host => join( '.', $address, $args->{dnsbl} ),
123             type => 'A',
124             context => $args,
125             );
126 11 50       49786 $kernel->yield( '_lookup', $response ) if $response;
127 11         63 return;
128             }
129              
130             sub _lookup {
131 11     11   57358 my ($kernel,$self,$record) = @_[KERNEL,OBJECT,ARG0];
132 11         26 my $args = $record->{context};
133 11         26 my $host = $record->{host};
134 11 50       97 unless ( $record->{response} ) {
135 0         0 $args->{error} = $record->{error};
136 0         0 delete $args->{response};
137             }
138             else {
139 11         26 delete $args->{error};
140 11         112 my @answers = $record->{response}->answer();
141 11 100       75 if ( @answers ) {
142 2         7 foreach my $answer ( @answers ) {
143 2         60 $args->{response} = $answer->rdatastr();
144 2         46 my $response = $self->{resolver}->resolve(
145             event => '_reason',
146             host => $host,
147             type => 'TXT',
148             context => $args,
149             );
150 2 50       4496 $kernel->yield( '_reason', $response ) if $response;
151             }
152 2         12 return;
153             }
154             else {
155 9         29 $args->{response} = 'NXDOMAIN';
156             }
157             }
158 9         25 my $sender_id = delete $args->{sender};
159 9         21 my $event = delete $args->{event};
160 9 100       34 if ( ref $event eq 'POE::Session::AnonEvent' ) {
161 4         14 $event->( $args );
162             }
163             else {
164 5         20 $kernel->post( $sender_id, $event, $args );
165 5         886 $kernel->refcount_decrement( $sender_id, __PACKAGE__ );
166             }
167 9         489 return;
168             }
169              
170             sub _reason {
171 2     2   14265 my ($kernel,$self,$record) = @_[KERNEL,OBJECT,ARG0];
172 2         7 my $args = $record->{context};
173 2         6 my $host = $record->{host};
174 2 50       8 unless ( $record->{response} ) {
175 0         0 $args->{error} = $record->{error};
176 0         0 delete $args->{response};
177             }
178             else {
179 2         4 delete $args->{error};
180 2         15 my @answers = $record->{response}->answer();
181 2 50       21 if ( @answers ) {
182 2         5 foreach my $answer ( @answers ) {
183 2         16 $args->{reason} = $answer->rdatastr();
184             }
185             }
186             else {
187 0         0 $args->{reason} = '';
188             }
189             }
190 2         679 my $sender_id = delete $args->{sender};
191 2         13 $kernel->post( $sender_id, $args->{event}, $args );
192 2         228 $kernel->refcount_decrement( $sender_id, __PACKAGE__ );
193 2         55 return;
194             }
195              
196             1;
197              
198              
199             __END__